pax_global_header00006660000000000000000000000064136751323570014526gustar00rootroot0000000000000052 comment=2921f45de19eb28a5b1960c110d6c6761361c1dc golang-github-gophercloud-gophercloud-0.12.0/000077500000000000000000000000001367513235700211575ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/.github/000077500000000000000000000000001367513235700225175ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/.github/CONTRIBUTING.md000066400000000000000000000207261367513235700247570ustar00rootroot00000000000000# 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.12.0/.github/ISSUE_TEMPLATE000066400000000000000000000002201367513235700246170ustar00rootroot00000000000000Before starting a PR, please read our [contributor tutorial](https://github.com/gophercloud/gophercloud/tree/master/docs/contributor-tutorial). golang-github-gophercloud-gophercloud-0.12.0/.github/PULL_REQUEST_TEMPLATE000066400000000000000000000007071367513235700257250ustar00rootroot00000000000000Prior 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.12.0/.gitignore000066400000000000000000000000271367513235700231460ustar00rootroot00000000000000**/*.swp .idea .vscode golang-github-gophercloud-gophercloud-0.12.0/.travis.yml000066400000000000000000000023571367513235700232770ustar00rootroot00000000000000language: go sudo: false install: - GO111MODULE=off go get golang.org/x/crypto/ssh - GO111MODULE=off go get -v -tags 'fixtures acceptance' ./... - GO111MODULE=off go get github.com/wadey/gocovmerge - GO111MODULE=off go get github.com/mattn/goveralls - GO111MODULE=off go get golang.org/x/tools/cmd/goimports go: - "1.11" - "1.12" - "1.13" - "tip" env: global: - secure: "xSQsAG5wlL9emjbCdxzz/hYQsSpJ/bABO1kkbwMSISVcJ3Nk0u4ywF+LS4bgeOnwPfmFvNTOqVDu3RwEvMeWXSI76t1piCPcObutb2faKLVD/hLoAS76gYX+Z8yGWGHrSB7Do5vTPj1ERe2UljdrnsSeOXzoDwFxYRaZLX4bBOB4AyoGvRniil5QXPATiA1tsWX1VMicj8a4F8X+xeESzjt1Q5Iy31e7vkptu71bhvXCaoo5QhYwT+pLR9dN0S1b7Ro0KVvkRefmr1lUOSYd2e74h6Lc34tC1h3uYZCS4h47t7v5cOXvMNxinEj2C51RvbjvZI1RLVdkuAEJD1Iz4+Ote46nXbZ//6XRZMZz/YxQ13l7ux1PFjgEB6HAapmF5Xd8PRsgeTU9LRJxpiTJ3P5QJ3leS1va8qnziM5kYipj/Rn+V8g2ad/rgkRox9LSiR9VYZD2Pe45YCb1mTKSl2aIJnV7nkOqsShY5LNB4JZSg7xIffA+9YVDktw8dJlATjZqt7WvJJ49g6A61mIUV4C15q2JPGKTkZzDiG81NtmS7hFa7k0yaE2ELgYocbcuyUcAahhxntYTC0i23nJmEHVNiZmBO3u7EgpWe4KGVfumU+lt12tIn5b3dZRBBUk3QakKKozSK1QPHGpk/AZGrhu7H6l8to6IICKWtDcyMPQ=" - GO111MODULE=on before_script: - go vet ./... script: - ./script/coverage - ./script/unittest - ./script/format after_success: - $HOME/gopath/bin/goveralls -service=travis-ci -coverprofile=cover.out golang-github-gophercloud-gophercloud-0.12.0/.zuul.yaml000066400000000000000000000053701367513235700231250ustar00rootroot00000000000000- 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 nodeset: ubuntu-bionic - job: name: gophercloud-acceptance-test-ironic parent: golang-test description: | Run gophercloud ironic acceptance test on master branch run: .zuul/playbooks/gophercloud-acceptance-test-ironic/run.yaml nodeset: ubuntu-bionic - job: name: gophercloud-acceptance-test-stein parent: gophercloud-acceptance-test description: | Run gophercloud acceptance test on stein branch vars: global_env: OS_BRANCH: stable/stein - job: name: gophercloud-acceptance-test-rocky parent: gophercloud-acceptance-test description: | Run gophercloud acceptance test on rocky branch nodeset: ubuntu-xenial vars: global_env: OS_BRANCH: stable/rocky - job: name: gophercloud-acceptance-test-queens parent: gophercloud-acceptance-test description: | Run gophercloud acceptance test on queens branch nodeset: ubuntu-xenial 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 nodeset: ubuntu-xenial 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 nodeset: ubuntu-xenial 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 nodeset: ubuntu-xenial vars: global_env: OS_BRANCH: stable/newton - project: name: gophercloud/gophercloud check: jobs: - gophercloud-unittest - gophercloud-acceptance-test - gophercloud-acceptance-test-ironic 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 recheck-rocky: jobs: - gophercloud-acceptance-test-rocky recheck-stein: jobs: - gophercloud-acceptance-test-stein golang-github-gophercloud-gophercloud-0.12.0/.zuul/000077500000000000000000000000001367513235700222345ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/.zuul/playbooks/000077500000000000000000000000001367513235700242375ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/.zuul/playbooks/gophercloud-acceptance-test-ironic/000077500000000000000000000000001367513235700330745ustar00rootroot00000000000000run.yaml000066400000000000000000000015371367513235700345130ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/.zuul/playbooks/gophercloud-acceptance-test-ironic- hosts: all become: yes roles: - config-golang - clone-devstack-gate-to-workspace - role: create-devstack-local-conf enable_services: - 'ironic' - role: install-devstack environment: OVERRIDE_ENABLED_SERVICES: 'g-api,g-reg,q-agt,q-dhcp,q-l3,q-svc,key,mysql,rabbit,ir-api,ir-cond,s-account,s-container,s-object,s-proxy,tempest' PROJECTS: 'openstack/ironic-python-agent-builder openstack/ironic' tasks: - name: Run ironic acceptance tests with gophercloud shell: cmd: | set -e set -o pipefail set -x echo $(export |grep OS_BRANCH) go get ./... || true ./script/acceptancetest-ironic -v 2>&1 | tee $TEST_RESULTS_TXT executable: /bin/bash chdir: '{{ zuul.project.src_dir }}' environment: '{{ global_env }}' golang-github-gophercloud-gophercloud-0.12.0/.zuul/playbooks/gophercloud-acceptance-test/000077500000000000000000000000001367513235700316135ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/.zuul/playbooks/gophercloud-acceptance-test/run.yaml000066400000000000000000000013021367513235700332770ustar00rootroot00000000000000- hosts: all become: yes roles: - config-golang - clone-devstack-gate-to-workspace - role: create-devstack-local-conf enable_services: - 'manila' - 'designate' - 'zun' - 'octavia' - 'neutron-ext' - install-devstack tasks: - name: Run acceptance tests with gophercloud shell: cmd: | set -e set -o pipefail set -x echo $(export |grep OS_BRANCH) export OS_DEBUG=1 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.12.0/.zuul/playbooks/gophercloud-unittest/000077500000000000000000000000001367513235700304275ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/.zuul/playbooks/gophercloud-unittest/run.yaml000066400000000000000000000006201367513235700321150ustar00rootroot00000000000000- 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.12.0/CHANGELOG.md000066400000000000000000001063321367513235700227750ustar00rootroot00000000000000## 0.13.0 (Unlreleased) ## 0.12.0 (June 25, 2020) UPGRADE NOTES * The URL used in the `compute/v2/extensions/bootfromvolume` package has been changed from `os-volumes_boot` to `servers`. IMPROVEMENTS * The URL used in the `compute/v2/extensions/bootfromvolume` package has been changed from `os-volumes_boot` to `servers` [GH-1973](https://github.com/gophercloud/gophercloud/pull/1973) * Modify `baremetal/v1/nodes.LogicalDisk.PhysicalDisks` type to support physical disks hints [GH-1982](https://github.com/gophercloud/gophercloud/pull/1982) * Added `baremetalintrospection/httpbasic` which provides an HTTP Basic Auth client [GH-1986](https://github.com/gophercloud/gophercloud/pull/1986) * Added `baremetal/httpbasic` which provides an HTTP Basic Auth client [GH-1983](https://github.com/gophercloud/gophercloud/pull/1983) * Added `containerinfra/v1/clusters.CreateOpts.MergeLabels` [GH-1985](https://github.com/gophercloud/gophercloud/pull/1985) BUG FIXES * Changed `containerinfra/v1/clusters.Cluster.HealthStatusReason` from `string` to `map[string]interface{}` [GH-1968](https://github.com/gophercloud/gophercloud/pull/1968) * Fixed marshalling of `blockstorage/extensions/backups.ImportBackup.Metadata` [GH-1967](https://github.com/gophercloud/gophercloud/pull/1967) * Fixed typo of "OAUth" to "OAuth" in `identity/v3/extensions/oauth1` [GH-1969](https://github.com/gophercloud/gophercloud/pull/1969) * Fixed goroutine leak during reauthentication [GH-1978](https://github.com/gophercloud/gophercloud/pull/1978) ## 0.11.0 (May 14, 2020) UPGRADE NOTES * Object storage container and object names are now URL encoded [GH-1930](https://github.com/gophercloud/gophercloud/pull/1930) * All responses now have access to the returned headers. Please report any issues this has caused [GH-1942](https://github.com/gophercloud/gophercloud/pull/1942) * Changes have been made to the internal HTTP client to ensure response bodies are handled in a way that enables connections to be re-used more efficiently [GH-1952](https://github.com/gophercloud/gophercloud/pull/1952) IMPROVEMENTS * Added `objectstorage/v1/containers.BulkDelete` [GH-1930](https://github.com/gophercloud/gophercloud/pull/1930) * Added `objectstorage/v1/objects.BulkDelete` [GH-1930](https://github.com/gophercloud/gophercloud/pull/1930) * Object storage container and object names are now URL encoded [GH-1930](https://github.com/gophercloud/gophercloud/pull/1930) * All responses now have access to the returned headers [GH-1942](https://github.com/gophercloud/gophercloud/pull/1942) * Added `compute/v2/extensions/injectnetworkinfo.InjectNetworkInfo` [GH-1941](https://github.com/gophercloud/gophercloud/pull/1941) * Added `compute/v2/extensions/resetnetwork.ResetNetwork` [GH-1941](https://github.com/gophercloud/gophercloud/pull/1941) * Added `identity/v3/extensions/trusts.ListRoles` [GH-1939](https://github.com/gophercloud/gophercloud/pull/1939) * Added `identity/v3/extensions/trusts.GetRole` [GH-1939](https://github.com/gophercloud/gophercloud/pull/1939) * Added `identity/v3/extensions/trusts.CheckRole` [GH-1939](https://github.com/gophercloud/gophercloud/pull/1939) * Added `identity/v3/extensions/oauth1.Create` [GH-1935](https://github.com/gophercloud/gophercloud/pull/1935) * Added `identity/v3/extensions/oauth1.CreateConsumer` [GH-1935](https://github.com/gophercloud/gophercloud/pull/1935) * Added `identity/v3/extensions/oauth1.DeleteConsumer` [GH-1935](https://github.com/gophercloud/gophercloud/pull/1935) * Added `identity/v3/extensions/oauth1.ListConsumers` [GH-1935](https://github.com/gophercloud/gophercloud/pull/1935) * Added `identity/v3/extensions/oauth1.GetConsumer` [GH-1935](https://github.com/gophercloud/gophercloud/pull/1935) * Added `identity/v3/extensions/oauth1.UpdateConsumer` [GH-1935](https://github.com/gophercloud/gophercloud/pull/1935) * Added `identity/v3/extensions/oauth1.RequestToken` [GH-1935](https://github.com/gophercloud/gophercloud/pull/1935) * Added `identity/v3/extensions/oauth1.AuthorizeToken` [GH-1935](https://github.com/gophercloud/gophercloud/pull/1935) * Added `identity/v3/extensions/oauth1.CreateAccessToken` [GH-1935](https://github.com/gophercloud/gophercloud/pull/1935) * Added `identity/v3/extensions/oauth1.GetAccessToken` [GH-1935](https://github.com/gophercloud/gophercloud/pull/1935) * Added `identity/v3/extensions/oauth1.RevokeAccessToken` [GH-1935](https://github.com/gophercloud/gophercloud/pull/1935) * Added `identity/v3/extensions/oauth1.ListAccessTokens` [GH-1935](https://github.com/gophercloud/gophercloud/pull/1935) * Added `identity/v3/extensions/oauth1.ListAccessTokenRoles` [GH-1935](https://github.com/gophercloud/gophercloud/pull/1935) * Added `identity/v3/extensions/oauth1.GetAccessTokenRole` [GH-1935](https://github.com/gophercloud/gophercloud/pull/1935) * Added `networking/v2/extensions/agents.Update` [GH-1954](https://github.com/gophercloud/gophercloud/pull/1954) * Added `networking/v2/extensions/agents.Delete` [GH-1954](https://github.com/gophercloud/gophercloud/pull/1954) * Added `networking/v2/extensions/agents.ScheduleDHCPNetwork` [GH-1954](https://github.com/gophercloud/gophercloud/pull/1954) * Added `networking/v2/extensions/agents.RemoveDHCPNetwork` [GH-1954](https://github.com/gophercloud/gophercloud/pull/1954) * Added `identity/v3/projects.CreateOpts.Extra` [GH-1951](https://github.com/gophercloud/gophercloud/pull/1951) * Added `identity/v3/projects.CreateOpts.Options` [GH-1951](https://github.com/gophercloud/gophercloud/pull/1951) * Added `identity/v3/projects.UpdateOpts.Extra` [GH-1951](https://github.com/gophercloud/gophercloud/pull/1951) * Added `identity/v3/projects.UpdateOpts.Options` [GH-1951](https://github.com/gophercloud/gophercloud/pull/1951) * Added `identity/v3/projects.Project.Extra` [GH-1951](https://github.com/gophercloud/gophercloud/pull/1951) * Added `identity/v3/projects.Options.Options` [GH-1951](https://github.com/gophercloud/gophercloud/pull/1951) * Added `imageservice/v2/images.Image.OpenStackImageImportMethods` [GH-1962](https://github.com/gophercloud/gophercloud/pull/1962) * Added `imageservice/v2/images.Image.OpenStackImageStoreIDs` [GH-1962](https://github.com/gophercloud/gophercloud/pull/1962) BUG FIXES * Changed`identity/v3/extensions/trusts.Trust.RemainingUses` from `bool` to `int` [GH-1939](https://github.com/gophercloud/gophercloud/pull/1939) * Changed `identity/v3/applicationcredentials.CreateOpts.ExpiresAt` from `string` to `*time.Time` [GH-1937](https://github.com/gophercloud/gophercloud/pull/1937) * Fixed issue with unmarshalling/decoding slices of composed structs [GH-1964](https://github.com/gophercloud/gophercloud/pull/1964) ## 0.10.0 (April 12, 2020) UPGRADE NOTES * The various `IDFromName` convenience functions have been moved to https://github.com/gophercloud/utils [GH-1897](https://github.com/gophercloud/gophercloud/pull/1897) * `sharedfilesystems/v2/shares.GetExportLocations` was renamed to `sharedfilesystems/v2/shares.ListExportLocations` [GH-1932](https://github.com/gophercloud/gophercloud/pull/1932) IMPROVEMENTS * Added `blockstorage/extensions/volumeactions.SetBootable` [GH-1891](https://github.com/gophercloud/gophercloud/pull/1891) * Added `blockstorage/extensions/backups.Export` [GH-1894](https://github.com/gophercloud/gophercloud/pull/1894) * Added `blockstorage/extensions/backups.Import` [GH-1894](https://github.com/gophercloud/gophercloud/pull/1894) * Added `placement/v1/resourceproviders.GetTraits` [GH-1899](https://github.com/gophercloud/gophercloud/pull/1899) * Added the ability to authenticate with Amazon EC2 Credentials [GH-1900](https://github.com/gophercloud/gophercloud/pull/1900) * Added ability to list Nova services by binary and host [GH-1904](https://github.com/gophercloud/gophercloud/pull/1904) * Added `compute/v2/extensions/services.Update` [GH-1902](https://github.com/gophercloud/gophercloud/pull/1902) * Added system scope to v3 authentication [GH-1908](https://github.com/gophercloud/gophercloud/pull/1908) * Added `identity/v3/extensions/ec2tokens.ValidateS3Token` [GH-1906](https://github.com/gophercloud/gophercloud/pull/1906) * Added `containerinfra/v1/clusters.Cluster.HealthStatus` [GH-1910](https://github.com/gophercloud/gophercloud/pull/1910) * Added `containerinfra/v1/clusters.Cluster.HealthStatusReason` [GH-1910](https://github.com/gophercloud/gophercloud/pull/1910) * Added `loadbalancer/v2/amphorae.Failover` [GH-1912](https://github.com/gophercloud/gophercloud/pull/1912) * Added `identity/v3/extensions/ec2credentials.List` [GH-1916](https://github.com/gophercloud/gophercloud/pull/1916) * Added `identity/v3/extensions/ec2credentials.Get` [GH-1916](https://github.com/gophercloud/gophercloud/pull/1916) * Added `identity/v3/extensions/ec2credentials.Create` [GH-1916](https://github.com/gophercloud/gophercloud/pull/1916) * Added `identity/v3/extensions/ec2credentials.Delete` [GH-1916](https://github.com/gophercloud/gophercloud/pull/1916) * Added `ErrUnexpectedResponseCode.ResponseHeader` [GH-1919](https://github.com/gophercloud/gophercloud/pull/1919) * Added support for TOTP authentication [GH-1922](https://github.com/gophercloud/gophercloud/pull/1922) * `sharedfilesystems/v2/shares.GetExportLocations` was renamed to `sharedfilesystems/v2/shares.ListExportLocations` [GH-1932](https://github.com/gophercloud/gophercloud/pull/1932) * Added `sharedfilesystems/v2/shares.GetExportLocation` [GH-1932](https://github.com/gophercloud/gophercloud/pull/1932) * Added `sharedfilesystems/v2/shares.Revert` [GH-1931](https://github.com/gophercloud/gophercloud/pull/1931) * Added `sharedfilesystems/v2/shares.ResetStatus` [GH-1931](https://github.com/gophercloud/gophercloud/pull/1931) * Added `sharedfilesystems/v2/shares.ForceDelete` [GH-1931](https://github.com/gophercloud/gophercloud/pull/1931) * Added `sharedfilesystems/v2/shares.Unmanage` [GH-1931](https://github.com/gophercloud/gophercloud/pull/1931) * Added `blockstorage/v3/attachments.Create` [GH-1934](https://github.com/gophercloud/gophercloud/pull/1934) * Added `blockstorage/v3/attachments.List` [GH-1934](https://github.com/gophercloud/gophercloud/pull/1934) * Added `blockstorage/v3/attachments.Get` [GH-1934](https://github.com/gophercloud/gophercloud/pull/1934) * Added `blockstorage/v3/attachments.Update` [GH-1934](https://github.com/gophercloud/gophercloud/pull/1934) * Added `blockstorage/v3/attachments.Delete` [GH-1934](https://github.com/gophercloud/gophercloud/pull/1934) * Added `blockstorage/v3/attachments.Complete` [GH-1934](https://github.com/gophercloud/gophercloud/pull/1934) BUG FIXES * Fixed issue with Orchestration `get_file` only being able to read JSON and YAML files [GH-1915](https://github.com/gophercloud/gophercloud/pull/1915) ## 0.9.0 (March 10, 2020) UPGRADE NOTES * The way we implement new API result fields added by microversions has changed. Previously, we would declare a dedicated `ExtractFoo` function in a file called `microversions.go`. Now, we are declaring those fields inline of the original result struct as a pointer. [GH-1854](https://github.com/gophercloud/gophercloud/pull/1854) * `compute/v2/servers.CreateOpts.Networks` has changed from `[]Network` to `interface{}` in order to support creating servers that have no networks. [GH-1884](https://github.com/gophercloud/gophercloud/pull/1884) IMPROVEMENTS * Added `compute/v2/extensions/instanceactions.List` [GH-1848](https://github.com/gophercloud/gophercloud/pull/1848) * Added `compute/v2/extensions/instanceactions.Get` [GH-1848](https://github.com/gophercloud/gophercloud/pull/1848) * Added `networking/v2/ports.List.FixedIPs` [GH-1849](https://github.com/gophercloud/gophercloud/pull/1849) * Added `identity/v3/extensions/trusts.List` [GH-1855](https://github.com/gophercloud/gophercloud/pull/1855) * Added `identity/v3/extensions/trusts.Get` [GH-1855](https://github.com/gophercloud/gophercloud/pull/1855) * Added `identity/v3/extensions/trusts.Trust.ExpiresAt` [GH-1857](https://github.com/gophercloud/gophercloud/pull/1857) * Added `identity/v3/extensions/trusts.Trust.DeletedAt` [GH-1857](https://github.com/gophercloud/gophercloud/pull/1857) * Added `compute/v2/extensions/instanceactions.InstanceActionDetail` [GH-1851](https://github.com/gophercloud/gophercloud/pull/1851) * Added `compute/v2/extensions/instanceactions.Event` [GH-1851](https://github.com/gophercloud/gophercloud/pull/1851) * Added `compute/v2/extensions/instanceactions.ListOpts` [GH-1858](https://github.com/gophercloud/gophercloud/pull/1858) * Added `objectstorage/v1/containers.UpdateOpts.TempURLKey` [GH-1864](https://github.com/gophercloud/gophercloud/pull/1864) * Added `objectstorage/v1/containers.UpdateOpts.TempURLKey2` [GH-1864](https://github.com/gophercloud/gophercloud/pull/1864) * Added `placement/v1/resourceproviders.GetUsages` [GH-1862](https://github.com/gophercloud/gophercloud/pull/1862) * Added `placement/v1/resourceproviders.GetInventories` [GH-1862](https://github.com/gophercloud/gophercloud/pull/1862) * Added `imageservice/v2/images.ReplaceImageMinRam` [GH-1867](https://github.com/gophercloud/gophercloud/pull/1867) * Added `objectstorage/v1/containers.UpdateOpts.TempURLKey` [GH-1865](https://github.com/gophercloud/gophercloud/pull/1865) * Added `objectstorage/v1/containers.CreateOpts.TempURLKey2` [GH-1865](https://github.com/gophercloud/gophercloud/pull/1865) * Added `blockstorage/extensions/volumetransfers.List` [GH-1869](https://github.com/gophercloud/gophercloud/pull/1869) * Added `blockstorage/extensions/volumetransfers.Create` [GH-1869](https://github.com/gophercloud/gophercloud/pull/1869) * Added `blockstorage/extensions/volumetransfers.Accept` [GH-1869](https://github.com/gophercloud/gophercloud/pull/1869) * Added `blockstorage/extensions/volumetransfers.Get` [GH-1869](https://github.com/gophercloud/gophercloud/pull/1869) * Added `blockstorage/extensions/volumetransfers.Delete` [GH-1869](https://github.com/gophercloud/gophercloud/pull/1869) * Added `blockstorage/extensions/backups.RestoreFromBackup` [GH-1871](https://github.com/gophercloud/gophercloud/pull/1871) * Added `blockstorage/v3/volumes.CreateOpts.BackupID` [GH-1871](https://github.com/gophercloud/gophercloud/pull/1871) * Added `blockstorage/v3/volumes.Volume.BackupID` [GH-1871](https://github.com/gophercloud/gophercloud/pull/1871) * Added `identity/v3/projects.ListOpts.Tags` [GH-1882](https://github.com/gophercloud/gophercloud/pull/1882) * Added `identity/v3/projects.ListOpts.TagsAny` [GH-1882](https://github.com/gophercloud/gophercloud/pull/1882) * Added `identity/v3/projects.ListOpts.NotTags` [GH-1882](https://github.com/gophercloud/gophercloud/pull/1882) * Added `identity/v3/projects.ListOpts.NotTagsAny` [GH-1882](https://github.com/gophercloud/gophercloud/pull/1882) * Added `identity/v3/projects.CreateOpts.Tags` [GH-1882](https://github.com/gophercloud/gophercloud/pull/1882) * Added `identity/v3/projects.UpdateOpts.Tags` [GH-1882](https://github.com/gophercloud/gophercloud/pull/1882) * Added `identity/v3/projects.Project.Tags` [GH-1882](https://github.com/gophercloud/gophercloud/pull/1882) * Changed `compute/v2/servers.CreateOpts.Networks` from `[]Network` to `interface{}` to support creating servers with no networks. [GH-1884](https://github.com/gophercloud/gophercloud/pull/1884) BUG FIXES * Added support for `int64` headers, which were previously being silently dropped [GH-1860](https://github.com/gophercloud/gophercloud/pull/1860) * Allow image properties with empty values [GH-1875](https://github.com/gophercloud/gophercloud/pull/1875) * Fixed `compute/v2/extensions/extendedserverattributes.ServerAttributesExt.Userdata` JSON tag [GH-1881](https://github.com/gophercloud/gophercloud/pull/1881) ## 0.8.0 (February 8, 2020) UPGRADE NOTES * The behavior of `keymanager/v1/acls.SetOpts` has changed. Instead of a struct, it is now `[]SetOpt`. See [GH-1816](https://github.com/gophercloud/gophercloud/pull/1816) for implementation details. IMPROVEMENTS * The result of `containerinfra/v1/clusters.Resize` now returns only the UUID when calling `Extract`. This is a backwards-breaking change from the previous struct that was returned [GH-1649](https://github.com/gophercloud/gophercloud/pull/1649) * Added `compute/v2/extensions/shelveunshelve.Shelve` [GH-1799](https://github.com/gophercloud/gophercloud/pull/1799) * Added `compute/v2/extensions/shelveunshelve.ShelveOffload` [GH-1799](https://github.com/gophercloud/gophercloud/pull/1799) * Added `compute/v2/extensions/shelveunshelve.Unshelve` [GH-1799](https://github.com/gophercloud/gophercloud/pull/1799) * Added `containerinfra/v1/nodegroups.Get` [GH-1774](https://github.com/gophercloud/gophercloud/pull/1774) * Added `containerinfra/v1/nodegroups.List` [GH-1774](https://github.com/gophercloud/gophercloud/pull/1774) * Added `orchestration/v1/resourcetypes.List` [GH-1806](https://github.com/gophercloud/gophercloud/pull/1806) * Added `orchestration/v1/resourcetypes.GetSchema` [GH-1806](https://github.com/gophercloud/gophercloud/pull/1806) * Added `orchestration/v1/resourcetypes.GenerateTemplate` [GH-1806](https://github.com/gophercloud/gophercloud/pull/1806) * Added `keymanager/v1/acls.SetOpt` and changed `keymanager/v1/acls.SetOpts` to `[]SetOpt` [GH-1816](https://github.com/gophercloud/gophercloud/pull/1816) * Added `blockstorage/apiversions.List` [GH-458](https://github.com/gophercloud/gophercloud/pull/458) * Added `blockstorage/apiversions.Get` [GH-458](https://github.com/gophercloud/gophercloud/pull/458) * Added `StatusCodeError` interface and `GetStatusCode` convenience method [GH-1820](https://github.com/gophercloud/gophercloud/pull/1820) * Added pagination support to `compute/v2/extensions/usage.SingleTenant` [GH-1819](https://github.com/gophercloud/gophercloud/pull/1819) * Added pagination support to `compute/v2/extensions/usage.AllTenants` [GH-1819](https://github.com/gophercloud/gophercloud/pull/1819) * Added `placement/v1/resourceproviders.List` [GH-1815](https://github.com/gophercloud/gophercloud/pull/1815) * Allow `CreateMemberOptsBuilder` to be passed in `loadbalancer/v2/pools.Create` [GH-1822](https://github.com/gophercloud/gophercloud/pull/1822) * Added `Backup` to `loadbalancer/v2/pools.CreateMemberOpts` [GH-1824](https://github.com/gophercloud/gophercloud/pull/1824) * Added `MonitorAddress` to `loadbalancer/v2/pools.CreateMemberOpts` [GH-1824](https://github.com/gophercloud/gophercloud/pull/1824) * Added `MonitorPort` to `loadbalancer/v2/pools.CreateMemberOpts` [GH-1824](https://github.com/gophercloud/gophercloud/pull/1824) * Changed `Impersonation` to a non-required field in `identity/v3/extensions/trusts.CreateOpts` [GH-1818](https://github.com/gophercloud/gophercloud/pull/1818) * Added `InsertHeaders` to `loadbalancer/v2/listeners.UpdateOpts` [GH-1835] * Added `NUMATopology` to `baremetalintrospection/v1/introspection.Data` [GH-1842](https://github.com/gophercloud/gophercloud/pull/1842) * Added `placement/v1/resourceproviders.Create` [GH-1841](https://github.com/gophercloud/gophercloud/pull/1841) * Added `blockstorage/extensions/volumeactions.UploadImageOpts.Visibility` [GH-1873](https://github.com/gophercloud/gophercloud/pull/1873) * Added `blockstorage/extensions/volumeactions.UploadImageOpts.Protected` [GH-1873](https://github.com/gophercloud/gophercloud/pull/1873) * Added `blockstorage/extensions/volumeactions.VolumeImage.Visibility` [GH-1873](https://github.com/gophercloud/gophercloud/pull/1873) * Added `blockstorage/extensions/volumeactions.VolumeImage.Protected` [GH-1873](https://github.com/gophercloud/gophercloud/pull/1873) BUG FIXES * Changed `sort_key` to `sort_keys` in ` workflow/v2/crontriggers.ListOpts` [GH-1809](https://github.com/gophercloud/gophercloud/pull/1809) * Allow `blockstorage/extensions/schedulerstats.Capabilities.MaxOverSubscriptionRatio` to accept both string and int/float responses [GH-1817](https://github.com/gophercloud/gophercloud/pull/1817) * Fixed bug in `NewLoadBalancerV2` for situations when the LBaaS service was advertised without a `/v2.0` endpoint [GH-1829](https://github.com/gophercloud/gophercloud/pull/1829) * Fixed JSON tags in `baremetal/v1/ports.UpdateOperation` [GH-1840](https://github.com/gophercloud/gophercloud/pull/1840) * Fixed JSON tags in `networking/v2/extensions/lbaas/vips.commonResult.Extract()` [GH-1840](https://github.com/gophercloud/gophercloud/pull/1840) ## 0.7.0 (December 3, 2019) IMPROVEMENTS * Allow a token to be used directly for authentication instead of generating a new token based on a given token [GH-1752](https://github.com/gophercloud/gophercloud/pull/1752) * Moved `tags.ServerTagsExt` to servers.TagsExt` [GH-1760](https://github.com/gophercloud/gophercloud/pull/1760) * Added `tags`, `tags-any`, `not-tags`, and `not-tags-any` to `compute/v2/servers.ListOpts` [GH-1759](https://github.com/gophercloud/gophercloud/pull/1759) * Added `AccessRule` to `identity/v3/applicationcredentials` [GH-1758](https://github.com/gophercloud/gophercloud/pull/1758) * Gophercloud no longer returns an error when multiple endpoints are found. Instead, it will choose the first endpoint and discard the others [GH-1766](https://github.com/gophercloud/gophercloud/pull/1766) * Added `networking/v2/extensions/fwaas_v2/rules.Create` [GH-1768](https://github.com/gophercloud/gophercloud/pull/1768) * Added `networking/v2/extensions/fwaas_v2/rules.Delete` [GH-1771](https://github.com/gophercloud/gophercloud/pull/1771) * Added `loadbalancer/v2/providers.List` [GH-1765](https://github.com/gophercloud/gophercloud/pull/1765) * Added `networking/v2/extensions/fwaas_v2/rules.Get` [GH-1772](https://github.com/gophercloud/gophercloud/pull/1772) * Added `networking/v2/extensions/fwaas_v2/rules.Update` [GH-1776](https://github.com/gophercloud/gophercloud/pull/1776) * Added `networking/v2/extensions/fwaas_v2/rules.List` [GH-1783](https://github.com/gophercloud/gophercloud/pull/1783) * Added `MaxRetriesDown` into `loadbalancer/v2/monitors.CreateOpts` [GH-1785](https://github.com/gophercloud/gophercloud/pull/1785) * Added `MaxRetriesDown` into `loadbalancer/v2/monitors.UpdateOpts` [GH-1786](https://github.com/gophercloud/gophercloud/pull/1786) * Added `MaxRetriesDown` into `loadbalancer/v2/monitors.Monitor` [GH-1787](https://github.com/gophercloud/gophercloud/pull/1787) * Added `MaxRetriesDown` into `loadbalancer/v2/monitors.ListOpts` [GH-1788](https://github.com/gophercloud/gophercloud/pull/1788) * Updated `go.mod` dependencies, specifically to account for CVE-2019-11840 with `golang.org/x/crypto` [GH-1793](https://github.com/gophercloud/gophercloud/pull/1788) ## 0.6.0 (October 17, 2019) UPGRADE NOTES * The way reauthentication works has been refactored. This should not cause a problem, but please report bugs if it does. See [GH-1746](https://github.com/gophercloud/gophercloud/pull/1746) for more information. IMPROVEMENTS * Added `networking/v2/extensions/quotas.Get` [GH-1742](https://github.com/gophercloud/gophercloud/pull/1742) * Added `networking/v2/extensions/quotas.Update` [GH-1747](https://github.com/gophercloud/gophercloud/pull/1747) * Refactored the reauthentication implementation to use goroutines and added a check to prevent an infinite loop in certain situations. [GH-1746](https://github.com/gophercloud/gophercloud/pull/1746) BUG FIXES * Changed `Flavor` to `FlavorID` in `loadbalancer/v2/loadbalancers` [GH-1744](https://github.com/gophercloud/gophercloud/pull/1744) * Changed `Flavor` to `FlavorID` in `networking/v2/extensions/lbaas_v2/loadbalancers` [GH-1744](https://github.com/gophercloud/gophercloud/pull/1744) * The `go-yaml` dependency was updated to `v2.2.4` to fix possible DDOS vulnerabilities [GH-1751](https://github.com/gophercloud/gophercloud/pull/1751) ## 0.5.0 (October 13, 2019) IMPROVEMENTS * Added `VolumeType` to `compute/v2/extensions/bootfromvolume.BlockDevice`[GH-1690](https://github.com/gophercloud/gophercloud/pull/1690) * Added `networking/v2/extensions/layer3/portforwarding.List` [GH-1688](https://github.com/gophercloud/gophercloud/pull/1688) * Added `networking/v2/extensions/layer3/portforwarding.Get` [GH-1698](https://github.com/gophercloud/gophercloud/pull/1696) * Added `compute/v2/extensions/tags.ReplaceAll` [GH-1696](https://github.com/gophercloud/gophercloud/pull/1696) * Added `compute/v2/extensions/tags.Add` [GH-1696](https://github.com/gophercloud/gophercloud/pull/1696) * Added `networking/v2/extensions/layer3/portforwarding.Update` [GH-1703](https://github.com/gophercloud/gophercloud/pull/1703) * Added `ExtractDomain` method to token results in `identity/v3/tokens` [GH-1712](https://github.com/gophercloud/gophercloud/pull/1712) * Added `AllowedCIDRs` to `loadbalancer/v2/listeners.CreateOpts` [GH-1710](https://github.com/gophercloud/gophercloud/pull/1710) * Added `AllowedCIDRs` to `loadbalancer/v2/listeners.UpdateOpts` [GH-1710](https://github.com/gophercloud/gophercloud/pull/1710) * Added `AllowedCIDRs` to `loadbalancer/v2/listeners.Listener` [GH-1710](https://github.com/gophercloud/gophercloud/pull/1710) * Added `compute/v2/extensions/tags.Add` [GH-1695](https://github.com/gophercloud/gophercloud/pull/1695) * Added `compute/v2/extensions/tags.ReplaceAll` [GH-1694](https://github.com/gophercloud/gophercloud/pull/1694) * Added `compute/v2/extensions/tags.Delete` [GH-1699](https://github.com/gophercloud/gophercloud/pull/1699) * Added `compute/v2/extensions/tags.DeleteAll` [GH-1700](https://github.com/gophercloud/gophercloud/pull/1700) * Added `ImageStatusImporting` as an image status [GH-1725](https://github.com/gophercloud/gophercloud/pull/1725) * Added `ByPath` to `baremetalintrospection/v1/introspection.RootDiskType` [GH-1730](https://github.com/gophercloud/gophercloud/pull/1730) * Added `AttachedVolumes` to `compute/v2/servers.Server` [GH-1732](https://github.com/gophercloud/gophercloud/pull/1732) * Enable unmarshaling server tags to a `compute/v2/servers.Server` struct [GH-1734] * Allow setting an empty members list in `loadbalancer/v2/pools.BatchUpdateMembers` [GH-1736](https://github.com/gophercloud/gophercloud/pull/1736) * Allow unsetting members' subnet ID and name in `loadbalancer/v2/pools.BatchUpdateMemberOpts` [GH-1738](https://github.com/gophercloud/gophercloud/pull/1738) BUG FIXES * Changed struct type for options in `networking/v2/extensions/lbaas_v2/listeners` to `UpdateOptsBuilder` interface instead of specific UpdateOpts type [GH-1705](https://github.com/gophercloud/gophercloud/pull/1705) * Changed struct type for options in `networking/v2/extensions/lbaas_v2/loadbalancers` to `UpdateOptsBuilder` interface instead of specific UpdateOpts type [GH-1706](https://github.com/gophercloud/gophercloud/pull/1706) * Fixed issue with `blockstorage/v1/volumes.Create` where the response was expected to be 202 [GH-1720](https://github.com/gophercloud/gophercloud/pull/1720) * Changed `DefaultTlsContainerRef` from `string` to `*string` in `loadbalancer/v2/listeners.UpdateOpts` to allow the value to be removed during update. [GH-1723](https://github.com/gophercloud/gophercloud/pull/1723) * Changed `SniContainerRefs` from `[]string{}` to `*[]string{}` in `loadbalancer/v2/listeners.UpdateOpts` to allow the value to be removed during update. [GH-1723](https://github.com/gophercloud/gophercloud/pull/1723) * Changed `DefaultTlsContainerRef` from `string` to `*string` in `networking/v2/extensions/lbaas_v2/listeners.UpdateOpts` to allow the value to be removed during update. [GH-1723](https://github.com/gophercloud/gophercloud/pull/1723) * Changed `SniContainerRefs` from `[]string{}` to `*[]string{}` in `networking/v2/extensions/lbaas_v2/listeners.UpdateOpts` to allow the value to be removed during update. [GH-1723](https://github.com/gophercloud/gophercloud/pull/1723) ## 0.4.0 (September 3, 2019) IMPROVEMENTS * Added `blockstorage/extensions/quotasets.results.QuotaSet.Groups` [GH-1668](https://github.com/gophercloud/gophercloud/pull/1668) * Added `blockstorage/extensions/quotasets.results.QuotaUsageSet.Groups` [GH-1668](https://github.com/gophercloud/gophercloud/pull/1668) * Added `containerinfra/v1/clusters.CreateOpts.FixedNetwork` [GH-1674](https://github.com/gophercloud/gophercloud/pull/1674) * Added `containerinfra/v1/clusters.CreateOpts.FixedSubnet` [GH-1676](https://github.com/gophercloud/gophercloud/pull/1676) * Added `containerinfra/v1/clusters.CreateOpts.FloatingIPEnabled` [GH-1677](https://github.com/gophercloud/gophercloud/pull/1677) * Added `CreatedAt` and `UpdatedAt` to `loadbalancers/v2/loadbalancers.LoadBalancer` [GH-1681](https://github.com/gophercloud/gophercloud/pull/1681) * Added `networking/v2/extensions/layer3/portforwarding.Create` [GH-1651](https://github.com/gophercloud/gophercloud/pull/1651) * Added `networking/v2/extensions/agents.ListDHCPNetworks` [GH-1686](https://github.com/gophercloud/gophercloud/pull/1686) * Added `networking/v2/extensions/layer3/portforwarding.Delete` [GH-1652](https://github.com/gophercloud/gophercloud/pull/1652) * Added `compute/v2/extensions/tags.List` [GH-1679](https://github.com/gophercloud/gophercloud/pull/1679) * Added `compute/v2/extensions/tags.Check` [GH-1679](https://github.com/gophercloud/gophercloud/pull/1679) BUG FIXES * Changed `identity/v3/endpoints.ListOpts.RegionID` from `int` to `string` [GH-1664](https://github.com/gophercloud/gophercloud/pull/1664) * Fixed issue where older time formats in some networking APIs/resources were unable to be parsed [GH-1671](https://github.com/gophercloud/gophercloud/pull/1664) * Changed `SATA`, `SCSI`, and `SAS` types to `InterfaceType` in `baremetal/v1/nodes` [GH-1683] ## 0.3.0 (July 31, 2019) IMPROVEMENTS * Added `baremetal/apiversions.List` [GH-1577](https://github.com/gophercloud/gophercloud/pull/1577) * Added `baremetal/apiversions.Get` [GH-1577](https://github.com/gophercloud/gophercloud/pull/1577) * Added `compute/v2/extensions/servergroups.CreateOpts.Policy` [GH-1636](https://github.com/gophercloud/gophercloud/pull/1636) * Added `identity/v3/extensions/trusts.Create` [GH-1644](https://github.com/gophercloud/gophercloud/pull/1644) * Added `identity/v3/extensions/trusts.Delete` [GH-1644](https://github.com/gophercloud/gophercloud/pull/1644) * Added `CreatedAt` and `UpdatedAt` to `networking/v2/extensions/layer3/floatingips.FloatingIP` [GH-1647](https://github.com/gophercloud/gophercloud/issues/1646) * Added `CreatedAt` and `UpdatedAt` to `networking/v2/extensions/security/groups.SecGroup` [GH-1654](https://github.com/gophercloud/gophercloud/issues/1654) * Added `CreatedAt` and `UpdatedAt` to `networking/v2/networks.Network` [GH-1657](https://github.com/gophercloud/gophercloud/issues/1657) * Added `keymanager/v1/containers.CreateSecretRef` [GH-1659](https://github.com/gophercloud/gophercloud/issues/1659) * Added `keymanager/v1/containers.DeleteSecretRef` [GH-1659](https://github.com/gophercloud/gophercloud/issues/1659) * Added `sharedfilesystems/v2/shares.GetMetadata` [GH-1656](https://github.com/gophercloud/gophercloud/issues/1656) * Added `sharedfilesystems/v2/shares.GetMetadatum` [GH-1656](https://github.com/gophercloud/gophercloud/issues/1656) * Added `sharedfilesystems/v2/shares.SetMetadata` [GH-1656](https://github.com/gophercloud/gophercloud/issues/1656) * Added `sharedfilesystems/v2/shares.UpdateMetadata` [GH-1656](https://github.com/gophercloud/gophercloud/issues/1656) * Added `sharedfilesystems/v2/shares.DeleteMetadatum` [GH-1656](https://github.com/gophercloud/gophercloud/issues/1656) * Added `sharedfilesystems/v2/sharetypes.IDFromName` [GH-1662](https://github.com/gophercloud/gophercloud/issues/1662) BUG FIXES * Changed `baremetal/v1/nodes.CleanStep.Args` from `map[string]string` to `map[string]interface{}` [GH-1638](https://github.com/gophercloud/gophercloud/pull/1638) * Removed `URLPath` and `ExpectedCodes` from `loadbalancer/v2/monitors.ToMonitorCreateMap` since Octavia now provides default values when these fields are not specified [GH-1640](https://github.com/gophercloud/gophercloud/pull/1540) ## 0.2.0 (June 17, 2019) IMPROVEMENTS * Added `networking/v2/extensions/qos/rules.ListBandwidthLimitRules` [GH-1584](https://github.com/gophercloud/gophercloud/pull/1584) * Added `networking/v2/extensions/qos/rules.GetBandwidthLimitRule` [GH-1584](https://github.com/gophercloud/gophercloud/pull/1584) * Added `networking/v2/extensions/qos/rules.CreateBandwidthLimitRule` [GH-1584](https://github.com/gophercloud/gophercloud/pull/1584) * Added `networking/v2/extensions/qos/rules.UpdateBandwidthLimitRule` [GH-1589](https://github.com/gophercloud/gophercloud/pull/1589) * Added `networking/v2/extensions/qos/rules.DeleteBandwidthLimitRule` [GH-1590](https://github.com/gophercloud/gophercloud/pull/1590) * Added `networking/v2/extensions/qos/policies.List` [GH-1591](https://github.com/gophercloud/gophercloud/pull/1591) * Added `networking/v2/extensions/qos/policies.Get` [GH-1593](https://github.com/gophercloud/gophercloud/pull/1593) * Added `networking/v2/extensions/qos/rules.ListDSCPMarkingRules` [GH-1594](https://github.com/gophercloud/gophercloud/pull/1594) * Added `networking/v2/extensions/qos/policies.Create` [GH-1595](https://github.com/gophercloud/gophercloud/pull/1595) * Added `compute/v2/extensions/diagnostics.Get` [GH-1592](https://github.com/gophercloud/gophercloud/pull/1592) * Added `networking/v2/extensions/qos/policies.Update` [GH-1603](https://github.com/gophercloud/gophercloud/pull/1603) * Added `networking/v2/extensions/qos/policies.Delete` [GH-1603](https://github.com/gophercloud/gophercloud/pull/1603) * Added `networking/v2/extensions/qos/rules.CreateDSCPMarkingRule` [GH-1605](https://github.com/gophercloud/gophercloud/pull/1605) * Added `networking/v2/extensions/qos/rules.UpdateDSCPMarkingRule` [GH-1605](https://github.com/gophercloud/gophercloud/pull/1605) * Added `networking/v2/extensions/qos/rules.GetDSCPMarkingRule` [GH-1609](https://github.com/gophercloud/gophercloud/pull/1609) * Added `networking/v2/extensions/qos/rules.DeleteDSCPMarkingRule` [GH-1609](https://github.com/gophercloud/gophercloud/pull/1609) * Added `networking/v2/extensions/qos/rules.ListMinimumBandwidthRules` [GH-1615](https://github.com/gophercloud/gophercloud/pull/1615) * Added `networking/v2/extensions/qos/rules.GetMinimumBandwidthRule` [GH-1615](https://github.com/gophercloud/gophercloud/pull/1615) * Added `networking/v2/extensions/qos/rules.CreateMinimumBandwidthRule` [GH-1615](https://github.com/gophercloud/gophercloud/pull/1615) * Added `Hostname` to `baremetalintrospection/v1/introspection.Data` [GH-1627](https://github.com/gophercloud/gophercloud/pull/1627) * Added `networking/v2/extensions/qos/rules.UpdateMinimumBandwidthRule` [GH-1624](https://github.com/gophercloud/gophercloud/pull/1624) * Added `networking/v2/extensions/qos/rules.DeleteMinimumBandwidthRule` [GH-1624](https://github.com/gophercloud/gophercloud/pull/1624) * Added `networking/v2/extensions/qos/ruletypes.GetRuleType` [GH-1625](https://github.com/gophercloud/gophercloud/pull/1625) * Added `Extra` to `baremetalintrospection/v1/introspection.Data` [GH-1611](https://github.com/gophercloud/gophercloud/pull/1611) * Added `blockstorage/extensions/volumeactions.SetImageMetadata` [GH-1621](https://github.com/gophercloud/gophercloud/pull/1621) BUG FIXES * Updated `networking/v2/extensions/qos/rules.UpdateBandwidthLimitRule` to use return code 200 [GH-1606](https://github.com/gophercloud/gophercloud/pull/1606) * Fixed bug in `compute/v2/extensions/schedulerhints.SchedulerHints.Query` where contents will now be marshalled to a string [GH-1620](https://github.com/gophercloud/gophercloud/pull/1620) ## 0.1.0 (May 27, 2019) Initial tagged release. golang-github-gophercloud-gophercloud-0.12.0/LICENSE000066400000000000000000000247731367513235700222010ustar00rootroot00000000000000Copyright 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.12.0/README.md000066400000000000000000000126571367513235700224510ustar00rootroot00000000000000# 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.12.0/acceptance/000077500000000000000000000000001367513235700232455ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/acceptance/README.md000066400000000000000000000076541367513235700245400ustar00rootroot00000000000000# 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_NETWORK_ID`| The network ID to use when creating shared network| |`OS_SUBNET_ID`| The subnet ID to use when creating shared network| ### 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.12.0/acceptance/clients/000077500000000000000000000000001367513235700247065ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/acceptance/clients/clients.go000066400000000000000000000526431367513235700267100ustar00rootroot00000000000000// 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" baremetalHTTPBasic "github.com/gophercloud/gophercloud/openstack/baremetal/httpbasic" baremetalNoAuth "github.com/gophercloud/gophercloud/openstack/baremetal/noauth" blockstorageNoAuth "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 // NetworkID is the ID of a network to launch the instance on. NetworkID string // SubnetID is the ID of a subnet to launch the instance on. SubnetID string // ExternalNetworkID is the network ID of the external network. ExternalNetworkID 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") networkID := os.Getenv("OS_NETWORK_ID") subnetID := os.Getenv("OS_SUBNET_ID") floatingIPPoolName := os.Getenv("OS_POOL_NAME") externalNetworkID := os.Getenv("OS_EXTGW_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") } /* // Temporarily disabled, see https://github.com/gophercloud/gophercloud/issues/1345 if networkID == "" { missing = append(missing, "OS_NETWORK_ID") } if subnetID == "" { missing = append(missing, "OS_SUBNET_ID") } */ if networkName == "" { networkName = "private" } 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, NetworkID: networkID, SubnetID: subnetID, ExternalNetworkID: externalNetworkID, 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 := blockstorageNoAuth.NewClient(gophercloud.AuthOptions{ Username: os.Getenv("OS_USERNAME"), TenantName: os.Getenv("OS_TENANT_NAME"), }) if err != nil { return nil, err } client = configureDebug(client) return blockstorageNoAuth.NewBlockStorageNoAuth(client, blockstorageNoAuth.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 := blockstorageNoAuth.NewClient(gophercloud.AuthOptions{ Username: os.Getenv("OS_USERNAME"), TenantName: os.Getenv("OS_TENANT_NAME"), }) if err != nil { return nil, err } client = configureDebug(client) return blockstorageNoAuth.NewBlockStorageNoAuth(client, blockstorageNoAuth.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"), }) } // NewBareMetalV1Client returns a *ServiceClient for making calls // to the OpenStack Bare Metal v1 API. An error will be returned // if authentication or client creation was not possible. func NewBareMetalV1Client() (*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.NewBareMetalV1(client, gophercloud.EndpointOpts{ Region: os.Getenv("OS_REGION_NAME"), }) } // NewBareMetalV1NoAuthClient returns a *ServiceClient for making calls // to the OpenStack Bare Metal v1 API. An error will be returned // if authentication or client creation was not possible. func NewBareMetalV1NoAuthClient() (*gophercloud.ServiceClient, error) { return baremetalNoAuth.NewBareMetalNoAuth(baremetalNoAuth.EndpointOpts{ IronicEndpoint: os.Getenv("IRONIC_ENDPOINT"), }) } // NewBareMetalV1HTTPBasic returns a *ServiceClient for making calls // to the OpenStack Bare Metal v1 API. An error will be returned // if authentication or client creation was not possible. func NewBareMetalV1HTTPBasic() (*gophercloud.ServiceClient, error) { return baremetalHTTPBasic.NewBareMetalHTTPBasic(baremetalHTTPBasic.EndpointOpts{ IronicEndpoint: os.Getenv("IRONIC_ENDPOINT"), IronicUser: os.Getenv("OS_USERNAME"), IronicUserPassword: os.Getenv("OS_PASSWORD"), }) } // NewBareMetalIntrospectionV1Client returns a *ServiceClient for making calls // to the OpenStack Bare Metal Introspection v1 API. An error will be returned // if authentication or client creation was not possible. func NewBareMetalIntrospectionV1Client() (*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.NewBareMetalIntrospectionV1(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 } client = configureDebug(client) 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"), }) } // NewPlacementV1Client returns a *ServiceClient for making calls // to the OpenStack Placement API. An error will be returned // if authentication or client creation was not possible. func NewPlacementV1Client() (*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.NewPlacementV1(client, gophercloud.EndpointOpts{ Region: os.Getenv("OS_REGION_NAME"), }) } golang-github-gophercloud-gophercloud-0.12.0/acceptance/clients/conditions.go000066400000000000000000000054251367513235700274140ustar00rootroot00000000000000package 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") } } // RequirePortForwarding will restrict a test to only be run in environments // that support port forwarding func RequirePortForwarding(t *testing.T) { if os.Getenv("OS_PORTFORWARDING_ENVIRONMENT") == "" { t.Skip("this test requires support for port forwarding") } } // 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") } } // RequireIronicHTTPBasic will restric a test to be only run in // environments that have Ironic using http_basic. func RequireIronicHTTPBasic(t *testing.T) { if os.Getenv("IRONIC_ENDPOINT") == "" || os.Getenv("OS_USERNAME") == "" || os.Getenv("OS_PASSWORD") == "" { t.Skip("this test requires Ironic using http_basic, set OS_USERNAME, OS_PASSWORD and IRONIC_ENDPOINT") } } // 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.12.0/acceptance/clients/http.go000066400000000000000000000120561367513235700262200ustar00rootroot00000000000000package 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 rawData interface{} err := json.Unmarshal(raw, &rawData) if err != nil { log.Printf("[DEBUG] Unable to parse OpenStack JSON: %s", err) return string(raw) } data, ok := rawData.(map[string]interface{}) if !ok { pretty, err := json.MarshalIndent(rawData, "", " ") if err != nil { log.Printf("[DEBUG] Unable to re-marshal OpenStack JSON: %s", err) return string(raw) } return string(pretty) } // 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"] = "***" } } if v, ok := v["application_credential"].(map[string]interface{}); ok { v["secret"] = "***" } if v, ok := v["token"].(map[string]interface{}); ok { v["id"] = "***" } } } // 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.12.0/acceptance/openstack/000077500000000000000000000000001367513235700252345ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/baremetal/000077500000000000000000000000001367513235700271705ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/baremetal/httpbasic/000077500000000000000000000000001367513235700311515ustar00rootroot00000000000000allocations_test.go000066400000000000000000000022171367513235700347720ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/baremetal/httpbasic// +build acceptance baremetal allocations package httpbasic import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" v1 "github.com/gophercloud/gophercloud/acceptance/openstack/baremetal/v1" "github.com/gophercloud/gophercloud/openstack/baremetal/v1/allocations" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" ) func TestAllocationsCreateDestroy(t *testing.T) { clients.RequireLong(t) clients.RequireIronicHTTPBasic(t) client, err := clients.NewBareMetalV1HTTPBasic() th.AssertNoErr(t, err) client.Microversion = "1.52" allocation, err := v1.CreateAllocation(t, client) th.AssertNoErr(t, err) defer v1.DeleteAllocation(t, client, allocation) found := false err = allocations.List(client, allocations.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { allocationList, err := allocations.ExtractAllocations(page) if err != nil { return false, err } for _, a := range allocationList { if a.UUID == allocation.UUID { found = true return true, nil } } return false, nil }) th.AssertNoErr(t, err) th.AssertEquals(t, found, true) } golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/baremetal/httpbasic/doc.go000066400000000000000000000005171367513235700322500ustar00rootroot00000000000000package httpbasic /* Acceptance tests for Ironic endpoints with auth_strategy=noauth. Specify IRONIC_ENDPOINT, OS_USERNAME and OS_PASSWORD environment variables. For example: IRONIC_ENDPOINT="http://127.0.0.1:6385/v1" OS_USERNAME="myUser" OS_PASSWORD="myPassword" go test ./acceptance/openstack/baremetal/httpbasic/... */ golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/baremetal/httpbasic/nodes_test.go000066400000000000000000000043641367513235700336560ustar00rootroot00000000000000package httpbasic import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" v1 "github.com/gophercloud/gophercloud/acceptance/openstack/baremetal/v1" "github.com/gophercloud/gophercloud/openstack/baremetal/v1/nodes" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" ) func TestNodesCreateDestroy(t *testing.T) { clients.RequireLong(t) clients.RequireIronicHTTPBasic(t) client, err := clients.NewBareMetalV1HTTPBasic() th.AssertNoErr(t, err) client.Microversion = "1.50" node, err := v1.CreateNode(t, client) th.AssertNoErr(t, err) defer v1.DeleteNode(t, client, node) found := false err = nodes.List(client, nodes.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { nodeList, err := nodes.ExtractNodes(page) if err != nil { return false, err } for _, n := range nodeList { if n.UUID == node.UUID { found = true return true, nil } } return false, nil }) th.AssertNoErr(t, err) th.AssertEquals(t, found, true) } func TestNodesUpdate(t *testing.T) { clients.RequireLong(t) clients.RequireIronicHTTPBasic(t) client, err := clients.NewBareMetalV1HTTPBasic() th.AssertNoErr(t, err) client.Microversion = "1.50" node, err := v1.CreateNode(t, client) th.AssertNoErr(t, err) defer v1.DeleteNode(t, client, node) updated, err := nodes.Update(client, node.UUID, nodes.UpdateOpts{ nodes.UpdateOperation{ Op: nodes.ReplaceOp, Path: "/maintenance", Value: "true", }, }).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, updated.Maintenance, true) } func TestNodesRAIDConfig(t *testing.T) { clients.RequireLong(t) clients.RequireIronicHTTPBasic(t) client, err := clients.NewBareMetalV1HTTPBasic() th.AssertNoErr(t, err) client.Microversion = "1.50" node, err := v1.CreateNode(t, client) th.AssertNoErr(t, err) defer v1.DeleteNode(t, client, node) sizeGB := 100 isTrue := true err = nodes.SetRAIDConfig(client, node.UUID, nodes.RAIDConfigOpts{ LogicalDisks: []nodes.LogicalDisk{ { SizeGB: &sizeGB, IsRootVolume: &isTrue, RAIDLevel: nodes.RAID5, DiskType: nodes.HDD, NumberOfPhysicalDisks: 5, }, }, }).ExtractErr() th.AssertNoErr(t, err) } golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/baremetal/httpbasic/ports_test.go000066400000000000000000000034571367513235700337170ustar00rootroot00000000000000// +build acceptance baremetal ports package httpbasic import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" v1 "github.com/gophercloud/gophercloud/acceptance/openstack/baremetal/v1" "github.com/gophercloud/gophercloud/openstack/baremetal/v1/ports" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" ) func TestPortsCreateDestroy(t *testing.T) { clients.RequireLong(t) clients.RequireIronicHTTPBasic(t) client, err := clients.NewBareMetalV1HTTPBasic() th.AssertNoErr(t, err) client.Microversion = "1.53" node, err := v1.CreateFakeNode(t, client) port, err := v1.CreatePort(t, client, node) th.AssertNoErr(t, err) defer v1.DeleteNode(t, client, node) defer v1.DeletePort(t, client, port) found := false err = ports.List(client, ports.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { portList, err := ports.ExtractPorts(page) if err != nil { return false, err } for _, p := range portList { if p.UUID == port.UUID { found = true return true, nil } } return false, nil }) th.AssertNoErr(t, err) th.AssertEquals(t, found, true) } func TestPortsUpdate(t *testing.T) { clients.RequireLong(t) clients.RequireIronicHTTPBasic(t) client, err := clients.NewBareMetalV1HTTPBasic() th.AssertNoErr(t, err) client.Microversion = "1.53" node, err := v1.CreateFakeNode(t, client) port, err := v1.CreatePort(t, client, node) th.AssertNoErr(t, err) defer v1.DeleteNode(t, client, node) defer v1.DeletePort(t, client, port) updated, err := ports.Update(client, port.UUID, ports.UpdateOpts{ ports.UpdateOperation{ Op: ports.ReplaceOp, Path: "/address", Value: "aa:bb:cc:dd:ee:ff", }, }).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, updated.Address, "aa:bb:cc:dd:ee:ff") } golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/baremetal/noauth/000077500000000000000000000000001367513235700304665ustar00rootroot00000000000000allocations_test.go000066400000000000000000000021541367513235700343070ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/baremetal/noauth// +build acceptance baremetal allocations package noauth import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" v1 "github.com/gophercloud/gophercloud/acceptance/openstack/baremetal/v1" "github.com/gophercloud/gophercloud/openstack/baremetal/v1/allocations" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" ) func TestAllocationsCreateDestroy(t *testing.T) { clients.RequireLong(t) client, err := clients.NewBareMetalV1NoAuthClient() th.AssertNoErr(t, err) client.Microversion = "1.52" allocation, err := v1.CreateAllocation(t, client) th.AssertNoErr(t, err) defer v1.DeleteAllocation(t, client, allocation) found := false err = allocations.List(client, allocations.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { allocationList, err := allocations.ExtractAllocations(page) if err != nil { return false, err } for _, a := range allocationList { if a.UUID == allocation.UUID { found = true return true, nil } } return false, nil }) th.AssertNoErr(t, err) th.AssertEquals(t, found, true) } golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/baremetal/noauth/doc.go000066400000000000000000000003661367513235700315670ustar00rootroot00000000000000package noauth /* Acceptance tests for Ironic endpoints with auth_strategy=noauth. Specify IRONIC_ENDPOINT environment variable. For example: IRONIC_ENDPOINT="http://127.0.0.1:6385/v1" go test ./acceptance/openstack/baremetal/noauth/... */ golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/baremetal/noauth/nodes_test.go000066400000000000000000000042211367513235700331630ustar00rootroot00000000000000package noauth import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" v1 "github.com/gophercloud/gophercloud/acceptance/openstack/baremetal/v1" "github.com/gophercloud/gophercloud/openstack/baremetal/v1/nodes" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" ) func TestNodesCreateDestroy(t *testing.T) { clients.RequireLong(t) client, err := clients.NewBareMetalV1NoAuthClient() th.AssertNoErr(t, err) client.Microversion = "1.50" node, err := v1.CreateNode(t, client) th.AssertNoErr(t, err) defer v1.DeleteNode(t, client, node) found := false err = nodes.List(client, nodes.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { nodeList, err := nodes.ExtractNodes(page) if err != nil { return false, err } for _, n := range nodeList { if n.UUID == node.UUID { found = true return true, nil } } return false, nil }) th.AssertNoErr(t, err) th.AssertEquals(t, found, true) } func TestNodesUpdate(t *testing.T) { clients.RequireLong(t) client, err := clients.NewBareMetalV1NoAuthClient() th.AssertNoErr(t, err) client.Microversion = "1.50" node, err := v1.CreateNode(t, client) th.AssertNoErr(t, err) defer v1.DeleteNode(t, client, node) updated, err := nodes.Update(client, node.UUID, nodes.UpdateOpts{ nodes.UpdateOperation{ Op: nodes.ReplaceOp, Path: "/maintenance", Value: "true", }, }).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, updated.Maintenance, true) } func TestNodesRAIDConfig(t *testing.T) { clients.RequireLong(t) client, err := clients.NewBareMetalV1NoAuthClient() th.AssertNoErr(t, err) client.Microversion = "1.50" node, err := v1.CreateNode(t, client) th.AssertNoErr(t, err) defer v1.DeleteNode(t, client, node) sizeGB := 100 isTrue := true err = nodes.SetRAIDConfig(client, node.UUID, nodes.RAIDConfigOpts{ LogicalDisks: []nodes.LogicalDisk{ { SizeGB: &sizeGB, IsRootVolume: &isTrue, RAIDLevel: nodes.RAID5, DiskType: nodes.HDD, NumberOfPhysicalDisks: 5, }, }, }).ExtractErr() th.AssertNoErr(t, err) } golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/baremetal/noauth/ports_test.go000066400000000000000000000033541367513235700332300ustar00rootroot00000000000000// +build acceptance baremetal ports package noauth import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" v1 "github.com/gophercloud/gophercloud/acceptance/openstack/baremetal/v1" "github.com/gophercloud/gophercloud/openstack/baremetal/v1/ports" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" ) func TestPortsCreateDestroy(t *testing.T) { clients.RequireLong(t) client, err := clients.NewBareMetalV1NoAuthClient() th.AssertNoErr(t, err) client.Microversion = "1.53" node, err := v1.CreateFakeNode(t, client) port, err := v1.CreatePort(t, client, node) th.AssertNoErr(t, err) defer v1.DeleteNode(t, client, node) defer v1.DeletePort(t, client, port) found := false err = ports.List(client, ports.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { portList, err := ports.ExtractPorts(page) if err != nil { return false, err } for _, p := range portList { if p.UUID == port.UUID { found = true return true, nil } } return false, nil }) th.AssertNoErr(t, err) th.AssertEquals(t, found, true) } func TestPortsUpdate(t *testing.T) { clients.RequireLong(t) client, err := clients.NewBareMetalV1NoAuthClient() th.AssertNoErr(t, err) client.Microversion = "1.53" node, err := v1.CreateFakeNode(t, client) port, err := v1.CreatePort(t, client, node) th.AssertNoErr(t, err) defer v1.DeleteNode(t, client, node) defer v1.DeletePort(t, client, port) updated, err := ports.Update(client, port.UUID, ports.UpdateOpts{ ports.UpdateOperation{ Op: ports.ReplaceOp, Path: "/address", Value: "aa:bb:cc:dd:ee:ff", }, }).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, updated.Address, "aa:bb:cc:dd:ee:ff") } golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/baremetal/v1/000077500000000000000000000000001367513235700275165ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/baremetal/v1/allocations_test.go000066400000000000000000000020211367513235700334070ustar00rootroot00000000000000// +build acceptance baremetal allocations package v1 import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/openstack/baremetal/v1/allocations" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" ) func TestAllocationsCreateDestroy(t *testing.T) { clients.RequireLong(t) client, err := clients.NewBareMetalV1Client() th.AssertNoErr(t, err) client.Microversion = "1.52" allocation, err := CreateAllocation(t, client) th.AssertNoErr(t, err) defer DeleteAllocation(t, client, allocation) found := false err = allocations.List(client, allocations.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { allocationList, err := allocations.ExtractAllocations(page) if err != nil { return false, err } for _, a := range allocationList { if a.UUID == allocation.UUID { found = true return true, nil } } return false, nil }) th.AssertNoErr(t, err) th.AssertEquals(t, found, true) } golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/baremetal/v1/baremetal.go000066400000000000000000000072411367513235700320050ustar00rootroot00000000000000package v1 import ( "testing" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/baremetal/v1/allocations" "github.com/gophercloud/gophercloud/openstack/baremetal/v1/nodes" "github.com/gophercloud/gophercloud/openstack/baremetal/v1/ports" ) // CreateNode creates a basic node with a randomly generated name. func CreateNode(t *testing.T, client *gophercloud.ServiceClient) (*nodes.Node, error) { name := tools.RandomString("ACPTTEST", 16) t.Logf("Attempting to create bare metal node: %s", name) node, err := nodes.Create(client, nodes.CreateOpts{ Name: name, Driver: "ipmi", BootInterface: "ipxe", RAIDInterface: "agent", DriverInfo: map[string]interface{}{ "ipmi_port": "6230", "ipmi_username": "admin", "deploy_kernel": "http://172.22.0.1/images/tinyipa-stable-rocky.vmlinuz", "ipmi_address": "192.168.122.1", "deploy_ramdisk": "http://172.22.0.1/images/tinyipa-stable-rocky.gz", "ipmi_password": "admin", }, }).Extract() return node, err } // DeleteNode deletes a bare metal node via its UUID. func DeleteNode(t *testing.T, client *gophercloud.ServiceClient, node *nodes.Node) { err := nodes.Delete(client, node.UUID).ExtractErr() if err != nil { t.Fatalf("Unable to delete node %s: %s", node.UUID, err) } t.Logf("Deleted server: %s", node.UUID) } // CreateAllocation creates an allocation func CreateAllocation(t *testing.T, client *gophercloud.ServiceClient) (*allocations.Allocation, error) { name := tools.RandomString("ACPTTEST", 16) t.Logf("Attempting to create bare metal allocation: %s", name) allocation, err := allocations.Create(client, allocations.CreateOpts{ Name: name, ResourceClass: "baremetal", }).Extract() return allocation, err } // DeleteAllocation deletes a bare metal allocation via its UUID. func DeleteAllocation(t *testing.T, client *gophercloud.ServiceClient, allocation *allocations.Allocation) { err := allocations.Delete(client, allocation.UUID).ExtractErr() if err != nil { t.Fatalf("Unable to delete allocation %s: %s", allocation.UUID, err) } t.Logf("Deleted allocation: %s", allocation.UUID) } // CreateFakeNode creates a node with fake-hardware to use for port tests. func CreateFakeNode(t *testing.T, client *gophercloud.ServiceClient) (*nodes.Node, error) { name := tools.RandomString("ACPTTEST", 16) t.Logf("Attempting to create bare metal node: %s", name) node, err := nodes.Create(client, nodes.CreateOpts{ Name: name, Driver: "fake-hardware", BootInterface: "fake", DriverInfo: map[string]interface{}{ "ipmi_port": "6230", "ipmi_username": "admin", "deploy_kernel": "http://172.22.0.1/images/tinyipa-stable-rocky.vmlinuz", "ipmi_address": "192.168.122.1", "deploy_ramdisk": "http://172.22.0.1/images/tinyipa-stable-rocky.gz", "ipmi_password": "admin", }, }).Extract() return node, err } // CreatePort - creates a port for a node with a fixed Address func CreatePort(t *testing.T, client *gophercloud.ServiceClient, node *nodes.Node) (*ports.Port, error) { mac := "e6:72:1f:52:00:f4" t.Logf("Attempting to create Port for Node: %s with Address: %s", node.UUID, mac) iTrue := true port, err := ports.Create(client, ports.CreateOpts{ NodeUUID: node.UUID, Address: mac, PXEEnabled: &iTrue, }).Extract() return port, err } // DeletePort - deletes a port via its UUID func DeletePort(t *testing.T, client *gophercloud.ServiceClient, port *ports.Port) { err := ports.Delete(client, port.UUID).ExtractErr() if err != nil { t.Fatalf("Unable to delete port %s: %s", port.UUID, err) } t.Logf("Deleted port: %s", port.UUID) } golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/baremetal/v1/nodes_test.go000066400000000000000000000047611367513235700322240ustar00rootroot00000000000000// +build acceptance baremetal nodes package v1 import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/openstack/baremetal/v1/nodes" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" ) func TestNodesCreateDestroy(t *testing.T) { clients.RequireLong(t) client, err := clients.NewBareMetalV1Client() th.AssertNoErr(t, err) client.Microversion = "1.38" node, err := CreateNode(t, client) th.AssertNoErr(t, err) defer DeleteNode(t, client, node) found := false err = nodes.List(client, nodes.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { nodeList, err := nodes.ExtractNodes(page) if err != nil { return false, err } for _, n := range nodeList { if n.UUID == node.UUID { found = true return true, nil } } return false, nil }) th.AssertNoErr(t, err) th.AssertEquals(t, found, true) } func TestNodesUpdate(t *testing.T) { clients.RequireLong(t) client, err := clients.NewBareMetalV1Client() th.AssertNoErr(t, err) client.Microversion = "1.38" node, err := CreateNode(t, client) th.AssertNoErr(t, err) defer DeleteNode(t, client, node) updated, err := nodes.Update(client, node.UUID, nodes.UpdateOpts{ nodes.UpdateOperation{ Op: nodes.ReplaceOp, Path: "/maintenance", Value: "true", }, }).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, updated.Maintenance, true) } func TestNodesRAIDConfig(t *testing.T) { clients.RequireLong(t) client, err := clients.NewBareMetalV1Client() th.AssertNoErr(t, err) client.Microversion = "1.50" node, err := CreateNode(t, client) th.AssertNoErr(t, err) defer DeleteNode(t, client, node) sizeGB := 100 isTrue := true err = nodes.SetRAIDConfig(client, node.UUID, nodes.RAIDConfigOpts{ LogicalDisks: []nodes.LogicalDisk{ { SizeGB: &sizeGB, IsRootVolume: &isTrue, RAIDLevel: nodes.RAID5, Controller: "software", PhysicalDisks: []interface{}{ map[string]string{ "size": "> 100", }, map[string]string{ "size": "> 100", }, }, }, }, }).ExtractErr() th.AssertNoErr(t, err) err = nodes.SetRAIDConfig(client, node.UUID, nodes.RAIDConfigOpts{ LogicalDisks: []nodes.LogicalDisk{ { SizeGB: &sizeGB, IsRootVolume: &isTrue, RAIDLevel: nodes.RAID5, DiskType: nodes.HDD, NumberOfPhysicalDisks: 5, }, }, }).ExtractErr() th.AssertNoErr(t, err) } golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/baremetal/v1/ports_test.go000066400000000000000000000032511367513235700322540ustar00rootroot00000000000000// +build acceptance baremetal ports package v1 import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/openstack/baremetal/v1/ports" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" ) func TestPortsCreateDestroy(t *testing.T) { clients.RequireLong(t) client, err := clients.NewBareMetalV1Client() th.AssertNoErr(t, err) client.Microversion = "1.53" node, err := CreateFakeNode(t, client) th.AssertNoErr(t, err) defer DeleteNode(t, client, node) port, err := CreatePort(t, client, node) th.AssertNoErr(t, err) defer DeletePort(t, client, port) found := false err = ports.List(client, ports.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { portList, err := ports.ExtractPorts(page) if err != nil { return false, err } for _, p := range portList { if p.UUID == port.UUID { found = true return true, nil } } return false, nil }) th.AssertNoErr(t, err) th.AssertEquals(t, found, true) } func TestPortsUpdate(t *testing.T) { clients.RequireLong(t) client, err := clients.NewBareMetalV1Client() th.AssertNoErr(t, err) client.Microversion = "1.53" node, err := CreateFakeNode(t, client) th.AssertNoErr(t, err) defer DeleteNode(t, client, node) port, err := CreatePort(t, client, node) th.AssertNoErr(t, err) defer DeletePort(t, client, port) updated, err := ports.Update(client, port.UUID, ports.UpdateOpts{ ports.UpdateOperation{ Op: ports.ReplaceOp, Path: "/address", Value: "aa:bb:cc:dd:ee:ff", }, }).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, updated.Address, "aa:bb:cc:dd:ee:ff") } golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/blockstorage/000077500000000000000000000000001367513235700277135ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/blockstorage/apiversions_test.go000066400000000000000000000023271367513235700336470ustar00rootroot00000000000000// +build acceptance blockstorage package blockstorage import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/blockstorage/apiversions" ) func TestAPIVersionsList(t *testing.T) { client, err := clients.NewBlockStorageV2Client() if err != nil { t.Fatalf("Unable to create a blockstorage client: %v", err) } allPages, err := apiversions.List(client).AllPages() if err != nil { t.Fatalf("Unable to retrieve API versions: %v", err) } allVersions, err := apiversions.ExtractAPIVersions(allPages) if err != nil { t.Fatalf("Unable to extract API versions: %v", err) } for _, v := range allVersions { tools.PrintResource(t, v) } } func TestAPIVersionsGet(t *testing.T) { client, err := clients.NewBlockStorageV2Client() if err != nil { t.Fatalf("Unable to create a blockstorage client: %v", err) } allPages, err := apiversions.List(client).AllPages() if err != nil { t.Fatalf("Unable to retrieve API versions: %v", err) } v, err := apiversions.ExtractAPIVersion(allPages, "v3.0") if err != nil { t.Fatalf("Unable to extract API version: %v", err) } tools.PrintResource(t, v) } golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/blockstorage/extensions/000077500000000000000000000000001367513235700321125ustar00rootroot00000000000000backups_test.go000066400000000000000000000020731367513235700350530ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/blockstorage/extensions// +build acceptance blockstorage package extensions import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/openstack/blockstorage/extensions/backups" blockstorage "github.com/gophercloud/gophercloud/acceptance/openstack/blockstorage/v3" th "github.com/gophercloud/gophercloud/testhelper" ) func TestBackupsCRUD(t *testing.T) { clients.SkipRelease(t, "stable/mitaka") blockClient, err := clients.NewBlockStorageV3Client() th.AssertNoErr(t, err) volume, err := blockstorage.CreateVolume(t, blockClient) th.AssertNoErr(t, err) defer blockstorage.DeleteVolume(t, blockClient, volume) backup, err := CreateBackup(t, blockClient, volume.ID) th.AssertNoErr(t, err) defer DeleteBackup(t, blockClient, backup.ID) allPages, err := backups.List(blockClient, nil).AllPages() th.AssertNoErr(t, err) allBackups, err := backups.ExtractBackups(allPages) th.AssertNoErr(t, err) var found bool for _, v := range allBackups { if backup.Name == v.Name { found = true } } th.AssertEquals(t, found, true) } extensions.go000066400000000000000000000212051367513235700345610ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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 ( "fmt" "strings" "testing" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/blockstorage/extensions/backups" "github.com/gophercloud/gophercloud/openstack/blockstorage/extensions/volumeactions" "github.com/gophercloud/gophercloud/openstack/blockstorage/v2/volumes" v3 "github.com/gophercloud/gophercloud/openstack/blockstorage/v3/volumes" "github.com/gophercloud/gophercloud/openstack/compute/v2/images" "github.com/gophercloud/gophercloud/openstack/compute/v2/servers" th "github.com/gophercloud/gophercloud/testhelper" ) // 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, imageID string) error { if testing.Short() { t.Skip("Skipping test that requires volume-backed image removing in short mode.") } 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 } // SetImageMetadata will apply the metadata to a volume. func SetImageMetadata(t *testing.T, client *gophercloud.ServiceClient, volume *volumes.Volume) error { t.Logf("Attempting to apply image metadata to volume %s", volume.ID) imageMetadataOpts := volumeactions.ImageMetadataOpts{ Metadata: map[string]string{ "image_name": "testimage", }, } err := volumeactions.SetImageMetadata(client, volume.ID, imageMetadataOpts).ExtractErr() if err != nil { return err } return nil } // CreateBackup will create a backup based on a volume. An error will be // will be returned if the backup could not be created. func CreateBackup(t *testing.T, client *gophercloud.ServiceClient, volumeID string) (*backups.Backup, error) { t.Logf("Attempting to create a backup of volume %s", volumeID) backupName := tools.RandomString("ACPTTEST", 16) createOpts := backups.CreateOpts{ VolumeID: volumeID, Name: backupName, } backup, err := backups.Create(client, createOpts).Extract() if err != nil { return nil, err } err = WaitForBackupStatus(client, backup.ID, "available", 120) if err != nil { return nil, err } backup, err = backups.Get(client, backup.ID).Extract() if err != nil { return nil, err } t.Logf("Successfully created backup %s", backup.ID) tools.PrintResource(t, backup) th.AssertEquals(t, backup.Name, backupName) return backup, nil } // DeleteBackup will delete a backup. A fatal error will occur if the backup // could not be deleted. This works best when used as a deferred function. func DeleteBackup(t *testing.T, client *gophercloud.ServiceClient, backupID string) { if err := backups.Delete(client, backupID).ExtractErr(); err != nil { t.Fatalf("Unable to delete backup %s: %s", backupID, err) } t.Logf("Deleted backup %s", backupID) } // WaitForBackupStatus will continually poll a backup, checking for a particular // status. It will do this for the amount of seconds defined. func WaitForBackupStatus(client *gophercloud.ServiceClient, id, status string, secs int) error { return gophercloud.WaitFor(secs, func() (bool, error) { current, err := backups.Get(client, id).Extract() if err != nil { return false, err } if current.Status == status { return true, nil } return false, nil }) } // SetBootable will set a bootable status to a volume. func SetBootable(t *testing.T, client *gophercloud.ServiceClient, volume *volumes.Volume) error { t.Logf("Attempting to apply bootable status to volume %s", volume.ID) bootableOpts := volumeactions.BootableOpts{ Bootable: true, } err := volumeactions.SetBootable(client, volume.ID, bootableOpts).ExtractErr() if err != nil { return err } vol, err := v3.Get(client, volume.ID).Extract() if err != nil { return err } if strings.ToLower(vol.Bootable) != "true" { return fmt.Errorf("Volume bootable status is %q, expected 'true'", vol.Bootable) } bootableOpts = volumeactions.BootableOpts{ Bootable: false, } err = volumeactions.SetBootable(client, volume.ID, bootableOpts).ExtractErr() if err != nil { return err } vol, err = v3.Get(client, volume.ID).Extract() if err != nil { return err } if strings.ToLower(vol.Bootable) == "true" { return fmt.Errorf("Volume bootable status is %q, expected 'false'", vol.Bootable) } return nil } golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/blockstorage/extensions/pkg.go000066400000000000000000000001651367513235700332240ustar00rootroot00000000000000// The extensions package contains acceptance tests for the Openstack Cinder extensions service. package extensions schedulerhints_test.go000066400000000000000000000030531367513235700364460ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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/schedulerhints" "github.com/gophercloud/gophercloud/openstack/blockstorage/v3/volumes" th "github.com/gophercloud/gophercloud/testhelper" ) func TestSchedulerHints(t *testing.T) { clients.SkipRelease(t, "stable/mitaka") clients.RequireLong(t) client, err := clients.NewBlockStorageV3Client() th.AssertNoErr(t, err) volumeName := tools.RandomString("ACPTTEST", 16) createOpts := volumes.CreateOpts{ Size: 1, Name: volumeName, } volume1, err := volumes.Create(client, createOpts).Extract() th.AssertNoErr(t, err) err = volumes.WaitForStatus(client, volume1.ID, "available", 60) th.AssertNoErr(t, err) defer volumes.Delete(client, volume1.ID, volumes.DeleteOpts{}) volumeName = tools.RandomString("ACPTTEST", 16) base := volumes.CreateOpts{ Size: 1, Name: volumeName, } schedulerHints := schedulerhints.SchedulerHints{ SameHost: []string{ volume1.ID, }, } createOptsWithHints := schedulerhints.CreateOptsExt{ VolumeCreateOptsBuilder: base, SchedulerHints: schedulerHints, } volume2, err := volumes.Create(client, createOptsWithHints).Extract() th.AssertNoErr(t, err) err = volumes.WaitForStatus(client, volume2.ID, "available", 60) th.AssertNoErr(t, err) err = volumes.Delete(client, volume2.ID, volumes.DeleteOpts{}).ExtractErr() th.AssertNoErr(t, err) } schedulerstats_test.go000066400000000000000000000016711367513235700364630ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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" th "github.com/gophercloud/gophercloud/testhelper" ) func TestSchedulerStatsList(t *testing.T) { clients.RequireAdmin(t) clients.SkipRelease(t, "stable/mitaka") clients.SkipRelease(t, "stable/newton") clients.SkipRelease(t, "stable/ocata") clients.SkipRelease(t, "stable/pike") blockClient, err := clients.NewBlockStorageV2Client() th.AssertNoErr(t, err) listOpts := schedulerstats.ListOpts{ Detail: true, } allPages, err := schedulerstats.List(blockClient, listOpts).AllPages() th.AssertNoErr(t, err) allStats, err := schedulerstats.ExtractStoragePools(allPages) th.AssertNoErr(t, err) for _, stat := range allStats { tools.PrintResource(t, stat) } } services_test.go000066400000000000000000000014021367513235700352410ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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" th "github.com/gophercloud/gophercloud/testhelper" ) func TestServicesList(t *testing.T) { clients.SkipRelease(t, "stable/mitaka") clients.RequireAdmin(t) blockClient, err := clients.NewBlockStorageV3Client() th.AssertNoErr(t, err) allPages, err := services.List(blockClient, services.ListOpts{}).AllPages() th.AssertNoErr(t, err) allServices, err := services.ExtractServices(allPages) th.AssertNoErr(t, err) for _, service := range allServices { tools.PrintResource(t, service) } } volumeactions_test.go000066400000000000000000000110521367513235700363100ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/blockstorage/extensions// +build acceptance blockstorage package extensions import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" blockstorage "github.com/gophercloud/gophercloud/acceptance/openstack/blockstorage/v2" compute "github.com/gophercloud/gophercloud/acceptance/openstack/compute/v2" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/blockstorage/v2/volumes" th "github.com/gophercloud/gophercloud/testhelper" ) func TestVolumeActionsUploadImageDestroy(t *testing.T) { blockClient, err := clients.NewBlockStorageV2Client() th.AssertNoErr(t, err) computeClient, err := clients.NewComputeV2Client() th.AssertNoErr(t, err) volume, err := blockstorage.CreateVolume(t, blockClient) th.AssertNoErr(t, err) defer blockstorage.DeleteVolume(t, blockClient, volume) volumeImage, err := CreateUploadImage(t, blockClient, volume) th.AssertNoErr(t, err) tools.PrintResource(t, volumeImage) err = DeleteUploadedImage(t, computeClient, volumeImage.ImageID) th.AssertNoErr(t, err) } func TestVolumeActionsAttachCreateDestroy(t *testing.T) { blockClient, err := clients.NewBlockStorageV2Client() th.AssertNoErr(t, err) computeClient, err := clients.NewComputeV2Client() th.AssertNoErr(t, err) server, err := compute.CreateServer(t, computeClient) th.AssertNoErr(t, err) defer compute.DeleteServer(t, computeClient, server) volume, err := blockstorage.CreateVolume(t, blockClient) th.AssertNoErr(t, err) defer blockstorage.DeleteVolume(t, blockClient, volume) err = CreateVolumeAttach(t, blockClient, volume, server) th.AssertNoErr(t, err) newVolume, err := volumes.Get(blockClient, volume.ID).Extract() th.AssertNoErr(t, err) DeleteVolumeAttach(t, blockClient, newVolume) } func TestVolumeActionsReserveUnreserve(t *testing.T) { client, err := clients.NewBlockStorageV2Client() th.AssertNoErr(t, err) volume, err := blockstorage.CreateVolume(t, client) th.AssertNoErr(t, err) defer blockstorage.DeleteVolume(t, client, volume) err = CreateVolumeReserve(t, client, volume) th.AssertNoErr(t, err) defer DeleteVolumeReserve(t, client, volume) } func TestVolumeActionsExtendSize(t *testing.T) { blockClient, err := clients.NewBlockStorageV2Client() th.AssertNoErr(t, err) volume, err := blockstorage.CreateVolume(t, blockClient) th.AssertNoErr(t, err) defer blockstorage.DeleteVolume(t, blockClient, volume) tools.PrintResource(t, volume) err = ExtendVolumeSize(t, blockClient, volume) th.AssertNoErr(t, err) newVolume, err := volumes.Get(blockClient, volume.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, newVolume) } func TestVolumeActionsImageMetadata(t *testing.T) { blockClient, err := clients.NewBlockStorageV2Client() th.AssertNoErr(t, err) volume, err := blockstorage.CreateVolume(t, blockClient) th.AssertNoErr(t, err) defer blockstorage.DeleteVolume(t, blockClient, volume) err = SetImageMetadata(t, blockClient, volume) th.AssertNoErr(t, err) } func TestVolumeActionsSetBootable(t *testing.T) { blockClient, err := clients.NewBlockStorageV2Client() th.AssertNoErr(t, err) volume, err := blockstorage.CreateVolume(t, blockClient) th.AssertNoErr(t, err) defer blockstorage.DeleteVolume(t, blockClient, volume) err = SetBootable(t, blockClient, volume) th.AssertNoErr(t, err) } // 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, volumes.DeleteOpts{}).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) } */ volumetenants_test.go000066400000000000000000000024531367513235700363310ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/blockstorage/extensions// +build acceptance blockstorage package extensions import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" blockstorage "github.com/gophercloud/gophercloud/acceptance/openstack/blockstorage/v3" "github.com/gophercloud/gophercloud/openstack/blockstorage/extensions/volumetenants" "github.com/gophercloud/gophercloud/openstack/blockstorage/v3/volumes" th "github.com/gophercloud/gophercloud/testhelper" ) func TestVolumeTenants(t *testing.T) { clients.SkipRelease(t, "stable/mitaka") type volumeWithTenant struct { volumes.Volume volumetenants.VolumeTenantExt } var allVolumes []volumeWithTenant client, err := clients.NewBlockStorageV3Client() th.AssertNoErr(t, err) listOpts := volumes.ListOpts{ Name: "I SHOULD NOT EXIST", } allPages, err := volumes.List(client, listOpts).AllPages() th.AssertNoErr(t, err) err = volumes.ExtractVolumesInto(allPages, &allVolumes) th.AssertNoErr(t, err) th.AssertEquals(t, 0, len(allVolumes)) volume1, err := blockstorage.CreateVolume(t, client) th.AssertNoErr(t, err) defer blockstorage.DeleteVolume(t, client, volume1) allPages, err = volumes.List(client, nil).AllPages() th.AssertNoErr(t, err) err = volumes.ExtractVolumesInto(allPages, &allVolumes) th.AssertNoErr(t, err) th.AssertEquals(t, true, len(allVolumes) > 0) } golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/blockstorage/noauth/000077500000000000000000000000001367513235700312115ustar00rootroot00000000000000blockstorage.go000066400000000000000000000102531367513235700341410ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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, volumes.DeleteOpts{}).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) } golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/blockstorage/noauth/pkg.go000066400000000000000000000001551367513235700323220ustar00rootroot00000000000000// The noauth package contains acceptance tests for the Openstack Cinder standalone service. package noauth snapshots_test.go000066400000000000000000000027021367513235700345430ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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.go000066400000000000000000000023701367513235700342140ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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.12.0/acceptance/openstack/blockstorage/v1/000077500000000000000000000000001367513235700302415ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/blockstorage/v1/blockstorage.go000066400000000000000000000110561367513235700332520ustar00rootroot00000000000000// 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) volumeDescription := tools.RandomString("ACPTTEST-DESC", 16) t.Logf("Attempting to create volume: %s", volumeName) createOpts := volumes.CreateOpts{ Size: 1, Name: volumeName, Description: volumeDescription, } 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) } golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/blockstorage/v1/pkg.go000066400000000000000000000001041367513235700313440ustar00rootroot00000000000000// Package v1 contains openstack cinder acceptance tests package v1 golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/blockstorage/v1/snapshots_test.go000066400000000000000000000026661367513235700336630ustar00rootroot00000000000000// +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) } golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/blockstorage/v1/volumes_test.go000066400000000000000000000035361367513235700333300ustar00rootroot00000000000000// +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" th "github.com/gophercloud/gophercloud/testhelper" ) 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) th.AssertEquals(t, volume.Name, newVolume.Name) th.AssertEquals(t, volume.Description, newVolume.Description) // Update volume updatedVolumeName := "" updatedVolumeDescription := "" updateOpts := volumes.UpdateOpts{ Name: &updatedVolumeName, Description: &updatedVolumeDescription, } updatedVolume, err := volumes.Update(client, volume.ID, updateOpts).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, updatedVolume) th.AssertEquals(t, updatedVolume.Name, updatedVolumeName) th.AssertEquals(t, updatedVolume.Description, updatedVolumeDescription) } volumetypes_test.go000066400000000000000000000022341367513235700341450ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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.12.0/acceptance/openstack/blockstorage/v2/000077500000000000000000000000001367513235700302425ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/blockstorage/v2/blockstorage.go000066400000000000000000000113161367513235700332520ustar00rootroot00000000000000// 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) volumeDescription := tools.RandomString("ACPTTEST-DESC", 16) t.Logf("Attempting to create volume: %s", volumeName) createOpts := volumes.CreateOpts{ Size: 1, Name: volumeName, Description: volumeDescription, } 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 } tools.PrintResource(t, volume) th.AssertEquals(t, volume.Name, volumeName) th.AssertEquals(t, volume.Description, volumeDescription) th.AssertEquals(t, volume.Size, 1) 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, volumes.DeleteOpts{}).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) } golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/blockstorage/v2/pkg.go000066400000000000000000000001351367513235700313510ustar00rootroot00000000000000// The v2 package contains acceptance tests for the Openstack Cinder V2 service. package v2 golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/blockstorage/v2/snapshots_test.go000066400000000000000000000021341367513235700336520ustar00rootroot00000000000000// +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) } golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/blockstorage/v2/volumes_test.go000066400000000000000000000064101367513235700333230ustar00rootroot00000000000000// +build acceptance blockstorage 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/extensions/volumeactions" "github.com/gophercloud/gophercloud/openstack/blockstorage/v2/snapshots" "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) // Update volume updatedVolumeName := "" updatedVolumeDescription := "" updateOpts := volumes.UpdateOpts{ Name: &updatedVolumeName, Description: &updatedVolumeDescription, } updatedVolume, err := volumes.Update(client, volume.ID, updateOpts).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, updatedVolume) th.AssertEquals(t, updatedVolume.Name, updatedVolumeName) th.AssertEquals(t, updatedVolume.Description, updatedVolumeDescription) 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) } func TestVolumesCascadeDelete(t *testing.T) { clients.RequireLong(t) client, err := clients.NewBlockStorageV2Client() th.AssertNoErr(t, err) vol, err := CreateVolume(t, client) th.AssertNoErr(t, err) err = volumes.WaitForStatus(client, vol.ID, "available", 60) th.AssertNoErr(t, err) snapshot1, err := CreateSnapshot(t, client, vol) th.AssertNoErr(t, err) snapshot2, err := CreateSnapshot(t, client, vol) th.AssertNoErr(t, err) t.Logf("Attempting to delete volume: %s", vol.ID) deleteOpts := volumes.DeleteOpts{Cascade: true} err = volumes.Delete(client, vol.ID, deleteOpts).ExtractErr() if err != nil { t.Fatalf("Unable to delete volume %s: %v", vol.ID, err) } for _, sid := range []string{snapshot1.ID, snapshot2.ID} { err := gophercloud.WaitFor(120, func() (bool, error) { _, err := snapshots.Get(client, sid).Extract() if err != nil { return true, nil } return false, nil }) th.AssertNoErr(t, err) t.Logf("Successfully deleted snapshot: %s", sid) } err = gophercloud.WaitFor(120, func() (bool, error) { _, err := volumes.Get(client, vol.ID).Extract() if err != nil { return true, nil } return false, nil }) th.AssertNoErr(t, err) t.Logf("Successfully deleted volume: %s", vol.ID) } golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/blockstorage/v3/000077500000000000000000000000001367513235700302435ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/blockstorage/v3/blockstorage.go000066400000000000000000000126131367513235700332540ustar00rootroot00000000000000// 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 } tools.PrintResource(t, snapshot) th.AssertEquals(t, snapshot.Name, snapshotName) th.AssertEquals(t, snapshot.VolumeID, volume.ID) 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) volumeDescription := tools.RandomString("ACPTTEST-DESC", 16) t.Logf("Attempting to create volume: %s", volumeName) createOpts := volumes.CreateOpts{ Size: 1, Name: volumeName, Description: volumeDescription, } 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 } tools.PrintResource(t, volume) th.AssertEquals(t, volume.Name, volumeName) th.AssertEquals(t, volume.Description, volumeDescription) th.AssertEquals(t, volume.Size, 1) 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) description := "create_from_gophercloud" 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: description, } vt, err := volumetypes.Create(client, createOpts).Extract() if err != nil { return nil, err } tools.PrintResource(t, vt) th.AssertEquals(t, vt.IsPublic, true) th.AssertEquals(t, vt.Name, name) th.AssertEquals(t, vt.Description, description) // TODO: For some reason returned extra_specs are empty even in API reference: https://developer.openstack.org/api-ref/block-storage/v3/?expanded=create-a-volume-type-detail#volume-types-types // "extra_specs": {} // th.AssertEquals(t, vt.ExtraSpecs, createOpts.ExtraSpecs) 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, volumes.DeleteOpts{}).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) } golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/blockstorage/v3/pkg.go000066400000000000000000000001351367513235700313520ustar00rootroot00000000000000// The v3 package contains acceptance tests for the OpenStack Cinder V3 service. package v3 golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/blockstorage/v3/quotaset_test.go000066400000000000000000000106221367513235700334770ustar00rootroot00000000000000// +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.SkipRelease(t, "stable/mitaka") 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.SkipRelease(t, "stable/mitaka") 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.SkipRelease(t, "stable/mitaka") 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), Groups: gophercloud.IntToPointer(350), } var UpdatedQuotas = quotasets.QuotaSet{ Volumes: 100, Snapshots: 200, Gigabytes: 300, PerVolumeGigabytes: 50, Backups: 2, BackupGigabytes: 300, Groups: 350, } func TestQuotasetUpdate(t *testing.T) { clients.SkipRelease(t, "stable/mitaka") clients.SkipRelease(t, "stable/newton") clients.SkipRelease(t, "stable/ocata") 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.SkipRelease(t, "stable/mitaka") clients.SkipRelease(t, "stable/newton") clients.SkipRelease(t, "stable/ocata") 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 dest.Groups = &src.Groups } golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/blockstorage/v3/snapshots_test.go000066400000000000000000000025721367513235700336610ustar00rootroot00000000000000// +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.SkipRelease(t, "stable/mitaka") 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) } volumeattachments.go000066400000000000000000000062341367513235700342630ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/blockstorage/v3package v3 import ( "fmt" "testing" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack/blockstorage/v3/attachments" v3 "github.com/gophercloud/gophercloud/openstack/blockstorage/v3/volumes" "github.com/gophercloud/gophercloud/openstack/compute/v2/servers" ) // CreateVolumeAttachment will attach a volume to an instance. An error will be // returned if the attachment failed. func CreateVolumeAttachment(t *testing.T, client *gophercloud.ServiceClient, volume *v3.Volume, server *servers.Server) error { if testing.Short() { t.Skip("Skipping test that requires volume attachment in short mode.") } attachOpts := &attachments.CreateOpts{ VolumeUUID: volume.ID, InstanceUUID: server.ID, Connector: map[string]interface{}{ "mode": "rw", "initiator": "fake", }, } t.Logf("Attempting to attach volume %s to server %s", volume.ID, server.ID) var err error var attachment *attachments.Attachment if attachment, err = attachments.Create(client, attachOpts).Extract(); err != nil { return err } mv := client.Microversion client.Microversion = "3.44" defer func() { client.Microversion = mv }() if err = attachments.Complete(client, attachment.ID).ExtractErr(); err != nil { return err } if err = attachments.WaitForStatus(client, attachment.ID, "attached", 60); err != nil { e := attachments.Delete(client, attachment.ID).ExtractErr() if e != nil { t.Logf("Failed to delete %q attachment: %s", attachment.ID, err) } return err } attachment, err = attachments.Get(client, attachment.ID).Extract() if err != nil { return err } /* // Not clear how perform a proper update, OpenStack returns "Unable to update the attachment." updateOpts := &attachments.UpdateOpts{ Connector: map[string]interface{}{ "mode": "ro", "initiator": "fake", }, } attachment, err = attachments.Update(client, attachment.ID, updateOpts).Extract() if err != nil { return err } */ listOpts := &attachments.ListOpts{ VolumeID: volume.ID, InstanceID: server.ID, } allPages, err := attachments.List(client, listOpts).AllPages() if err != nil { return err } allAttachments, err := attachments.ExtractAttachments(allPages) if err != nil { return err } if allAttachments[0].ID != attachment.ID { return fmt.Errorf("Attachment IDs from get and list are not equal: %q != %q", allAttachments[0].ID, attachment.ID) } t.Logf("Attached volume %s to server %s within %q attachment", volume.ID, server.ID, attachment.ID) return nil } // DeleteVolumeAttachment will detach a volume from an instance. A fatal error // will occur if the attachment failed to be deleted. func DeleteVolumeAttachment(t *testing.T, client *gophercloud.ServiceClient, volume *v3.Volume) { t.Logf("Attepting to detach volume volume: %s", volume.ID) if err := attachments.Delete(client, volume.Attachments[0].AttachmentID).ExtractErr(); err != nil { t.Fatalf("Unable to detach volume %s: %v", volume.ID, err) } if err := v3.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) } volumeattachments_test.go000066400000000000000000000021241367513235700353140ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/blockstorage/v3// +build acceptance blockstorage package v3 import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" compute "github.com/gophercloud/gophercloud/acceptance/openstack/compute/v2" "github.com/gophercloud/gophercloud/openstack/blockstorage/v3/volumes" th "github.com/gophercloud/gophercloud/testhelper" ) func TestVolumeAttachments(t *testing.T) { blockClient, err := clients.NewBlockStorageV3Client() th.AssertNoErr(t, err) // minimu required microversion for volume attachments is 3.27 blockClient.Microversion = "3.27" computeClient, err := clients.NewComputeV2Client() th.AssertNoErr(t, err) server, err := compute.CreateServer(t, computeClient) th.AssertNoErr(t, err) defer compute.DeleteServer(t, computeClient, server) volume, err := CreateVolume(t, blockClient) th.AssertNoErr(t, err) defer DeleteVolume(t, blockClient, volume) err = CreateVolumeAttachment(t, blockClient, volume, server) th.AssertNoErr(t, err) newVolume, err := volumes.Get(blockClient, volume.ID).Extract() th.AssertNoErr(t, err) DeleteVolumeAttachment(t, blockClient, newVolume) } golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/blockstorage/v3/volumes_test.go000066400000000000000000000075021367513235700333270ustar00rootroot00000000000000// +build acceptance blockstorage 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/blockstorage/v3/snapshots" "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.SkipRelease(t, "stable/mitaka") 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) // Update volume updatedVolumeName := "" updatedVolumeDescription := "" updateOpts := volumes.UpdateOpts{ Name: &updatedVolumeName, Description: &updatedVolumeDescription, } updatedVolume, err := volumes.Update(client, volume1.ID, updateOpts).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, updatedVolume) th.AssertEquals(t, updatedVolume.Name, updatedVolumeName) th.AssertEquals(t, updatedVolume.Description, updatedVolumeDescription) 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) } func TestVolumesMultiAttach(t *testing.T) { clients.RequireLong(t) clients.SkipRelease(t, "stable/mitaka") clients.SkipRelease(t, "stable/newton") clients.SkipRelease(t, "stable/ocata") clients.SkipRelease(t, "stable/pike") client, err := clients.NewBlockStorageV3Client() th.AssertNoErr(t, err) volumeName := tools.RandomString("ACPTTEST", 16) volOpts := volumes.CreateOpts{ Size: 1, Name: volumeName, Description: "Testing creation of multiattach enabled volume", Multiattach: true, } vol, err := volumes.Create(client, volOpts).Extract() th.AssertNoErr(t, err) err = volumes.WaitForStatus(client, vol.ID, "available", 60) th.AssertNoErr(t, err) th.AssertEquals(t, vol.Multiattach, true) err = volumes.Delete(client, vol.ID, volumes.DeleteOpts{}).ExtractErr() th.AssertNoErr(t, err) } func TestVolumesCascadeDelete(t *testing.T) { clients.SkipRelease(t, "stable/mitaka") clients.RequireLong(t) client, err := clients.NewBlockStorageV3Client() th.AssertNoErr(t, err) vol, err := CreateVolume(t, client) th.AssertNoErr(t, err) err = volumes.WaitForStatus(client, vol.ID, "available", 60) th.AssertNoErr(t, err) snapshot1, err := CreateSnapshot(t, client, vol) th.AssertNoErr(t, err) snapshot2, err := CreateSnapshot(t, client, vol) th.AssertNoErr(t, err) t.Logf("Attempting to delete volume: %s", vol.ID) deleteOpts := volumes.DeleteOpts{Cascade: true} err = volumes.Delete(client, vol.ID, deleteOpts).ExtractErr() if err != nil { t.Fatalf("Unable to delete volume %s: %v", vol.ID, err) } for _, sid := range []string{snapshot1.ID, snapshot2.ID} { err := gophercloud.WaitFor(120, func() (bool, error) { _, err := snapshots.Get(client, sid).Extract() if err != nil { return true, nil } return false, nil }) th.AssertNoErr(t, err) t.Logf("Successfully deleted snapshot: %s", sid) } err = gophercloud.WaitFor(120, func() (bool, error) { _, err := volumes.Get(client, vol.ID).Extract() if err != nil { return true, nil } return false, nil }) th.AssertNoErr(t, err) t.Logf("Successfully deleted volume: %s", vol.ID) } volumetypes_test.go000066400000000000000000000025441367513235700341530ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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.SkipRelease(t, "stable/mitaka") 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) isPublic := false name := vt.Name + "-UPDATED" description := "" updateOpts := volumetypes.UpdateOpts{ Name: &name, Description: &description, IsPublic: &isPublic, } newVT, err := volumetypes.Update(client, vt.ID, updateOpts).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, newVT) th.AssertEquals(t, name, newVT.Name) th.AssertEquals(t, description, newVT.Description) th.AssertEquals(t, isPublic, newVT.IsPublic) } golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/client_test.go000066400000000000000000000100431367513235700300760ustar00rootroot00000000000000// +build acceptance package openstack import ( "os" "testing" "time" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack" "github.com/gophercloud/gophercloud/openstack/identity/v3/credentials" "github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/ec2tokens" "github.com/gophercloud/gophercloud/openstack/identity/v3/tokens" th "github.com/gophercloud/gophercloud/testhelper" ) 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 TestEC2AuthMethod(t *testing.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: ao.DomainName, DomainID: ao.DomainID, // We need a scope to get the token roles list Scope: tokens.Scope{ ProjectID: ao.TenantID, ProjectName: ao.TenantName, DomainID: ao.DomainID, DomainName: ao.DomainName, }, } token, err := tokens.Create(client, &authOptions).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, token) user, err := tokens.Get(client, token.ID).ExtractUser() th.AssertNoErr(t, err) tools.PrintResource(t, user) project, err := tokens.Get(client, token.ID).ExtractProject() th.AssertNoErr(t, err) tools.PrintResource(t, project) createOpts := credentials.CreateOpts{ ProjectID: project.ID, Type: "ec2", UserID: user.ID, Blob: "{\"access\":\"181920\",\"secret\":\"secretKey\"}", } // Create a credential credential, err := credentials.Create(client, createOpts).Extract() th.AssertNoErr(t, err) // Delete a credential defer credentials.Delete(client, credential.ID) tools.PrintResource(t, credential) newClient, err := clients.NewIdentityV3UnauthenticatedClient() th.AssertNoErr(t, err) var ec2AuthOptions tokens.AuthOptionsBuilder ec2AuthOptions = &ec2tokens.AuthOptions{ Access: "181920", Secret: "secretKey", } err = openstack.AuthenticateV3(newClient.ProviderClient, ec2AuthOptions, gophercloud.EndpointOpts{}) th.AssertNoErr(t, err) tools.PrintResource(t, newClient.TokenID) } 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.12.0/acceptance/openstack/clustering/000077500000000000000000000000001367513235700274135ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/clustering/v1/000077500000000000000000000000001367513235700277415ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/clustering/v1/actions_test.go000066400000000000000000000012741367513235700327730ustar00rootroot00000000000000// +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) } } golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/clustering/v1/clustering.go000066400000000000000000000303041367513235700324470ustar00rootroot00000000000000package 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 } // CreateWebhookReceiver will create a random webhook receiver. An error will be returned if the // receiver could not be created. func CreateWebhookReceiver(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 webhook 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 } // CreateMessageReceiver will create a message receiver with a random name. An error will be returned if the // receiver could not be created. func CreateMessageReceiver(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.MessageReceiver, } 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 message 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) res := nodes.Delete(client, id) if res.Err != nil { t.Fatalf("Error deleting node %s: %s:", id, res.Err) } actionID, err := GetActionID(res.Header) if err != nil { t.Fatalf("Error getting actionID %s: %s:", id, err) } err = WaitForAction(client, actionID) 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 }) } golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/clustering/v1/clusters_test.go000066400000000000000000000336461367513235700332070ustar00rootroot00000000000000// +build acceptance clustering policies package v1 import ( "sort" "strings" "testing" "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" 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) } func TestClustersAddNode(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) node1, err := CreateNode(t, client, "", profile.ID) th.AssertNoErr(t, err) // Even tho deleting the cluster will delete the nodes but only if added into cluster successfully. defer DeleteNode(t, client, node1.ID) node2, err := CreateNode(t, client, "", profile.ID) th.AssertNoErr(t, err) // Even tho deleting the cluster will delete the nodes but only if added into cluster successfully. defer DeleteNode(t, client, node2.ID) cluster, err = clusters.Get(client, cluster.ID).Extract() th.AssertNoErr(t, err) nodeIDs := []string{node1.ID, node2.ID} nodeIDs = append(nodeIDs, cluster.Nodes...) nodeNames := []string{node1.Name, node2.Name} addNodesOpts := clusters.AddNodesOpts{ Nodes: nodeNames, } actionID, err := clusters.AddNodes(client, cluster.ID, addNodesOpts).Extract() if err != nil { t.Fatalf("Unable to add nodes to cluster: %v", err) } err = WaitForAction(client, actionID) th.AssertNoErr(t, err) cluster, err = clusters.Get(client, cluster.ID).Extract() th.AssertNoErr(t, err) sort.Strings(nodeIDs) sort.Strings(cluster.Nodes) tools.PrintResource(t, nodeIDs) tools.PrintResource(t, cluster.Nodes) th.AssertDeepEquals(t, nodeIDs, cluster.Nodes) tools.PrintResource(t, cluster) } func TestClustersRemoveNodeFromCluster(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) cluster, err = clusters.Get(client, cluster.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, cluster) opt := clusters.RemoveNodesOpts{Nodes: cluster.Nodes} res := clusters.RemoveNodes(client, cluster.ID, opt) err = res.ExtractErr() th.AssertNoErr(t, err) for _, n := range cluster.Nodes { defer DeleteNode(t, client, n) } actionID, err := GetActionID(res.Header) th.AssertNoErr(t, err) err = WaitForAction(client, actionID) th.AssertNoErr(t, err) cluster, err = clusters.Get(client, cluster.ID).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, 0, len(cluster.Nodes)) tools.PrintResource(t, cluster) } func TestClustersReplaceNode(t *testing.T) { client, err := clients.NewClusteringV1Client() th.AssertNoErr(t, err) client.Microversion = "1.3" 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) node1, err := CreateNode(t, client, "", profile.ID) th.AssertNoErr(t, err) defer DeleteNode(t, client, node1.ID) cluster, err = clusters.Get(client, cluster.ID).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, true, len(cluster.Nodes) > 0) for _, n := range cluster.Nodes { defer DeleteNode(t, client, n) } nodeIDToBeReplaced := cluster.Nodes[0] opts := clusters.ReplaceNodesOpts{Nodes: map[string]string{nodeIDToBeReplaced: node1.ID}} actionID, err := clusters.ReplaceNodes(client, cluster.ID, opts).Extract() th.AssertNoErr(t, err) err = WaitForAction(client, actionID) th.AssertNoErr(t, err) cluster, err = clusters.Get(client, cluster.ID).Extract() th.AssertNoErr(t, err) clusterNodes := strings.Join(cluster.Nodes, ",") th.AssertEquals(t, true, strings.Contains(clusterNodes, node1.ID)) th.AssertEquals(t, false, strings.Contains(clusterNodes, nodeIDToBeReplaced)) tools.PrintResource(t, cluster) } func TestClustersCollectAttributes(t *testing.T) { client, err := clients.NewClusteringV1Client() th.AssertNoErr(t, err) client.Microversion = "1.2" 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) cluster, err = clusters.Get(client, cluster.ID).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, true, len(cluster.Nodes) > 0) _, err = CreateNode(t, client, cluster.ID, profile.ID) th.AssertNoErr(t, err) cluster, err = clusters.Get(client, cluster.ID).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, true, len(cluster.Nodes) > 0) for _, n := range cluster.Nodes { defer DeleteNode(t, client, n) } opts := clusters.CollectOpts{ Path: "status", } attrs, err := clusters.Collect(client, cluster.ID, opts).Extract() th.AssertNoErr(t, err) for _, attr := range attrs { th.AssertEquals(t, attr.Value, "ACTIVE") } opts = clusters.CollectOpts{ Path: "data.placement.zone", } attrs, err = clusters.Collect(client, cluster.ID, opts).Extract() th.AssertNoErr(t, err) for _, attr := range attrs { th.AssertEquals(t, attr.Value, "nova") } } // Performs an operation on a cluster func TestClustersOps(t *testing.T) { choices, err := clients.AcceptanceTestChoicesFromEnv() th.AssertNoErr(t, err) client, err := clients.NewClusteringV1Client() th.AssertNoErr(t, err) client.Microversion = "1.4" 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) ops := []clusters.OperationOpts{ // TODO: Commented out due to backend returns error, as of 2019-01-09 //{Operation: clusters.RebuildOperation}, // Error in set_admin_password in nova log //{Operation: clusters.EvacuateOperation, Params: clusters.OperationParams{"host": cluster.ID, "force": "True"}}, {Operation: clusters.RebootOperation, Params: clusters.OperationParams{"type": "SOFT"}}, {Operation: clusters.ChangePasswordOperation, Params: clusters.OperationParams{"admin_pass": "test"}}, {Operation: clusters.LockOperation}, {Operation: clusters.UnlockOperation}, {Operation: clusters.SuspendOperation}, {Operation: clusters.ResumeOperation}, {Operation: clusters.RescueOperation, Params: clusters.OperationParams{"image_ref": choices.ImageID}}, {Operation: clusters.PauseOperation}, {Operation: clusters.UnpauseOperation}, {Operation: clusters.StopOperation}, {Operation: clusters.StartOperation}, } for _, op := range ops { opName := string(op.Operation) t.Logf("Attempting to perform '%s' on cluster: %s", opName, cluster.ID) actionID, res := clusters.Ops(client, cluster.ID, op).Extract() th.AssertNoErr(t, res) err = WaitForAction(client, actionID) th.AssertNoErr(t, err) action, err := actions.Get(client, actionID).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, "SUCCEEDED", action.Status) t.Logf("Successfully performed '%s' on cluster: %s", opName, cluster.ID) } cluster, err = clusters.Get(client, cluster.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, cluster) } golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/clustering/v1/events_test.go000066400000000000000000000012611367513235700326330ustar00rootroot00000000000000// +build acceptance clustering events package v1 import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/clustering/v1/events" th "github.com/gophercloud/gophercloud/testhelper" ) func TestEventsList(t *testing.T) { client, err := clients.NewClusteringV1Client() th.AssertNoErr(t, err) opts := events.ListOpts{ Limit: 200, } allPages, err := events.List(client, opts).AllPages() th.AssertNoErr(t, err) allEvents, err := events.ExtractEvents(allPages) th.AssertNoErr(t, err) for _, event := range allEvents { tools.PrintResource(t, event) } } golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/clustering/v1/nodes_test.go000066400000000000000000000150031367513235700324360ustar00rootroot00000000000000// +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) } // Performs an operation on a node func TestNodesOps(t *testing.T) { choices, err := clients.AcceptanceTestChoicesFromEnv() th.AssertNoErr(t, err) client, err := clients.NewClusteringV1Client() th.AssertNoErr(t, err) client.Microversion = "1.4" 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) ops := []nodes.OperationOpts{ // TODO: Commented out due to backend returns error, as of 2018-12-14 //{Operation: nodes.RebuildOperation}, //{Operation: nodes.EvacuateOperation, Params: nodes.OperationParams{"EvacuateHost": node.ID, "EvacuateForce", "True"}}, {Operation: nodes.RebootOperation, Params: nodes.OperationParams{"type": "SOFT"}}, {Operation: nodes.ChangePasswordOperation, Params: nodes.OperationParams{"admin_pass": "test"}}, {Operation: nodes.LockOperation}, {Operation: nodes.UnlockOperation}, {Operation: nodes.SuspendOperation}, {Operation: nodes.ResumeOperation}, {Operation: nodes.RescueOperation, Params: nodes.OperationParams{"image_ref": choices.ImageID}}, {Operation: nodes.PauseOperation}, {Operation: nodes.UnpauseOperation}, {Operation: nodes.StopOperation}, {Operation: nodes.StartOperation}, } for _, op := range ops { opName := string(op.Operation) t.Logf("Attempting to perform '%s' on node: %s", opName, node.ID) actionID, res := nodes.Ops(client, node.ID, op).Extract() th.AssertNoErr(t, res) err = WaitForAction(client, actionID) th.AssertNoErr(t, err) node, err = nodes.Get(client, node.ID).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, "Operation '"+opName+"' succeeded", node.StatusReason) t.Logf("Successfully performed '%s' on node: %s", opName, node.ID) } } func TestNodesRecover(t *testing.T) { client, err := clients.NewClusteringV1Client() th.AssertNoErr(t, err) client.Microversion = "1.6" 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) checkTrue := true checkFalse := false // TODO: nodes.RebuildRecovery is commented out as of 12/14/2018 the API backend can't perform the action without returning error ops := []nodes.RecoverOpts{ // Microversion < 1.6 legacy support where argument DOES NOT support Check nodes.RecoverOpts{}, nodes.RecoverOpts{Operation: nodes.RebootRecovery}, // nodes.RecoverOpts{Operation: nodes.RebuildRecovery}, // MicroVersion >= 1.6 that supports Check where Check is true nodes.RecoverOpts{Check: &checkTrue}, nodes.RecoverOpts{Operation: nodes.RebootRecovery, Check: &checkTrue}, //nodes.RecoverOpts{Operation: nodes.RebuildRecovery, Check: &checkTrue}, // MicroVersion >= 1.6 that supports Check where Check is false nodes.RecoverOpts{Check: &checkFalse}, nodes.RecoverOpts{Operation: nodes.RebootRecovery, Check: &checkFalse}, //nodes.RecoverOpts{Operation: nodes.RebuildRecovery, Check: &checkFalse}, } for _, recoverOpt := range ops { if recoverOpt.Check != nil { t.Logf("Attempting to recover by using '%s' check=%t on node: %s", recoverOpt.Operation, *recoverOpt.Check, node.ID) } else { t.Logf("Attempting to recover by using '%s' on node: %s", recoverOpt.Operation, node.ID) } actionID, err := nodes.Recover(client, node.ID, recoverOpt).Extract() th.AssertNoErr(t, err) err = WaitForAction(client, actionID) th.AssertNoErr(t, err) if recoverOpt.Check != nil { t.Logf("Successfully recovered by using '%s' check=%t on node: %s", recoverOpt.Operation, *recoverOpt.Check, node.ID) } else { t.Logf("Successfully recovered by using '%s' on node: %s", recoverOpt.Operation, node.ID) } node, err := nodes.Get(client, node.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, node) } } func TestNodeCheck(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) t.Logf("Attempting to check on node: %s", node.ID) actionID, err := nodes.Check(client, node.ID).Extract() th.AssertNoErr(t, err) err = WaitForAction(client, actionID) th.AssertNoErr(t, err) node, err = nodes.Get(client, node.ID).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, "Check: Node is ACTIVE.", node.StatusReason) tools.PrintResource(t, node) } golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/clustering/v1/pkg.go000066400000000000000000000001441367513235700310500ustar00rootroot00000000000000// Package v1 package contains acceptance tests for the Openstack Clustering V1 service. package v1 golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/clustering/v1/policies_test.go000066400000000000000000000033771367513235700331500ustar00rootroot00000000000000// +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) } golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/clustering/v1/policytypes_test.go000066400000000000000000000031061367513235700337130ustar00rootroot00000000000000// +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) } golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/clustering/v1/profiles_test.go000066400000000000000000000040441367513235700331540ustar00rootroot00000000000000// +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) } func TestProfileValidate(t *testing.T) { client, err := clients.NewClusteringV1Client() th.AssertNoErr(t, err) client.Microversion = "1.2" profile, err := CreateProfile(t, client) th.AssertNoErr(t, err) defer DeleteProfile(t, client, profile.ID) opts := profiles.ValidateOpts{ Spec: profile.Spec, } validatedProfile, err := profiles.Validate(client, opts).Extract() th.AssertNoErr(t, err) // Do not validate the following fields for AssertDeepEquals() because the actual fields are either missing or hardcoded. profile.CreatedAt = validatedProfile.CreatedAt profile.Domain = validatedProfile.Domain profile.ID = validatedProfile.ID profile.Metadata = validatedProfile.Metadata profile.Name = "validated_profile" profile.UpdatedAt = validatedProfile.UpdatedAt th.AssertDeepEquals(t, validatedProfile, profile) tools.PrintResource(t, validatedProfile) } golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/clustering/v1/profiletypes_test.go000066400000000000000000000022301367513235700340510ustar00rootroot00000000000000// +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) } } golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/clustering/v1/receivers_test.go000066400000000000000000000042571367513235700333260ustar00rootroot00000000000000// +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 := CreateWebhookReceiver(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) } func TestReceiversNotify(t *testing.T) { t.Parallel() 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 := CreateMessageReceiver(t, client, cluster.ID) th.AssertNoErr(t, err) defer DeleteReceiver(t, client, receiver.ID) t.Logf("Created Mesage Receiver Name:[%s] Message Receiver ID:[%s]", receiver.Name, receiver.ID) requestID, err := receivers.Notify(client, receiver.ID).Extract() th.AssertNoErr(t, err) t.Logf("Receiver Notify Service Request ID: %s", requestID) } webhooktrigger_test.go000066400000000000000000000032671367513235700343020ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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/nodes" "github.com/gophercloud/gophercloud/openstack/clustering/v1/webhooks" 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 := CreateWebhookReceiver(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.12.0/acceptance/openstack/common.go000066400000000000000000000011521367513235700270520ustar00rootroot00000000000000// 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.12.0/acceptance/openstack/compute/000077500000000000000000000000001367513235700267105ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/compute/v2/000077500000000000000000000000001367513235700272375ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/compute/v2/aggregates_test.go000066400000000000000000000076011367513235700327420ustar00rootroot00000000000000// +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.go000066400000000000000000000023661367513235700340650ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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.go000066400000000000000000000025511367513235700343020ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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) } golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/compute/v2/bootfromvolume_test.go000066400000000000000000000205301367513235700337040ustar00rootroot00000000000000// +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) } golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/compute/v2/compute.go000066400000000000000000001162501367513235700312470ustar00rootroot00000000000000// 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/aggregates" "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/remoteconsoles" "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" neutron "github.com/gophercloud/gophercloud/openstack/networking/v2/networks" th "github.com/gophercloud/gophercloud/testhelper" "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 := GetNetworkIDFromNetworks(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 := GetNetworkIDFromNetworks(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 := GetNetworkIDFromNetworks(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() if err != nil { return server, 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 } // 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 := GetNetworkIDFromNetworks(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 } // CreateMicroversionServer creates a basic instance compatible with // newer microversions 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 CreateMicroversionServer(t *testing.T, client *gophercloud.ServiceClient) (*servers.Server, error) { choices, err := clients.AcceptanceTestChoicesFromEnv() if err != nil { t.Fatal(err) } networkID, err := GetNetworkIDFromNetworks(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", }, }).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.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 := GetNetworkIDFromNetworks(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 } // CreateServerWithTags 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. // Two tags will be assigned to the server. // An error will be returned if the instance was unable to be created. func CreateServerWithTags(t *testing.T, client *gophercloud.ServiceClient, networkID string) (*servers.Server, error) { choices, err := clients.AcceptanceTestChoicesFromEnv() if err != nil { t.Fatal(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"), }, }, Tags: []string{"tag1", "tag2"}, }).Extract() if err != nil { return server, err } if err := WaitForComputeStatus(client, server, "ACTIVE"); err != nil { return nil, err } res := servers.Get(client, server.ID) if res.Err != nil { return nil, res.Err } newServer, err := res.Extract() th.AssertNoErr(t, err) th.AssertEquals(t, newServer.Name, name) th.AssertDeepEquals(t, *newServer.Tags, []string{"tag1", "tag2"}) return newServer, 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 } // CreateServerGroupMicroversion will create a server with a random name using 2.64 microversion. An error will be // returned if the server group failed to be created. func CreateServerGroupMicroversion(t *testing.T, client *gophercloud.ServiceClient) (*servergroups.ServerGroup, error) { name := tools.RandomString("ACPTTEST", 16) policy := "anti-affinity" maxServerPerHost := 3 t.Logf("Attempting to create %s server group with max server per host = %d: %s", policy, maxServerPerHost, name) sg, err := servergroups.Create(client, &servergroups.CreateOpts{ Name: name, Policy: policy, Rules: &servergroups.Rules{ MaxServerPerHost: maxServerPerHost, }, }).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 := GetNetworkIDFromNetworks(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 := GetNetworkIDFromNetworks(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) } // GetNetworkIDFromOSNetworks 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 GetNetworkIDFromOSNetworks(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) } // GetNetworkIDFromNetworks will return the network UUID for a given network // name using either the os-tenant-networks API extension or Neutron API. // 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 := tenantnetworks.List(client).AllPages() if err == nil { allTenantNetworks, err := tenantnetworks.ExtractNetworks(allPages) if err != nil { return "", err } for _, network := range allTenantNetworks { if network.Name == networkName { return network.ID, nil } } } networkClient, err := clients.NewNetworkV2Client() th.AssertNoErr(t, err) allPages2, err := neutron.List(networkClient, nil).AllPages() th.AssertNoErr(t, err) allNetworks, err := neutron.ExtractNetworks(allPages2) th.AssertNoErr(t, err) for _, network := range allNetworks { 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 } // CreateRemoteConsole will create a remote noVNC console for the specified server. func CreateRemoteConsole(t *testing.T, client *gophercloud.ServiceClient, serverID string) (*remoteconsoles.RemoteConsole, error) { createOpts := remoteconsoles.CreateOpts{ Protocol: remoteconsoles.ConsoleProtocolVNC, Type: remoteconsoles.ConsoleTypeNoVNC, } t.Logf("Attempting to create a %s console for the server %s", createOpts.Type, serverID) remoteConsole, err := remoteconsoles.Create(client, serverID, createOpts).Extract() if err != nil { return nil, err } t.Logf("Successfully created console: %s", remoteConsole.URL) return remoteConsole, nil } // CreateNoNetworkServer 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 without network interfaces attached. // An error will be returned if the instance was unable to be created. func CreateServerNoNetwork(t *testing.T, client *gophercloud.ServiceClient) (*servers.Server, error) { choices, err := clients.AcceptanceTestChoicesFromEnv() if err != nil { t.Fatal(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: "none", 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 } golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/compute/v2/defsecrules_test.go000066400000000000000000000027341367513235700331370ustar00rootroot00000000000000// +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) } golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/compute/v2/diagnostics_test.go000066400000000000000000000013301367513235700331310ustar00rootroot00000000000000// +build acceptance compute limits package v2 import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/diagnostics" th "github.com/gophercloud/gophercloud/testhelper" ) func TestDiagnostics(t *testing.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) diag, err := diagnostics.Get(client, server.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, diag) _, ok := diag["memory"] th.AssertEquals(t, true, ok) } golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/compute/v2/extension_test.go000066400000000000000000000020641367513235700326430ustar00rootroot00000000000000// +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") } golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/compute/v2/flavors_test.go000066400000000000000000000124601367513235700323040ustar00rootroot00000000000000// +build acceptance compute flavors package v2 import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" identity "github.com/gophercloud/gophercloud/acceptance/openstack/identity/v3" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/compute/v2/flavors" th "github.com/gophercloud/gophercloud/testhelper" ) 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") } golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/compute/v2/floatingip_test.go000066400000000000000000000064651367513235700327740ustar00rootroot00000000000000// +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) } golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/compute/v2/hypervisors_test.go000066400000000000000000000043641367513235700332310ustar00rootroot00000000000000// +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) (string, 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 "", fmt.Errorf("Unable to get hypervisor ID") } golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/compute/v2/images_test.go000066400000000000000000000022361367513235700320750ustar00rootroot00000000000000// +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) } instance_actions_test.go000066400000000000000000000053201367513235700340720ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/compute/v2// +build acceptance compute limits package v2 import ( "testing" "time" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/instanceactions" "github.com/gophercloud/gophercloud/openstack/compute/v2/servers" th "github.com/gophercloud/gophercloud/testhelper" ) func TestInstanceActions(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) allPages, err := instanceactions.List(client, server.ID, nil).AllPages() th.AssertNoErr(t, err) allActions, err := instanceactions.ExtractInstanceActions(allPages) th.AssertNoErr(t, err) var found bool for _, action := range allActions { action, err := instanceactions.Get(client, server.ID, action.RequestID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, action) if action.Action == "create" { found = true } } th.AssertEquals(t, found, true) } func TestInstanceActionsMicroversions(t *testing.T) { clients.RequireLong(t) clients.SkipRelease(t, "stable/mitaka") clients.SkipRelease(t, "stable/newton") clients.SkipRelease(t, "stable/ocata") clients.SkipRelease(t, "stable/pike") clients.SkipRelease(t, "stable/queens") clients.SkipRelease(t, "stable/rocky") now := time.Now() client, err := clients.NewComputeV2Client() th.AssertNoErr(t, err) client.Microversion = "2.66" server, err := CreateMicroversionServer(t, client) th.AssertNoErr(t, err) defer DeleteServer(t, client, server) rebootOpts := servers.RebootOpts{ Type: servers.HardReboot, } err = servers.Reboot(client, server.ID, rebootOpts).ExtractErr() if err = WaitForComputeStatus(client, server, "ACTIVE"); err != nil { t.Fatal(err) } listOpts := instanceactions.ListOpts{ Limit: 1, ChangesSince: &now, } allPages, err := instanceactions.List(client, server.ID, listOpts).AllPages() th.AssertNoErr(t, err) allActions, err := instanceactions.ExtractInstanceActions(allPages) th.AssertNoErr(t, err) var found bool for _, action := range allActions { action, err := instanceactions.Get(client, server.ID, action.RequestID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, action) if action.Action == "reboot" { found = true } } th.AssertEquals(t, found, true) listOpts = instanceactions.ListOpts{ Limit: 1, ChangesBefore: &now, } allPages, err = instanceactions.List(client, server.ID, listOpts).AllPages() th.AssertNoErr(t, err) allActions, err = instanceactions.ExtractInstanceActions(allPages) th.AssertNoErr(t, err) th.AssertEquals(t, len(allActions), 0) } golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/compute/v2/keypairs_test.go000066400000000000000000000047701367513235700324640ustar00rootroot00000000000000// +build acceptance compute keypairs package v2 import ( "testing" "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" "golang.org/x/crypto/ssh" ) 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) } golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/compute/v2/limits_test.go000066400000000000000000000024261367513235700321320ustar00rootroot00000000000000// +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) } golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/compute/v2/migrate_test.go000066400000000000000000000024661367513235700322650ustar00rootroot00000000000000// +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) } golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/compute/v2/network_test.go000066400000000000000000000024541367513235700323230ustar00rootroot00000000000000// +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 := GetNetworkIDFromOSNetworks(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) } golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/compute/v2/pkg.go000066400000000000000000000001411367513235700303430ustar00rootroot00000000000000// Package v2 package contains acceptance tests for the Openstack Compute V2 service. package v2 golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/compute/v2/quotaset_test.go000066400000000000000000000114201367513235700324700ustar00rootroot00000000000000// +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") clients.SkipRelease(t, "stable/mitaka") clients.SkipRelease(t, "stable/newton") clients.SkipRelease(t, "stable/ocata") clients.SkipRelease(t, "stable/pike") clients.SkipRelease(t, "stable/queens") clients.SkipRelease(t, "stable/rocky") clients.SkipRelease(t, "stable/stein") 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.SkipRelease(t, "stable/mitaka") clients.SkipRelease(t, "stable/newton") clients.SkipRelease(t, "stable/ocata") clients.SkipRelease(t, "stable/pike") clients.SkipRelease(t, "stable/queens") clients.SkipRelease(t, "stable/rocky") clients.SkipRelease(t, "stable/stein") 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) } golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/compute/v2/remoteconsoles_test.go000066400000000000000000000012121367513235700336620ustar00rootroot00000000000000// +build acceptance compute remoteconsoles package v2 import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" th "github.com/gophercloud/gophercloud/testhelper" ) func TestRemoteConsoleCreate(t *testing.T) { clients.RequireLong(t) client, err := clients.NewComputeV2Client() th.AssertNoErr(t, err) client.Microversion = "2.6" server, err := CreateServer(t, client) th.AssertNoErr(t, err) defer DeleteServer(t, client, server) remoteConsole, err := CreateRemoteConsole(t, client, server.ID) th.AssertNoErr(t, err) tools.PrintResource(t, remoteConsole) } golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/compute/v2/rescueunrescue_test.go000066400000000000000000000010351367513235700336640ustar00rootroot00000000000000// +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) } golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/compute/v2/secgroup_test.go000066400000000000000000000072631367513235700324640ustar00rootroot00000000000000// +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) description := "" updateOpts := secgroups.UpdateOpts{ Name: newName, Description: &description, } 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) th.AssertEquals(t, updatedSecurityGroup.Description, description) } 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) } golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/compute/v2/servergroup_test.go000066400000000000000000000056051367513235700332160ustar00rootroot00000000000000// +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) } func TestServergroupsMicroversionCreateDelete(t *testing.T) { clients.SkipRelease(t, "stable/mitaka") clients.SkipRelease(t, "stable/newton") clients.SkipRelease(t, "stable/ocata") clients.SkipRelease(t, "stable/pike") clients.SkipRelease(t, "stable/queens") client, err := clients.NewComputeV2Client() th.AssertNoErr(t, err) client.Microversion = "2.64" serverGroup, err := CreateServerGroupMicroversion(t, client) 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) } golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/compute/v2/servers_test.go000066400000000000000000000440221367513235700323200ustar00rootroot00000000000000// +build acceptance compute servers package v2 import ( "strings" "testing" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/acceptance/clients" networks "github.com/gophercloud/gophercloud/acceptance/openstack/networking/v2" "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/extendedserverattributes" "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/extensions/tags" "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.SkipRelease(t, "stable/mitaka") 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(string(err400.Body), "Missing imageRef attribute") { 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), ImageRef: 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) { 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) outputOpts := &servers.ShowConsoleOutputOpts{ Length: 4, } output, err := servers.ShowConsoleOutput(client, server.ID, outputOpts).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, output) } func TestServersTags(t *testing.T) { clients.RequireLong(t) clients.SkipRelease(t, "stable/mitaka") clients.SkipRelease(t, "stable/newton") clients.SkipRelease(t, "stable/ocata") choices, err := clients.AcceptanceTestChoicesFromEnv() th.AssertNoErr(t, err) client, err := clients.NewComputeV2Client() th.AssertNoErr(t, err) client.Microversion = "2.52" networkClient, err := clients.NewNetworkV2Client() th.AssertNoErr(t, err) networkID, err := networks.IDFromName(networkClient, choices.NetworkName) th.AssertNoErr(t, err) // Create server with tags. server, err := CreateServerWithTags(t, client, networkID) th.AssertNoErr(t, err) defer DeleteServer(t, client, server) // All the following calls should work with "2.26" microversion. client.Microversion = "2.26" // Check server tags in body. serverWithTags, err := servers.Get(client, server.ID).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, []string{"tag1", "tag2"}, *serverWithTags.Tags) // Check all tags. allTags, err := tags.List(client, server.ID).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, []string{"tag1", "tag2"}, allTags) // Check single tag. exists, err := tags.Check(client, server.ID, "tag2").Extract() th.AssertNoErr(t, err) th.AssertEquals(t, true, exists) // Add new tag. newTags, err := tags.ReplaceAll(client, server.ID, tags.ReplaceAllOpts{Tags: []string{"tag3", "tag4"}}).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, []string{"tag3", "tag4"}, newTags) // Add new single tag. err = tags.Add(client, server.ID, "tag5").ExtractErr() th.AssertNoErr(t, err) // Check current tags. newAllTags, err := tags.List(client, server.ID).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, []string{"tag3", "tag4", "tag5"}, newAllTags) // Remove single tag. err = tags.Delete(client, server.ID, "tag4").ExtractErr() th.AssertNoErr(t, err) // Check that tag doesn't exist anymore. exists, err = tags.Check(client, server.ID, "tag4").Extract() th.AssertNoErr(t, err) th.AssertEquals(t, false, exists) // Remove all tags. err = tags.DeleteAll(client, server.ID).ExtractErr() th.AssertNoErr(t, err) // Check that there are no more tags. currentTags, err := tags.List(client, server.ID).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, 0, len(currentTags)) } func TestServersWithExtendedAttributesCreateDestroy(t *testing.T) { clients.RequireLong(t) clients.RequireAdmin(t) clients.SkipRelease(t, "stable/mitaka") clients.SkipRelease(t, "stable/newton") clients.SkipRelease(t, "stable/ocata") client, err := clients.NewComputeV2Client() th.AssertNoErr(t, err) client.Microversion = "2.3" server, err := CreateServer(t, client) th.AssertNoErr(t, err) defer DeleteServer(t, client, server) type serverAttributesExt struct { servers.Server extendedserverattributes.ServerAttributesExt } var serverWithAttributesExt serverAttributesExt err = servers.Get(client, server.ID).ExtractInto(&serverWithAttributesExt) th.AssertNoErr(t, err) t.Logf("Server With Extended Attributes: %#v", serverWithAttributesExt) th.AssertEquals(t, *serverWithAttributesExt.ReservationID != "", true) th.AssertEquals(t, *serverWithAttributesExt.LaunchIndex, 0) th.AssertEquals(t, *serverWithAttributesExt.RAMDiskID == "", true) th.AssertEquals(t, *serverWithAttributesExt.KernelID == "", true) th.AssertEquals(t, *serverWithAttributesExt.Hostname != "", true) th.AssertEquals(t, *serverWithAttributesExt.RootDeviceName != "", true) th.AssertEquals(t, serverWithAttributesExt.Userdata == nil, true) } func TestServerNoNetworkCreateDestroy(t *testing.T) { clients.RequireLong(t) client, err := clients.NewComputeV2Client() th.AssertNoErr(t, err) choices, err := clients.AcceptanceTestChoicesFromEnv() th.AssertNoErr(t, err) client.Microversion = "2.37" server, err := CreateServerNoNetwork(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) } _, err = servers.ListAddressesByNetwork(client, server.ID, choices.NetworkName).AllPages() if err == nil { t.Fatalf("Instance must not be a member of specified network") } } golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/compute/v2/services_test.go000066400000000000000000000054231367513235700324540ustar00rootroot00000000000000// +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, nil).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) } func TestServicesListWithOpts(t *testing.T) { clients.RequireAdmin(t) client, err := clients.NewComputeV2Client() th.AssertNoErr(t, err) opts := services.ListOpts{ Binary: "nova-scheduler", } allPages, err := services.List(client, opts).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) th.AssertEquals(t, service.Binary, "nova-scheduler") if service.Binary == "nova-scheduler" { found = true } } th.AssertEquals(t, found, true) } func TestServicesUpdate(t *testing.T) { clients.RequireAdmin(t) client, err := clients.NewComputeV2Client() th.AssertNoErr(t, err) listOpts := services.ListOpts{ Binary: "nova-compute", } client.Microversion = "2.53" allPages, err := services.List(client, listOpts).AllPages() th.AssertNoErr(t, err) allServices, err := services.ExtractServices(allPages) th.AssertNoErr(t, err) // disable all services for _, service := range allServices { opts := services.UpdateOpts{ Status: services.ServiceDisabled, } updated, err := services.Update(client, service.ID, opts).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, updated.ID, service.ID) } // verify all services are disabled allPages, err = services.List(client, listOpts).AllPages() th.AssertNoErr(t, err) allServices, err = services.ExtractServices(allPages) th.AssertNoErr(t, err) for _, service := range allServices { th.AssertEquals(t, service.Status, "disabled") } // reenable all services allPages, err = services.List(client, listOpts).AllPages() th.AssertNoErr(t, err) allServices, err = services.ExtractServices(allPages) th.AssertNoErr(t, err) for _, service := range allServices { opts := services.UpdateOpts{ Status: services.ServiceEnabled, } updated, err := services.Update(client, service.ID, opts).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, updated.ID, service.ID) } } golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/compute/v2/tenantnetworks_test.go000066400000000000000000000024461367513235700337210ustar00rootroot00000000000000// +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) } golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/compute/v2/usage_test.go000066400000000000000000000040161367513235700317320ustar00rootroot00000000000000// +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) } golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/compute/v2/volumeattach_test.go000066400000000000000000000020511367513235700333170ustar00rootroot00000000000000// +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.12.0/acceptance/openstack/container/000077500000000000000000000000001367513235700272165ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/container/v1/000077500000000000000000000000001367513235700275445ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/container/v1/capsules.go000066400000000000000000000020501367513235700317070ustar00rootroot00000000000000package 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, uuid, status string) error { return tools.WaitFor(func() (bool, error) { v, err := capsules.Get(client, uuid).Extract() if err != nil { return false, err } var newStatus string if capsule, ok := v.(*capsules.Capsule); ok { newStatus = capsule.Status } if capsule, ok := v.(*capsules.CapsuleV132); ok { newStatus = capsule.Status } fmt.Println(status) fmt.Println(newStatus) if newStatus == status { // Success! return true, nil } if newStatus == "Failed" { return false, fmt.Errorf("Capsule in FAILED state") } if newStatus == "Error" { return false, fmt.Errorf("Capsule in ERROR state") } return false, nil }) } golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/container/v1/capsules_test.go000066400000000000000000000056501367513235700327570ustar00rootroot00000000000000// +build acceptance containers capsules package 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 TestCapsuleBase(t *testing.T) { t.Skip("Currently failing in OpenLab") clients.SkipRelease(t, "stable/mitaka") clients.SkipRelease(t, "stable/newton") clients.SkipRelease(t, "stable/ocata") clients.SkipRelease(t, "stable/pike") clients.SkipRelease(t, "stable/queens") client, err := clients.NewContainerV1Client() th.AssertNoErr(t, err) template := new(capsules.Template) template.Bin = []byte(capsuleTemplate) createOpts := capsules.CreateOpts{ TemplateOpts: template, } v, err := capsules.Create(client, createOpts).Extract() th.AssertNoErr(t, err) capsule := v.(*capsules.Capsule) err = WaitForCapsuleStatus(client, capsule.UUID, "Running") th.AssertNoErr(t, err) pager := capsules.List(client, nil) err = pager.EachPage(func(page pagination.Page) (bool, error) { v, err := capsules.ExtractCapsules(page) th.AssertNoErr(t, err) allCapsules := v.([]capsules.Capsule) for _, m := range allCapsules { capsuleUUID := m.UUID if capsuleUUID != capsule.UUID { continue } capsule, err := capsules.Get(client, capsuleUUID).ExtractBase() 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) } func TestCapsuleV132(t *testing.T) { t.Skip("Currently failing in OpenLab") clients.SkipRelease(t, "stable/mitaka") clients.SkipRelease(t, "stable/newton") clients.SkipRelease(t, "stable/ocata") clients.SkipRelease(t, "stable/pike") clients.SkipRelease(t, "stable/queens") clients.SkipRelease(t, "stable/rocky") clients.SkipRelease(t, "stable/stein") client, err := clients.NewContainerV1Client() th.AssertNoErr(t, err) client.Microversion = "1.32" template := new(capsules.Template) template.Bin = []byte(capsuleTemplate) createOpts := capsules.CreateOpts{ TemplateOpts: template, } capsule, err := capsules.Create(client, createOpts).ExtractV132() th.AssertNoErr(t, err) err = WaitForCapsuleStatus(client, capsule.UUID, "Running") th.AssertNoErr(t, err) pager := capsules.List(client, nil) err = pager.EachPage(func(page pagination.Page) (bool, error) { allCapsules, err := capsules.ExtractCapsulesV132(page) th.AssertNoErr(t, err) for _, m := range allCapsules { capsuleUUID := m.UUID if capsuleUUID != capsule.UUID { continue } capsule, err := capsules.Get(client, capsuleUUID).ExtractV132() 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.12.0/acceptance/openstack/container/v1/fixtures.go000066400000000000000000000012761367513235700317520ustar00rootroot00000000000000package v1 const capsuleTemplate = ` { "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", "ports": [ { "containerPort": 80, "hostPort": 80, "name": "nginx-port", "protocol": "TCP" } ], "resources": { "requests": { "cpu": 1, "memory": 1024 } }, "workDir": "/root" } ] } } ` golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/containerinfra/000077500000000000000000000000001367513235700302365ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/containerinfra/v1/000077500000000000000000000000001367513235700305645ustar00rootroot00000000000000certificates_test.go000066400000000000000000000031411367513235700345370ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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) } golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/containerinfra/v1/clusters_test.go000066400000000000000000000041221367513235700340150ustar00rootroot00000000000000// +build acceptance containerinfra package v1 import ( "testing" "time" "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) th.AssertNoErr(t, err) 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", time.Second*300) th.AssertNoErr(t, err) newCluster, err := clusters.Get(client, clusterID).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, newCluster.UUID, clusterID) allPagesDetail, err := clusters.ListDetail(client, nil).AllPages() th.AssertNoErr(t, err) allClustersDetail, err := clusters.ExtractClusters(allPagesDetail) th.AssertNoErr(t, err) var foundDetail bool for _, v := range allClustersDetail { if v.UUID == clusterID { foundDetail = true } } th.AssertEquals(t, foundDetail, true) tools.PrintResource(t, newCluster) } clustertemplates_test.go000066400000000000000000000036641367513235700355040ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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.go000066400000000000000000000203551367513235700340430ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/containerinfra/v1package v1 import ( "fmt" "math" "strings" "testing" "time" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/acceptance/clients" idv3 "github.com/gophercloud/gophercloud/acceptance/openstack/identity/v3" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/containerinfra/v1/clusters" "github.com/gophercloud/gophercloud/openstack/containerinfra/v1/clustertemplates" "github.com/gophercloud/gophercloud/openstack/containerinfra/v1/quotas" th "github.com/gophercloud/gophercloud/testhelper" ) // CreateClusterTemplateCOE will create a random cluster template for the specified orchestration engine. // An error will be returned if the cluster template could not be created. func CreateClusterTemplateCOE(t *testing.T, client *gophercloud.ServiceClient, coe string) (*clustertemplates.ClusterTemplate, error) { choices, err := clients.AcceptanceTestChoicesFromEnv() if err != nil { return nil, err } name := tools.RandomString("TESTACC-", 8) t.Logf("Attempting to create %s cluster template: %s", coe, name) boolFalse := false createOpts := clustertemplates.CreateOpts{ COE: coe, DNSNameServer: "8.8.8.8", DockerStorageDriver: "overlay2", 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 } // CreateClusterTemplate will create a random swarm cluster template. // An error will be returned if the cluster template could not be created. func CreateClusterTemplate(t *testing.T, client *gophercloud.ServiceClient) (*clustertemplates.ClusterTemplate, error) { return CreateClusterTemplateCOE(t, client, "swarm") } // CreateKubernetesClusterTemplate will create a random kubernetes cluster template. // An error will be returned if the cluster template could not be created. func CreateKubernetesClusterTemplate(t *testing.T, client *gophercloud.ServiceClient) (*clustertemplates.ClusterTemplate, error) { return CreateClusterTemplateCOE(t, client, "kubernetes") } // 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 } // CreateClusterTimeout will create a random cluster and wait for it to reach CREATE_COMPLETE status // within the given timeout duration. An error will be returned if the cluster could not be created. func CreateClusterTimeout(t *testing.T, client *gophercloud.ServiceClient, clusterTemplateID string, timeout time.Duration) (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 is the creation timeout on the magnum side in minutes createTimeout := int(math.Ceil(timeout.Minutes())) 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", timeout) if err != nil { return clusterID, err } t.Logf("Successfully created cluster: %s id: %s", clusterName, clusterID) return clusterID, nil } // CreateCluster will create a random cluster. An error will be returned if the // cluster could not be created. Has a timeout of 300 seconds. func CreateCluster(t *testing.T, client *gophercloud.ServiceClient, clusterTemplateID string) (string, error) { return CreateClusterTimeout(t, client, clusterTemplateID, 300*time.Second) } // CreateKubernetesCluster is the same as CreateCluster with a longer timeout necessary for creating a kubernetes cluster func CreateKubernetesCluster(t *testing.T, client *gophercloud.ServiceClient, clusterTemplateID string) (string, error) { return CreateClusterTimeout(t, client, clusterTemplateID, 900*time.Second) } 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", 300*time.Second) 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, timeout time.Duration) error { return tools.WaitForTimeout(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 }, timeout) } // CreateQuota will create a random quota. An error will be returned if the // quota could not be created. func CreateQuota(t *testing.T, client *gophercloud.ServiceClient) (*quotas.Quotas, error) { name := tools.RandomString("TESTACC-", 8) t.Logf("Attempting to create quota: %s", name) idClient, err := clients.NewIdentityV3Client() th.AssertNoErr(t, err) project, err := idv3.CreateProject(t, idClient, nil) th.AssertNoErr(t, err) defer idv3.DeleteProject(t, idClient, project.ID) createOpts := quotas.CreateOpts{ Resource: "Cluster", ProjectID: project.ID, HardLimit: 10, } res := quotas.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("Quota %s request ID: %s", name, requestID) quota, err := res.Extract() if err == nil { t.Logf("Successfully created quota: %s", quota.ProjectID) tools.PrintResource(t, quota) th.AssertEquals(t, project.ID, quota.ProjectID) th.AssertEquals(t, "Cluster", quota.Resource) th.AssertEquals(t, 10, quota.HardLimit) } return quota, err } nodegroups_test.go000066400000000000000000000123751367513235700342700ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/containerinfra/v1// +build acceptance containerinfra package v1 import ( "fmt" "testing" "time" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/containerinfra/v1/nodegroups" th "github.com/gophercloud/gophercloud/testhelper" ) func TestNodeGroupsCRUD(t *testing.T) { // API not available until Magnum train clients.SkipRelease(t, "stable/mitaka") clients.SkipRelease(t, "stable/newton") clients.SkipRelease(t, "stable/ocata") clients.SkipRelease(t, "stable/pike") clients.SkipRelease(t, "stable/queens") clients.SkipRelease(t, "stable/rocky") clients.SkipRelease(t, "stable/stein") client, err := clients.NewContainerInfraV1Client() th.AssertNoErr(t, err) client.Microversion = "1.9" clusterTemplate, err := CreateKubernetesClusterTemplate(t, client) th.AssertNoErr(t, err) defer DeleteClusterTemplate(t, client, clusterTemplate.UUID) clusterID, err := CreateKubernetesCluster(t, client, clusterTemplate.UUID) th.AssertNoErr(t, err) defer DeleteCluster(t, client, clusterID) var nodeGroupID string t.Run("list", func(t *testing.T) { testNodeGroupsList(t, client, clusterID) }) t.Run("listone-get", func(t *testing.T) { testNodeGroupGet(t, client, clusterID) }) t.Run("create", func(t *testing.T) { nodeGroupID = testNodeGroupCreate(t, client, clusterID) }) t.Logf("Created nodegroup: %s", nodeGroupID) // Wait for the node group to finish creating err = tools.WaitForTimeout(func() (bool, error) { ng, err := nodegroups.Get(client, clusterID, nodeGroupID).Extract() if err != nil { return false, fmt.Errorf("error waiting for node group to create: %v", err) } return (ng.Status == "CREATE_COMPLETE"), nil }, 900*time.Second) th.AssertNoErr(t, err) t.Run("update", func(t *testing.T) { testNodeGroupUpdate(t, client, clusterID, nodeGroupID) }) t.Run("delete", func(t *testing.T) { testNodeGroupDelete(t, client, clusterID, nodeGroupID) }) } func testNodeGroupsList(t *testing.T, client *gophercloud.ServiceClient, clusterID string) { allPages, err := nodegroups.List(client, clusterID, nil).AllPages() th.AssertNoErr(t, err) allNodeGroups, err := nodegroups.ExtractNodeGroups(allPages) th.AssertNoErr(t, err) // By default two node groups should be created th.AssertEquals(t, 2, len(allNodeGroups)) } func testNodeGroupGet(t *testing.T, client *gophercloud.ServiceClient, clusterID string) { listOpts := nodegroups.ListOpts{ Role: "worker", } allPages, err := nodegroups.List(client, clusterID, listOpts).AllPages() th.AssertNoErr(t, err) allNodeGroups, err := nodegroups.ExtractNodeGroups(allPages) th.AssertNoErr(t, err) // Should be one worker node group th.AssertEquals(t, 1, len(allNodeGroups)) ngID := allNodeGroups[0].UUID ng, err := nodegroups.Get(client, clusterID, ngID).Extract() th.AssertNoErr(t, err) // Should have got the same node group as from the list th.AssertEquals(t, ngID, ng.UUID) th.AssertEquals(t, "worker", ng.Role) } func testNodeGroupCreate(t *testing.T, client *gophercloud.ServiceClient, clusterID string) string { name := tools.RandomString("test-ng-", 8) // have to create two nodes for the Update test (can't set minimum above actual node count) two := 2 createOpts := nodegroups.CreateOpts{ Name: name, NodeCount: &two, } ng, err := nodegroups.Create(client, clusterID, createOpts).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, name, ng.Name) return ng.UUID } func testNodeGroupUpdate(t *testing.T, client *gophercloud.ServiceClient, clusterID, nodeGroupID string) { // Node group starts with min=1, max=unset // Set min, then set max, then set both updateOpts := []nodegroups.UpdateOptsBuilder{ nodegroups.UpdateOpts{ Op: nodegroups.ReplaceOp, Path: "/min_node_count", Value: 2, }, } ng, err := nodegroups.Update(client, clusterID, nodeGroupID, updateOpts).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, 2, ng.MinNodeCount) updateOpts = []nodegroups.UpdateOptsBuilder{ nodegroups.UpdateOpts{ Op: nodegroups.ReplaceOp, Path: "/max_node_count", Value: 5, }, } ng, err = nodegroups.Update(client, clusterID, nodeGroupID, updateOpts).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, false, ng.MaxNodeCount == nil) th.AssertEquals(t, 5, *ng.MaxNodeCount) updateOpts = []nodegroups.UpdateOptsBuilder{ nodegroups.UpdateOpts{ Op: nodegroups.ReplaceOp, Path: "/min_node_count", Value: 1, }, nodegroups.UpdateOpts{ Op: nodegroups.ReplaceOp, Path: "/max_node_count", Value: 3, }, } ng, err = nodegroups.Update(client, clusterID, nodeGroupID, updateOpts).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, false, ng.MaxNodeCount == nil) th.AssertEquals(t, 1, ng.MinNodeCount) th.AssertEquals(t, 3, *ng.MaxNodeCount) } func testNodeGroupDelete(t *testing.T, client *gophercloud.ServiceClient, clusterID, nodeGroupID string) { err := nodegroups.Delete(client, clusterID, nodeGroupID).ExtractErr() th.AssertNoErr(t, err) // Wait for the node group to be deleted err = tools.WaitFor(func() (bool, error) { _, err := nodegroups.Get(client, clusterID, nodeGroupID).Extract() if _, ok := err.(gophercloud.ErrDefault404); ok { return true, nil } return false, nil }) th.AssertNoErr(t, err) } golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/containerinfra/v1/pkg.go000066400000000000000000000000131367513235700316660ustar00rootroot00000000000000package v1 golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/containerinfra/v1/quotas_test.go000066400000000000000000000006751367513235700334760ustar00rootroot00000000000000// +build acceptance containerinfra package v1 import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" th "github.com/gophercloud/gophercloud/testhelper" ) func TestQuotasCRUD(t *testing.T) { client, err := clients.NewContainerInfraV1Client() th.AssertNoErr(t, err) quota, err := CreateQuota(t, client) th.AssertNoErr(t, err) tools.PrintResource(t, quota) } golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/db/000077500000000000000000000000001367513235700256215ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/db/v1/000077500000000000000000000000001367513235700261475ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/db/v1/configurations_test.go000066400000000000000000000042541367513235700325740ustar00rootroot00000000000000// +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" th "github.com/gophercloud/gophercloud/testhelper" ) 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) } readCgroup, err := configurations.Get(client, cgroup.ID).Extract() if err != nil { t.Fatalf("Unable to read configuration: %v", err) } tools.PrintResource(t, readCgroup) th.AssertEquals(t, readCgroup.Name, createOpts.Name) th.AssertEquals(t, readCgroup.Description, createOpts.Description) // TODO: verify datastore //th.AssertDeepEquals(t, readCgroup.Datastore, datastore) // Update cgroup newCgroupName := "New configuration name" newCgroupDescription := "" updateOpts := configurations.UpdateOpts{ Name: newCgroupName, Description: &newCgroupDescription, } err = configurations.Update(client, cgroup.ID, updateOpts).ExtractErr() th.AssertNoErr(t, err) newCgroup, err := configurations.Get(client, cgroup.ID).Extract() if err != nil { t.Fatalf("Unable to read updated configuration: %v", err) } tools.PrintResource(t, newCgroup) th.AssertEquals(t, newCgroup.Name, newCgroupName) th.AssertEquals(t, newCgroup.Description, newCgroupDescription) err = configurations.Delete(client, cgroup.ID).ExtractErr() if err != nil { t.Fatalf("Unable to delete configuration: %v", err) } } golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/db/v1/databases_test.go000066400000000000000000000024241367513235700314660ustar00rootroot00000000000000// +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.12.0/acceptance/openstack/db/v1/db.go000066400000000000000000000111531367513235700270640ustar00rootroot00000000000000// 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 }) } golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/db/v1/flavors_test.go000066400000000000000000000024231367513235700312120ustar00rootroot00000000000000// +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) } } golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/db/v1/instances_test.go000066400000000000000000000033421367513235700315260ustar00rootroot00000000000000// +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.12.0/acceptance/openstack/db/v1/pkg.go000066400000000000000000000000131367513235700272510ustar00rootroot00000000000000package v1 golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/db/v1/users_test.go000066400000000000000000000023411367513235700306760ustar00rootroot00000000000000// +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.12.0/acceptance/openstack/dns/000077500000000000000000000000001367513235700260205ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/dns/v2/000077500000000000000000000000001367513235700263475ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/dns/v2/dns.go000066400000000000000000000115101367513235700274600ustar00rootroot00000000000000package 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 }) } golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/dns/v2/recordsets_test.go000066400000000000000000000050101367513235700321060ustar00rootroot00000000000000// +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) description := "" updateOpts := recordsets.UpdateOpts{ Description: &description, } newRS, err := recordsets.Update(client, rs.ZoneID, rs.ID, updateOpts).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, &newRS) th.AssertEquals(t, newRS.Description, description) records := []string{"10.1.0.3"} updateOpts = recordsets.UpdateOpts{ Records: records, } newRS, err = recordsets.Update(client, rs.ZoneID, rs.ID, updateOpts).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, &newRS) th.AssertDeepEquals(t, newRS.Records, records) th.AssertEquals(t, newRS.TTL, 3600) ttl := 0 updateOpts = recordsets.UpdateOpts{ TTL: &ttl, } newRS, err = recordsets.Update(client, rs.ZoneID, rs.ID, updateOpts).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, &newRS) th.AssertDeepEquals(t, newRS.Records, records) th.AssertEquals(t, newRS.TTL, ttl) } golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/dns/v2/zones_test.go000066400000000000000000000021711367513235700310740ustar00rootroot00000000000000// +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) description := "" updateOpts := zones.UpdateOpts{ Description: &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, description) } golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/identity/000077500000000000000000000000001367513235700270655ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/identity/v2/000077500000000000000000000000001367513235700274145ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/identity/v2/extension_test.go000066400000000000000000000021371367513235700330210ustar00rootroot00000000000000// +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) } golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/identity/v2/identity.go000066400000000000000000000135431367513235700316020ustar00rootroot00000000000000// 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) description := tools.RandomString("ACPTTEST-DESC", 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 createOpts.Description = description 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) th.AssertEquals(t, description, tenant.Description) 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 } golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/identity/v2/pkg.go000066400000000000000000000000131367513235700305160ustar00rootroot00000000000000package v2 golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/identity/v2/role_test.go000066400000000000000000000033341367513235700317460ustar00rootroot00000000000000// +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) } golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/identity/v2/tenant_test.go000066400000000000000000000027021367513235700322740ustar00rootroot00000000000000// +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) description := "" updateOpts := tenants.UpdateOpts{ Description: &description, } newTenant, err := tenants.Update(client, tenant.ID, updateOpts).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, newTenant) th.AssertEquals(t, newTenant.Description, description) } golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/identity/v2/token_test.go000066400000000000000000000026151367513235700321260ustar00rootroot00000000000000// +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) } golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/identity/v2/user_test.go000066400000000000000000000023561367513235700317660ustar00rootroot00000000000000// +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.12.0/acceptance/openstack/identity/v3/000077500000000000000000000000001367513235700274155ustar00rootroot00000000000000applicationcredentials_test.go000066400000000000000000000237301367513235700354520ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/identity/v3// +build acceptance package v3 import ( "testing" "time" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack" "github.com/gophercloud/gophercloud/openstack/identity/v3/applicationcredentials" "github.com/gophercloud/gophercloud/openstack/identity/v3/tokens" th "github.com/gophercloud/gophercloud/testhelper" ) func TestApplicationCredentialsCRD(t *testing.T) { clients.SkipRelease(t, "stable/mitaka") clients.SkipRelease(t, "stable/newton") clients.SkipRelease(t, "stable/ocata") clients.SkipRelease(t, "stable/pike") // maps are required, because Application Credential roles are returned in a random order rolesToMap := func(roles []applicationcredentials.Role) map[string]string { rolesMap := map[string]string{} for _, role := range roles { rolesMap[role.Name] = role.Name rolesMap[role.ID] = role.ID } return rolesMap } 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: ao.DomainName, DomainID: ao.DomainID, // We need a scope to get the token roles list Scope: tokens.Scope{ ProjectID: ao.TenantID, ProjectName: ao.TenantName, DomainID: ao.DomainID, DomainName: ao.DomainName, }, } token, err := tokens.Create(client, &authOptions).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, token) 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) // prepare create parameters var apRoles []applicationcredentials.Role for i, role := range roles { if i%2 == 0 { apRoles = append(apRoles, applicationcredentials.Role{Name: role.Name}) } else { apRoles = append(apRoles, applicationcredentials.Role{ID: role.ID}) } if i > 4 { break } } tools.PrintResource(t, apRoles) // restricted, limited TTL, with limited roles, autogenerated secret expiresAt := time.Now().Add(time.Minute).Truncate(time.Millisecond).UTC() createOpts := applicationcredentials.CreateOpts{ Name: "test-ac", Description: "test application credential", Roles: apRoles, ExpiresAt: &expiresAt, } applicationCredential, err := applicationcredentials.Create(client, user.ID, createOpts).Extract() th.AssertNoErr(t, err) defer applicationcredentials.Delete(client, user.ID, applicationCredential.ID) tools.PrintResource(t, applicationCredential) if applicationCredential.Secret == "" { t.Fatalf("Application credential secret was not generated") } th.AssertEquals(t, applicationCredential.ExpiresAt, expiresAt) th.AssertEquals(t, applicationCredential.Name, createOpts.Name) th.AssertEquals(t, applicationCredential.Description, createOpts.Description) th.AssertEquals(t, applicationCredential.Unrestricted, false) th.AssertEquals(t, applicationCredential.ProjectID, project.ID) checkACroles := rolesToMap(applicationCredential.Roles) for i, role := range roles { if i%2 == 0 { th.AssertEquals(t, checkACroles[role.Name], role.Name) } else { th.AssertEquals(t, checkACroles[role.ID], role.ID) } if i > 4 { break } } // Get an application credential getApplicationCredential, err := applicationcredentials.Get(client, user.ID, applicationCredential.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, getApplicationCredential) if getApplicationCredential.Secret != "" { t.Fatalf("Application credential secret should not be returned by a GET request") } th.AssertEquals(t, getApplicationCredential.ExpiresAt, expiresAt) th.AssertEquals(t, getApplicationCredential.Name, createOpts.Name) th.AssertEquals(t, getApplicationCredential.Description, createOpts.Description) th.AssertEquals(t, getApplicationCredential.Unrestricted, false) th.AssertEquals(t, getApplicationCredential.ProjectID, project.ID) checkACroles = rolesToMap(getApplicationCredential.Roles) for i, role := range roles { if i%2 == 0 { th.AssertEquals(t, checkACroles[role.Name], role.Name) } else { th.AssertEquals(t, checkACroles[role.ID], role.ID) } if i > 4 { break } } // unrestricted, unlimited TTL, with all possible roles, with a custom secret createOpts = applicationcredentials.CreateOpts{ Name: "super-test-ac", Description: "test unrestricted application credential", Unrestricted: true, Secret: "myprecious", } newApplicationCredential, err := applicationcredentials.Create(client, user.ID, createOpts).Extract() th.AssertNoErr(t, err) defer applicationcredentials.Delete(client, user.ID, newApplicationCredential.ID) tools.PrintResource(t, newApplicationCredential) th.AssertEquals(t, newApplicationCredential.ExpiresAt, time.Time{}) th.AssertEquals(t, newApplicationCredential.Name, createOpts.Name) th.AssertEquals(t, newApplicationCredential.Description, createOpts.Description) th.AssertEquals(t, newApplicationCredential.Secret, createOpts.Secret) th.AssertEquals(t, newApplicationCredential.Unrestricted, true) th.AssertEquals(t, newApplicationCredential.ExpiresAt, time.Time{}) th.AssertEquals(t, newApplicationCredential.ProjectID, project.ID) checkACroles = rolesToMap(newApplicationCredential.Roles) for _, role := range roles { th.AssertEquals(t, checkACroles[role.Name], role.Name) th.AssertEquals(t, checkACroles[role.ID], role.ID) } } func TestApplicationCredentialsAccessRules(t *testing.T) { clients.RequireAdmin(t) clients.SkipRelease(t, "stable/mitaka") clients.SkipRelease(t, "stable/newton") clients.SkipRelease(t, "stable/ocata") clients.SkipRelease(t, "stable/pike") clients.SkipRelease(t, "stable/queens") clients.SkipRelease(t, "stable/rocky") clients.SkipRelease(t, "stable/stein") 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: ao.DomainName, DomainID: ao.DomainID, // We need a scope to get the token roles list Scope: tokens.Scope{ ProjectID: ao.TenantID, ProjectName: ao.TenantName, DomainID: ao.DomainID, DomainName: ao.DomainName, }, } token, err := tokens.Create(client, &authOptions).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, token) user, err := tokens.Get(client, token.ID).ExtractUser() th.AssertNoErr(t, err) tools.PrintResource(t, user) // prepare create parameters apAccessRules := []applicationcredentials.AccessRule{ { Path: "/v2.0/metrics", Service: "monitoring", Method: "GET", }, { Path: "/v2.0/metrics", Service: "monitoring", Method: "PUT", }, } tools.PrintResource(t, apAccessRules) // restricted, limited TTL, with limited roles, autogenerated secret expiresAt := time.Now().Add(time.Minute).Truncate(time.Millisecond).UTC() createOpts := applicationcredentials.CreateOpts{ Name: "test-ac", Description: "test application credential", AccessRules: apAccessRules, ExpiresAt: &expiresAt, } applicationCredential, err := applicationcredentials.Create(client, user.ID, createOpts).Extract() th.AssertNoErr(t, err) defer applicationcredentials.Delete(client, user.ID, applicationCredential.ID) tools.PrintResource(t, applicationCredential) if applicationCredential.Secret == "" { t.Fatalf("Application credential secret was not generated") } th.AssertEquals(t, applicationCredential.ExpiresAt, expiresAt) th.AssertEquals(t, applicationCredential.Name, createOpts.Name) th.AssertEquals(t, applicationCredential.Description, createOpts.Description) th.AssertEquals(t, applicationCredential.Unrestricted, false) for i, rule := range applicationCredential.AccessRules { th.AssertEquals(t, rule.Path, apAccessRules[i].Path) th.AssertEquals(t, rule.Service, apAccessRules[i].Service) th.AssertEquals(t, rule.Method, apAccessRules[i].Method) } // Get an application credential getApplicationCredential, err := applicationcredentials.Get(client, user.ID, applicationCredential.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, getApplicationCredential) if getApplicationCredential.Secret != "" { t.Fatalf("Application credential secret should not be returned by a GET request") } th.AssertEquals(t, getApplicationCredential.ExpiresAt, expiresAt) th.AssertEquals(t, getApplicationCredential.Name, createOpts.Name) th.AssertEquals(t, getApplicationCredential.Description, createOpts.Description) th.AssertEquals(t, getApplicationCredential.Unrestricted, false) for i, rule := range applicationCredential.AccessRules { th.AssertEquals(t, rule.Path, apAccessRules[i].Path) th.AssertEquals(t, rule.Service, apAccessRules[i].Service) th.AssertEquals(t, rule.Method, apAccessRules[i].Method) } // test list allPages, err := applicationcredentials.ListAccessRules(client, user.ID).AllPages() th.AssertNoErr(t, err) actual, err := applicationcredentials.ExtractAccessRules(allPages) th.AssertNoErr(t, err) th.CheckDeepEquals(t, getApplicationCredential.AccessRules, actual) // test individual get for i, rule := range actual { getRule, err := applicationcredentials.GetAccessRule(client, user.ID, rule.ID).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, actual[i], *getRule) } res := applicationcredentials.Delete(client, user.ID, applicationCredential.ID) th.AssertNoErr(t, res.Err) // test delete for _, rule := range actual { res := applicationcredentials.DeleteAccessRule(client, user.ID, rule.ID) th.AssertNoErr(t, res.Err) } allPages, err = applicationcredentials.ListAccessRules(client, user.ID).AllPages() th.AssertNoErr(t, err) actual, err = applicationcredentials.ExtractAccessRules(allPages) th.AssertNoErr(t, err) th.AssertEquals(t, len(actual), 0) } golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/identity/v3/credentials_test.go000066400000000000000000000110101367513235700332710ustar00rootroot00000000000000// +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/credentials" "github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/ec2tokens" "github.com/gophercloud/gophercloud/openstack/identity/v3/tokens" th "github.com/gophercloud/gophercloud/testhelper" ) func TestCredentialsCRUD(t *testing.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: ao.DomainName, DomainID: ao.DomainID, // We need a scope to get the token roles list Scope: tokens.Scope{ ProjectID: ao.TenantID, ProjectName: ao.TenantName, DomainID: ao.DomainID, DomainName: ao.DomainName, }, } token, err := tokens.Create(client, &authOptions).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, token) user, err := tokens.Get(client, token.ID).ExtractUser() th.AssertNoErr(t, err) tools.PrintResource(t, user) project, err := tokens.Get(client, token.ID).ExtractProject() th.AssertNoErr(t, err) tools.PrintResource(t, project) createOpts := credentials.CreateOpts{ ProjectID: project.ID, Type: "ec2", UserID: user.ID, Blob: "{\"access\":\"181920\",\"secret\":\"secretKey\"}", } // Create a credential credential, err := credentials.Create(client, createOpts).Extract() th.AssertNoErr(t, err) // Delete a credential defer credentials.Delete(client, credential.ID) tools.PrintResource(t, credential) th.AssertEquals(t, credential.Blob, createOpts.Blob) th.AssertEquals(t, credential.Type, createOpts.Type) th.AssertEquals(t, credential.UserID, createOpts.UserID) th.AssertEquals(t, credential.ProjectID, createOpts.ProjectID) // Get a credential getCredential, err := credentials.Get(client, credential.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, getCredential) th.AssertEquals(t, getCredential.Blob, createOpts.Blob) th.AssertEquals(t, getCredential.Type, createOpts.Type) th.AssertEquals(t, getCredential.UserID, createOpts.UserID) th.AssertEquals(t, getCredential.ProjectID, createOpts.ProjectID) updateOpts := credentials.UpdateOpts{ ProjectID: project.ID, Type: "ec2", UserID: user.ID, Blob: "{\"access\":\"181920\",\"secret\":\"mySecret\"}", } // Update a credential updateCredential, err := credentials.Update(client, credential.ID, updateOpts).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, updateCredential) th.AssertEquals(t, updateCredential.Blob, updateOpts.Blob) } func TestCredentialsValidateS3(t *testing.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: ao.DomainName, DomainID: ao.DomainID, // We need a scope to get the token roles list Scope: tokens.Scope{ ProjectID: ao.TenantID, ProjectName: ao.TenantName, DomainID: ao.DomainID, DomainName: ao.DomainName, }, } token, err := tokens.Create(client, &authOptions).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, token) user, err := tokens.Get(client, token.ID).ExtractUser() th.AssertNoErr(t, err) tools.PrintResource(t, user) project, err := tokens.Get(client, token.ID).ExtractProject() th.AssertNoErr(t, err) tools.PrintResource(t, project) createOpts := credentials.CreateOpts{ ProjectID: project.ID, Type: "ec2", UserID: user.ID, Blob: "{\"access\":\"181920\",\"secret\":\"secretKey\"}", } // Create a credential credential, err := credentials.Create(client, createOpts).Extract() th.AssertNoErr(t, err) // Delete a credential defer credentials.Delete(client, credential.ID) tools.PrintResource(t, credential) th.AssertEquals(t, credential.Blob, createOpts.Blob) th.AssertEquals(t, credential.Type, createOpts.Type) th.AssertEquals(t, credential.UserID, createOpts.UserID) th.AssertEquals(t, credential.ProjectID, createOpts.ProjectID) opts := ec2tokens.AuthOptions{ Access: "181920", Secret: "secretKey", // auth will fail if this is not s3 Service: "s3", } // Validate a credential token, err = ec2tokens.ValidateS3Token(client, &opts).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, token) } golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/identity/v3/domains_test.go000066400000000000000000000036261367513235700324440ustar00rootroot00000000000000// +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 var description = "Testing Domain" createOpts := domains.CreateOpts{ Description: description, 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, description) var iFalse bool = false description = "" updateOpts := domains.UpdateOpts{ Description: &description, Enabled: &iFalse, } newDomain, err := domains.Update(client, domain.ID, updateOpts).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, newDomain) th.AssertEquals(t, newDomain.Description, description) } golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/identity/v3/ec2credentials_test.go000066400000000000000000000053611367513235700336770ustar00rootroot00000000000000// +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/extensions/ec2credentials" "github.com/gophercloud/gophercloud/openstack/identity/v3/tokens" th "github.com/gophercloud/gophercloud/testhelper" ) func TestEC2CredentialsCRD(t *testing.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: ao.DomainName, DomainID: ao.DomainID, // We need a scope to get the token roles list Scope: tokens.Scope{ ProjectID: ao.TenantID, ProjectName: ao.TenantName, DomainID: ao.DomainID, DomainName: ao.DomainName, }, } res := tokens.Create(client, &authOptions) th.AssertNoErr(t, res.Err) token, err := res.Extract() th.AssertNoErr(t, err) tools.PrintResource(t, token) user, err := res.ExtractUser() th.AssertNoErr(t, err) tools.PrintResource(t, user) project, err := res.ExtractProject() th.AssertNoErr(t, err) tools.PrintResource(t, project) createOpts := ec2credentials.CreateOpts{ TenantID: project.ID, } ec2credential, err := ec2credentials.Create(client, user.ID, createOpts).Extract() th.AssertNoErr(t, err) defer ec2credentials.Delete(client, user.ID, ec2credential.Access) tools.PrintResource(t, ec2credential) access := ec2credential.Access secret := ec2credential.Secret if access == "" { t.Fatalf("EC2 credential access was not generated") } if secret == "" { t.Fatalf("EC2 credential secret was not generated") } th.AssertEquals(t, ec2credential.UserID, user.ID) th.AssertEquals(t, ec2credential.TenantID, project.ID) // Get an ec2 credential getEC2Credential, err := ec2credentials.Get(client, user.ID, ec2credential.Access).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, getEC2Credential) th.AssertEquals(t, getEC2Credential.UserID, user.ID) th.AssertEquals(t, getEC2Credential.TenantID, project.ID) th.AssertEquals(t, getEC2Credential.Access, access) th.AssertEquals(t, getEC2Credential.Secret, secret) allPages, err := ec2credentials.List(client, user.ID).AllPages() th.AssertNoErr(t, err) credentials, err := ec2credentials.ExtractCredentials(allPages) th.AssertNoErr(t, err) if v := len(credentials); v != 1 { t.Fatalf("expected to list one credential, got %d", v) } th.AssertEquals(t, credentials[0].UserID, user.ID) th.AssertEquals(t, credentials[0].TenantID, project.ID) th.AssertEquals(t, credentials[0].Access, access) th.AssertEquals(t, credentials[0].Secret, secret) } golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/identity/v3/endpoint_test.go000066400000000000000000000035661367513235700326350ustar00rootroot00000000000000// +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]) } golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/identity/v3/groups_test.go000066400000000000000000000057661367513235700323400ustar00rootroot00000000000000// +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) description := "Test Groups" domainID := "default" createOpts := groups.CreateOpts{ Description: description, DomainID: domainID, 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) th.AssertEquals(t, group.Description, description) th.AssertEquals(t, group.DomainID, domainID) th.AssertDeepEquals(t, group.Extra, createOpts.Extra) description = "" updateOpts := groups.UpdateOpts{ Description: &description, 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) th.AssertEquals(t, newGroup.Description, description) th.AssertDeepEquals(t, newGroup.Extra, updateOpts.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) } golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/identity/v3/identity.go000066400000000000000000000275141367513235700316060ustar00rootroot00000000000000package 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/extensions/trusts" "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) description := tools.RandomString("ACPTTEST-DESC", 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 createOpts.Description = description 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) th.AssertEquals(t, project.Description, description) 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 } // CreateTrust will create a trust with the provided options. // An error will be returned if the trust was unable to be created. func CreateTrust(t *testing.T, client *gophercloud.ServiceClient, createOpts trusts.CreateOpts) (*trusts.Trust, error) { trust, err := trusts.Create(client, createOpts).Extract() if err != nil { return nil, err } t.Logf("Successfully created trust %s", trust.ID) return trust, nil } // DeleteTrust will delete a trust by ID. A fatal error will occur if // the trust failed to be deleted. This works best when using it as // a deferred function. func DeleteTrust(t *testing.T, client *gophercloud.ServiceClient, trustID string) { err := trusts.Delete(client, trustID).ExtractErr() if err != nil { t.Fatalf("Unable to delete trust %s: %v", trustID, err) } t.Logf("Deleted trust: %s", trustID) } // FindTrust finds all trusts 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 FindTrust(t *testing.T, client *gophercloud.ServiceClient) (*trusts.Trust, error) { t.Log("Attempting to find a trust") var trust *trusts.Trust allPages, err := trusts.List(client, nil).AllPages() if err != nil { return nil, err } allTrusts, err := trusts.ExtractTrusts(allPages) if err != nil { return nil, err } for _, t := range allTrusts { trust = &t break } t.Logf("Successfully found a trust %s ", trust.ID) return trust, nil } golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/identity/v3/oauth1_test.go000066400000000000000000000157771367513235700322250ustar00rootroot00000000000000// +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" "github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/oauth1" "github.com/gophercloud/gophercloud/openstack/identity/v3/tokens" th "github.com/gophercloud/gophercloud/testhelper" ) func TestOAuth1CRUD(t *testing.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: ao.DomainName, DomainID: ao.DomainID, // We need a scope to get the token roles list Scope: tokens.Scope{ ProjectID: ao.TenantID, ProjectName: ao.TenantName, DomainID: ao.DomainID, DomainName: ao.DomainName, }, } tokenRes := tokens.Create(client, &authOptions) token, err := tokenRes.Extract() th.AssertNoErr(t, err) tools.PrintResource(t, token) user, err := tokenRes.ExtractUser() th.AssertNoErr(t, err) tools.PrintResource(t, user) roles, err := tokenRes.ExtractRoles() th.AssertNoErr(t, err) tools.PrintResource(t, roles) project, err := tokenRes.ExtractProject() th.AssertNoErr(t, err) tools.PrintResource(t, project) // Create a consumer createConsumerOpts := oauth1.CreateConsumerOpts{ Description: "My test consumer", } // NOTE: secret is available only in create response consumer, err := oauth1.CreateConsumer(client, createConsumerOpts).Extract() th.AssertNoErr(t, err) // Delete a consumer defer oauth1.DeleteConsumer(client, consumer.ID) tools.PrintResource(t, consumer) th.AssertEquals(t, consumer.Description, createConsumerOpts.Description) // Update a consumer updateConsumerOpts := oauth1.UpdateConsumerOpts{ Description: "", } updatedConsumer, err := oauth1.UpdateConsumer(client, consumer.ID, updateConsumerOpts).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, updatedConsumer) th.AssertEquals(t, updatedConsumer.ID, consumer.ID) th.AssertEquals(t, updatedConsumer.Description, updateConsumerOpts.Description) // Get a consumer getConsumer, err := oauth1.GetConsumer(client, consumer.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, getConsumer) th.AssertEquals(t, getConsumer.ID, consumer.ID) th.AssertEquals(t, getConsumer.Description, updateConsumerOpts.Description) // List consumers consumersPages, err := oauth1.ListConsumers(client).AllPages() th.AssertNoErr(t, err) consumers, err := oauth1.ExtractConsumers(consumersPages) th.AssertNoErr(t, err) th.AssertEquals(t, consumers[0].ID, updatedConsumer.ID) th.AssertEquals(t, consumers[0].Description, updatedConsumer.Description) // test HMACSHA1 and PLAINTEXT signature methods for _, method := range []oauth1.SignatureMethod{oauth1.HMACSHA1, oauth1.PLAINTEXT} { oauth1MethodTest(t, client, consumer, method, user, project, roles, ao.IdentityEndpoint) } } func oauth1MethodTest(t *testing.T, client *gophercloud.ServiceClient, consumer *oauth1.Consumer, method oauth1.SignatureMethod, user *tokens.User, project *tokens.Project, roles []tokens.Role, identityEndpoint string) { // Request a token requestTokenOpts := oauth1.RequestTokenOpts{ OAuthConsumerKey: consumer.ID, OAuthConsumerSecret: consumer.Secret, OAuthSignatureMethod: method, RequestedProjectID: project.ID, } requestToken, err := oauth1.RequestToken(client, requestTokenOpts).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, requestToken) // Authorize token authorizeTokenOpts := oauth1.AuthorizeTokenOpts{ Roles: []oauth1.Role{ // test role by ID {ID: roles[0].ID}, }, } if len(roles) > 1 { // test role by name authorizeTokenOpts.Roles = append(authorizeTokenOpts.Roles, oauth1.Role{Name: roles[1].Name}) } authToken, err := oauth1.AuthorizeToken(client, requestToken.OAuthToken, authorizeTokenOpts).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, authToken) // Create access token accessTokenOpts := oauth1.CreateAccessTokenOpts{ OAuthConsumerKey: consumer.ID, OAuthConsumerSecret: consumer.Secret, OAuthToken: requestToken.OAuthToken, OAuthTokenSecret: requestToken.OAuthTokenSecret, OAuthVerifier: authToken.OAuthVerifier, OAuthSignatureMethod: method, } accessToken, err := oauth1.CreateAccessToken(client, accessTokenOpts).Extract() th.AssertNoErr(t, err) defer oauth1.RevokeAccessToken(client, user.ID, accessToken.OAuthToken) tools.PrintResource(t, accessToken) // Get access token getAccessToken, err := oauth1.GetAccessToken(client, user.ID, accessToken.OAuthToken).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, getAccessToken) th.AssertEquals(t, getAccessToken.ID, accessToken.OAuthToken) th.AssertEquals(t, getAccessToken.ConsumerID, consumer.ID) th.AssertEquals(t, getAccessToken.AuthorizingUserID, user.ID) th.AssertEquals(t, getAccessToken.ProjectID, project.ID) // List access tokens accessTokensPages, err := oauth1.ListAccessTokens(client, user.ID).AllPages() th.AssertNoErr(t, err) accessTokens, err := oauth1.ExtractAccessTokens(accessTokensPages) th.AssertNoErr(t, err) tools.PrintResource(t, accessTokens) th.AssertDeepEquals(t, accessTokens[0], *getAccessToken) // List access token roles accessTokenRolesPages, err := oauth1.ListAccessTokenRoles(client, user.ID, accessToken.OAuthToken).AllPages() th.AssertNoErr(t, err) accessTokenRoles, err := oauth1.ExtractAccessTokenRoles(accessTokenRolesPages) th.AssertNoErr(t, err) tools.PrintResource(t, accessTokenRoles) th.AssertEquals(t, accessTokenRoles[0].ID, roles[0].ID) if len(accessTokenRoles) > 1 { th.AssertEquals(t, accessTokenRoles[1].Name, roles[1].Name) } // Get access token role getAccessTokenRole, err := oauth1.GetAccessTokenRole(client, user.ID, accessToken.OAuthToken, roles[0].ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, getAccessTokenRole) th.AssertDeepEquals(t, *getAccessTokenRole, accessTokenRoles[0]) // Test auth using OAuth1 newClient, err := clients.NewIdentityV3UnauthenticatedClient() th.AssertNoErr(t, err) // Opts to auth using an oauth1 credential authOptions := &oauth1.AuthOptions{ OAuthConsumerKey: consumer.ID, OAuthConsumerSecret: consumer.Secret, OAuthToken: accessToken.OAuthToken, OAuthTokenSecret: accessToken.OAuthTokenSecret, OAuthSignatureMethod: method, } err = openstack.AuthenticateV3(newClient.ProviderClient, authOptions, gophercloud.EndpointOpts{}) th.AssertNoErr(t, err) // Test OAuth1 token extract var token struct { tokens.Token oauth1.TokenExt } tokenRes := tokens.Get(newClient, newClient.ProviderClient.TokenID) err = tokenRes.ExtractInto(&token) th.AssertNoErr(t, err) oauth1Roles, err := tokenRes.ExtractRoles() th.AssertNoErr(t, err) tools.PrintResource(t, token) tools.PrintResource(t, oauth1Roles) th.AssertEquals(t, token.OAuth1.ConsumerID, consumer.ID) th.AssertEquals(t, token.OAuth1.AccessTokenID, accessToken.OAuthToken) } golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/identity/v3/pkg.go000066400000000000000000000000131367513235700305170ustar00rootroot00000000000000package v3 golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/identity/v3/policies_test.go000066400000000000000000000065701367513235700326220ustar00rootroot00000000000000// +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) } golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/identity/v3/projects_test.go000066400000000000000000000171341367513235700326420ustar00rootroot00000000000000// +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) description := "" iFalse := false updateOpts := projects.UpdateOpts{ Description: &description, Enabled: &iFalse, } updatedProject, err := projects.Update(client, project.ID, updateOpts).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, updatedProject) th.AssertEquals(t, updatedProject.Description, description) th.AssertEquals(t, updatedProject.Enabled, iFalse) } 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) } func TestProjectsTags(t *testing.T) { clients.RequireAdmin(t) client, err := clients.NewIdentityV3Client() th.AssertNoErr(t, err) createOpts := projects.CreateOpts{ Tags: []string{"Tag1", "Tag2"}, } projectMain, err := CreateProject(t, client, &createOpts) th.AssertNoErr(t, err) defer DeleteProject(t, client, projectMain.ID) // Search using all tags listOpts := projects.ListOpts{ Tags: "Tag1,Tag2", } 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 == projectMain.Name { found = true } } th.AssertEquals(t, found, true) // Search using all tags, including a not existing one listOpts = projects.ListOpts{ Tags: "Tag1,Tag2,Tag3", } allPages, err = projects.List(client, listOpts).AllPages() th.AssertNoErr(t, err) allProjects, err = projects.ExtractProjects(allPages) th.AssertNoErr(t, err) th.AssertEquals(t, len(allProjects), 0) // Search matching at least one tag listOpts = projects.ListOpts{ TagsAny: "Tag1,Tag2,Tag3", } 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 == projectMain.Name { found = true } } th.AssertEquals(t, found, true) // Search not matching any single tag listOpts = projects.ListOpts{ NotTagsAny: "Tag1", } 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 == projectMain.Name { found = true } } th.AssertEquals(t, found, false) // Search matching not all tags listOpts = projects.ListOpts{ NotTags: "Tag1,Tag2,Tag3", } 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) // Update the tags updateOpts := projects.UpdateOpts{ Tags: &[]string{"Tag1"}, } updatedProject, err := projects.Update(client, projectMain.ID, updateOpts).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, updatedProject) th.AssertEquals(t, len(updatedProject.Tags), 1) th.AssertEquals(t, updatedProject.Tags[0], "Tag1") // Update the project, but not its tags description := "Test description" updateOpts = projects.UpdateOpts{ Description: &description, } updatedProject, err = projects.Update(client, projectMain.ID, updateOpts).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, updatedProject) th.AssertEquals(t, len(updatedProject.Tags), 1) th.AssertEquals(t, updatedProject.Tags[0], "Tag1") // Remove all Tags updateOpts = projects.UpdateOpts{ Tags: &[]string{}, } updatedProject, err = projects.Update(client, projectMain.ID, updateOpts).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, updatedProject) th.AssertEquals(t, len(updatedProject.Tags), 0) } golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/identity/v3/reauth_test.go000066400000000000000000000015101367513235700322700ustar00rootroot00000000000000// +build acceptance package v3 import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/openstack" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack/identity/v3/projects" ) func TestReauthAuthResultDeadlock(t *testing.T) { clients.RequireAdmin(t) ao, err := openstack.AuthOptionsFromEnv() th.AssertNoErr(t, err) ao.AllowReauth = true provider, err := openstack.AuthenticatedClient(ao) th.AssertNoErr(t, err) provider.SetToken("this is not a valid token") client, err := openstack.NewIdentityV3(provider, gophercloud.EndpointOpts{}) pages, err := projects.List(client, nil).AllPages() th.AssertNoErr(t, err) _, err = projects.ExtractProjects(pages) th.AssertNoErr(t, err) } golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/identity/v3/regions_test.go000066400000000000000000000045031367513235700324530ustar00rootroot00000000000000// +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) var description = "" updateOpts := regions.UpdateOpts{ Description: &description, /* // 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) th.AssertEquals(t, newRegion.Description, description) } golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/identity/v3/roles_test.go000066400000000000000000000425711367513235700321400ustar00rootroot00000000000000// +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") clients.SkipRelease(t, "stable/mitaka") clients.SkipRelease(t, "stable/newton") clients.SkipRelease(t, "stable/ocata") clients.SkipRelease(t, "stable/pike") clients.SkipRelease(t, "stable/queens") clients.SkipRelease(t, "stable/rocky") clients.SkipRelease(t, "stable/stein") 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) } golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/identity/v3/service_test.go000066400000000000000000000033621367513235700324470ustar00rootroot00000000000000// +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") } golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/identity/v3/token_test.go000066400000000000000000000023421367513235700321240ustar00rootroot00000000000000// +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) } golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/identity/v3/trusts_test.go000066400000000000000000000066001367513235700323510ustar00rootroot00000000000000// +build acceptance identity trusts package v3 import ( "testing" "time" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack" "github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/trusts" "github.com/gophercloud/gophercloud/openstack/identity/v3/roles" "github.com/gophercloud/gophercloud/openstack/identity/v3/tokens" "github.com/gophercloud/gophercloud/openstack/identity/v3/users" th "github.com/gophercloud/gophercloud/testhelper" ) func TestTrustCRUD(t *testing.T) { clients.RequireAdmin(t) client, err := clients.NewIdentityV3Client() th.AssertNoErr(t, err) // Generate a token and obtain the Admin user's ID from it. ao, err := openstack.AuthOptionsFromEnv() th.AssertNoErr(t, err) authOptions := tokens.AuthOptions{ Username: ao.Username, Password: ao.Password, DomainName: ao.DomainName, DomainID: ao.DomainID, } token, err := tokens.Create(client, &authOptions).Extract() th.AssertNoErr(t, err) adminUser, err := tokens.Get(client, token.ID).ExtractUser() th.AssertNoErr(t, err) // Get the admin and member role IDs. adminRoleID := "" memberRoleID := "" allPages, err := roles.List(client, nil).AllPages() th.AssertNoErr(t, err) allRoles, err := roles.ExtractRoles(allPages) th.AssertNoErr(t, err) for _, v := range allRoles { if v.Name == "admin" { adminRoleID = v.ID } if v.Name == "member" { memberRoleID = v.ID } } // Create a project to apply the trust. trusteeProject, err := CreateProject(t, client, nil) th.AssertNoErr(t, err) defer DeleteProject(t, client, trusteeProject.ID) tools.PrintResource(t, trusteeProject) // Add the admin user to the trustee project. assignOpts := roles.AssignOpts{ UserID: adminUser.ID, ProjectID: trusteeProject.ID, } err = roles.Assign(client, adminRoleID, assignOpts).ExtractErr() th.AssertNoErr(t, err) // Create a user as the trustee. trusteeUserCreateOpts := users.CreateOpts{ Password: "secret", DomainID: "default", } trusteeUser, err := CreateUser(t, client, &trusteeUserCreateOpts) th.AssertNoErr(t, err) defer DeleteUser(t, client, trusteeUser.ID) expiresAt := time.Now().Add(time.Minute).Truncate(time.Second).UTC() // Create a trust. trust, err := CreateTrust(t, client, trusts.CreateOpts{ TrusteeUserID: trusteeUser.ID, TrustorUserID: adminUser.ID, ProjectID: trusteeProject.ID, ExpiresAt: &expiresAt, Roles: []trusts.Role{ { ID: memberRoleID, }, }, }) th.AssertNoErr(t, err) defer DeleteTrust(t, client, trust.ID) trust, err = FindTrust(t, client) th.AssertNoErr(t, err) // Get trust p, err := trusts.Get(client, trust.ID).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, p.ExpiresAt, expiresAt) th.AssertEquals(t, p.DeletedAt.IsZero(), true) tools.PrintResource(t, p) // List trust roles rolesPages, err := trusts.ListRoles(client, p.ID).AllPages() th.AssertNoErr(t, err) allTrustRoles, err := trusts.ExtractRoles(rolesPages) th.AssertNoErr(t, err) th.AssertEquals(t, len(allTrustRoles), 1) th.AssertEquals(t, allTrustRoles[0].ID, memberRoleID) // Get trust role role, err := trusts.GetRole(client, p.ID, memberRoleID).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, role.ID, memberRoleID) // Check trust role err = trusts.CheckRole(client, p.ID, memberRoleID).ExtractErr() th.AssertNoErr(t, err) } golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/identity/v3/users_test.go000066400000000000000000000172711367513235700321540ustar00rootroot00000000000000// +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, Description: "test description", 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) th.AssertEquals(t, user.Description, createOpts.Description) th.AssertEquals(t, user.DomainID, createOpts.DomainID) iFalse := false name := "newtestuser" description := "" updateOpts := users.UpdateOpts{ Name: name, Description: &description, 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.Name, name) th.AssertEquals(t, newUser.Description, description) th.AssertEquals(t, newUser.Enabled, iFalse) 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.12.0/acceptance/openstack/imageservice/000077500000000000000000000000001367513235700276775ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/imageservice/v2/000077500000000000000000000000001367513235700302265ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/imageservice/v2/imagedata_test.go000066400000000000000000000017321367513235700335330ustar00rootroot00000000000000package 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) { clients.SkipRelease(t, "stable/mitaka") clients.SkipRelease(t, "stable/newton") clients.SkipRelease(t, "stable/ocata") clients.SkipRelease(t, "stable/pike") clients.SkipRelease(t, "stable/queens") clients.SkipRelease(t, "stable/rocky") 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.go000066400000000000000000000022101367513235700340450ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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) { clients.SkipRelease(t, "stable/mitaka") clients.SkipRelease(t, "stable/newton") clients.SkipRelease(t, "stable/ocata") clients.SkipRelease(t, "stable/pike") clients.SkipRelease(t, "stable/queens") client, err := clients.NewImageServiceV2Client() th.AssertNoErr(t, err) importInfo, err := GetImportInfo(t, client) th.AssertNoErr(t, err) tools.PrintResource(t, importInfo) } func TestCreateImport(t *testing.T) { clients.SkipRelease(t, "stable/mitaka") clients.SkipRelease(t, "stable/newton") clients.SkipRelease(t, "stable/ocata") clients.SkipRelease(t, "stable/pike") clients.SkipRelease(t, "stable/queens") client, err := clients.NewImageServiceV2Client() th.AssertNoErr(t, err) image, err := CreateEmptyImage(t, client) th.AssertNoErr(t, err) defer DeleteImage(t, client, image) err = ImportImage(t, client, image.ID) th.AssertNoErr(t, err) } golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/imageservice/v2/images_test.go000066400000000000000000000105471367513235700330700ustar00rootroot00000000000000// +build acceptance imageservice images package v2 import ( "sort" "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.ReplaceImageMinDisk{NewMinDisk: 21}, 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") sort.Strings(newTags) sort.Strings(newImage.Tags) 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") } } golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/imageservice/v2/imageservice.go000066400000000000000000000117641367513235700332310ustar00rootroot00000000000000// 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) } // ImportImage will import image data from the remote source to the Imageservice. func ImportImage(t *testing.T, client *gophercloud.ServiceClient, imageID string) error { importOpts := imageimport.CreateOpts{ Name: imageimport.WebDownloadMethod, URI: ImportImageURL, } t.Logf("Attempting to import image data for %s from %s", imageID, importOpts.URI) return imageimport.Create(client, imageID, importOpts).ExtractErr() } golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/imageservice/v2/tasks_test.go000066400000000000000000000026311367513235700327430ustar00rootroot00000000000000// +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.12.0/acceptance/openstack/keymanager/000077500000000000000000000000001367513235700273575ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/keymanager/v1/000077500000000000000000000000001367513235700277055ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/keymanager/v1/acls_test.go000066400000000000000000000064671367513235700322320ustar00rootroot00000000000000// +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) { clients.SkipRelease(t, "stable/mitaka") clients.SkipRelease(t, "stable/newton") clients.SkipRelease(t, "stable/queens") 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{ acls.SetOpt{ 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{ acls.SetOpt{ 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) } golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/keymanager/v1/containers_test.go000066400000000000000000000146771367513235700334570ustar00rootroot00000000000000// +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) { clients.SkipRelease(t, "stable/mitaka") clients.SkipRelease(t, "stable/newton") clients.SkipRelease(t, "stable/queens") 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) payload1 := tools.RandomString("SUPERSECRET-", 8) secret1, err := CreateSecretWithPayload(t, client, payload1) th.AssertNoErr(t, err) secretID1, err := ParseID(secret1.SecretRef) th.AssertNoErr(t, err) defer DeleteSecret(t, client, secretID1) container, err := CreateGenericContainer(t, client, secret) th.AssertNoErr(t, err) containerID, err := ParseID(container.ContainerRef) th.AssertNoErr(t, err) defer DeleteContainer(t, client, containerID) err = ReplaceGenericContainerSecretRef(t, client, container, secret, secret1) th.AssertNoErr(t, err) 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) { clients.SkipRelease(t, "stable/mitaka") clients.SkipRelease(t, "stable/newton") clients.SkipRelease(t, "stable/queens") 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) { clients.SkipRelease(t, "stable/mitaka") clients.SkipRelease(t, "stable/newton") clients.SkipRelease(t, "stable/queens") 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) { clients.SkipRelease(t, "stable/mitaka") clients.SkipRelease(t, "stable/newton") clients.SkipRelease(t, "stable/queens") 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) } golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/keymanager/v1/keymanager.go000066400000000000000000000474671367513235700324010ustar00rootroot00000000000000package 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 } // ReplaceGenericContainerSecretRef will replace the container old secret // reference with a new one. An error will be returned if the reference could // not be replaced. func ReplaceGenericContainerSecretRef(t *testing.T, client *gophercloud.ServiceClient, container *containers.Container, secretOld *secrets.Secret, secretNew *secrets.Secret) error { containerID, err := ParseID(container.ContainerRef) if err != nil { return err } t.Logf("Attempting to remove an old secret reference %s", secretOld.SecretRef) res1 := containers.DeleteSecretRef(client, containerID, containers.SecretRef{Name: secretOld.Name, SecretRef: secretOld.SecretRef}) if res1.Err != nil { return res1.Err } t.Logf("Successfully removed old secret reference: %s", secretOld.SecretRef) t.Logf("Attempting to remove a new secret reference %s", secretNew.SecretRef) newRef := containers.SecretRef{Name: secretNew.Name, SecretRef: secretNew.SecretRef} res2 := containers.CreateSecretRef(client, containerID, newRef) if res2.Err != nil { return res2.Err } c, err := res2.Extract() if err != nil { return err } tools.PrintResource(t, c) t.Logf("Successfully created new secret reference: %s", secretNew.SecretRef) updatedContainer, err := containers.Get(client, containerID).Extract() if err != nil { return err } tools.PrintResource(t, container) th.AssertEquals(t, updatedContainer.Name, container.Name) th.AssertEquals(t, updatedContainer.Type, container.Type) th.AssertEquals(t, updatedContainer.SecretRefs[0], newRef) return 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) expiration := time.Date(2049, 1, 1, 1, 1, 1, 0, time.UTC) createOpts := secrets.CreateOpts{ Algorithm: "aes", BitLength: 256, Mode: "cbc", Name: secretName, Payload: payload, PayloadContentType: "text/plain", SecretType: secrets.OpaqueSecret, Expiration: &expiration, } 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") th.AssertEquals(t, secret.Expiration, expiration) 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 }) } golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/keymanager/v1/orders_test.go000066400000000000000000000046011367513235700325720ustar00rootroot00000000000000// +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.SkipRelease(t, "stable/mitaka") clients.SkipRelease(t, "stable/newton") clients.SkipRelease(t, "stable/queens") 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.SkipRelease(t, "stable/mitaka") clients.SkipRelease(t, "stable/newton") clients.SkipRelease(t, "stable/queens") 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)) } } golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/keymanager/v1/secrets_test.go000066400000000000000000000174731367513235700327570ustar00rootroot00000000000000// +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) { clients.SkipRelease(t, "stable/mitaka") clients.SkipRelease(t, "stable/newton") clients.SkipRelease(t, "stable/queens") 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) { clients.SkipRelease(t, "stable/mitaka") clients.SkipRelease(t, "stable/newton") clients.SkipRelease(t, "stable/queens") 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) { clients.SkipRelease(t, "stable/mitaka") clients.SkipRelease(t, "stable/newton") clients.SkipRelease(t, "stable/queens") 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) { clients.SkipRelease(t, "stable/mitaka") clients.SkipRelease(t, "stable/newton") clients.SkipRelease(t, "stable/queens") 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) { clients.SkipRelease(t, "stable/mitaka") clients.SkipRelease(t, "stable/newton") clients.SkipRelease(t, "stable/queens") 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) { clients.SkipRelease(t, "stable/mitaka") clients.SkipRelease(t, "stable/newton") clients.SkipRelease(t, "stable/queens") 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) { clients.SkipRelease(t, "stable/mitaka") clients.SkipRelease(t, "stable/newton") clients.SkipRelease(t, "stable/queens") 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) { clients.SkipRelease(t, "stable/mitaka") clients.SkipRelease(t, "stable/newton") clients.SkipRelease(t, "stable/queens") 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.12.0/acceptance/openstack/loadbalancer/000077500000000000000000000000001367513235700276435ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/loadbalancer/v2/000077500000000000000000000000001367513235700301725ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/loadbalancer/v2/amphorae_test.go000066400000000000000000000020071367513235700333530ustar00rootroot00000000000000// +build acceptance containers capsules package 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) clients.SkipRelease(t, "stable/mitaka") clients.SkipRelease(t, "stable/newton") clients.SkipRelease(t, "stable/ocata") clients.SkipRelease(t, "stable/pike") clients.SkipRelease(t, "stable/queens") clients.SkipRelease(t, "stable/rocky") 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) } } golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/loadbalancer/v2/l7policies_test.go000066400000000000000000000020151367513235700336300ustar00rootroot00000000000000// +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) { clients.SkipRelease(t, "stable/mitaka") clients.SkipRelease(t, "stable/newton") clients.SkipRelease(t, "stable/ocata") clients.SkipRelease(t, "stable/pike") clients.SkipRelease(t, "stable/queens") clients.SkipRelease(t, "stable/rocky") 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) } } golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/loadbalancer/v2/listeners_test.go000066400000000000000000000020071367513235700335670ustar00rootroot00000000000000// +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) { clients.SkipRelease(t, "stable/mitaka") clients.SkipRelease(t, "stable/newton") clients.SkipRelease(t, "stable/ocata") clients.SkipRelease(t, "stable/pike") clients.SkipRelease(t, "stable/queens") clients.SkipRelease(t, "stable/rocky") 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) } } golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/loadbalancer/v2/loadbalancer.go000066400000000000000000000407231367513235700331360ustar00rootroot00000000000000package 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" th "github.com/gophercloud/gophercloud/testhelper" ) const loadbalancerActiveTimeoutSeconds = 600 const loadbalancerDeleteTimeoutSeconds = 600 // 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) listenerDescription := tools.RandomString("TESTACCT-DESC-", 8) listenerPort := tools.RandomInt(1, 100) t.Logf("Attempting to create listener %s on port %d", listenerName, listenerPort) createOpts := listeners.CreateOpts{ Name: listenerName, Description: listenerDescription, LoadbalancerID: lb.ID, Protocol: listeners.ProtocolTCP, 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: %s", err) } th.AssertEquals(t, listener.Name, listenerName) th.AssertEquals(t, listener.Description, listenerDescription) th.AssertEquals(t, listener.Loadbalancers[0].ID, lb.ID) th.AssertEquals(t, listener.Protocol, string(listeners.ProtocolTCP)) th.AssertEquals(t, listener.ProtocolPort, listenerPort) 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, tags []string) (*loadbalancers.LoadBalancer, error) { lbName := tools.RandomString("TESTACCT-", 8) lbDescription := tools.RandomString("TESTACCT-DESC-", 8) t.Logf("Attempting to create loadbalancer %s on subnet %s", lbName, subnetID) createOpts := loadbalancers.CreateOpts{ Name: lbName, Description: lbDescription, VipSubnetID: subnetID, AdminStateUp: gophercloud.Enabled, } if len(tags) > 0 { createOpts.Tags = tags } 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) th.AssertEquals(t, lb.Name, lbName) th.AssertEquals(t, lb.Description, lbDescription) th.AssertEquals(t, lb.VipSubnetID, subnetID) th.AssertEquals(t, lb.AdminStateUp, true) if len(tags) > 0 { th.AssertDeepEquals(t, lb.Tags, tags) } 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: %s", err) } th.AssertEquals(t, member.Name, memberName) 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, MaxRetriesDown: 4, Type: monitors.TypePING, } 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: %s", err) } th.AssertEquals(t, monitor.Name, monitorName) th.AssertEquals(t, monitor.Type, monitors.TypePING) th.AssertEquals(t, monitor.Delay, 10) th.AssertEquals(t, monitor.Timeout, 5) th.AssertEquals(t, monitor.MaxRetries, 5) th.AssertEquals(t, monitor.MaxRetriesDown, 4) 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) poolDescription := tools.RandomString("TESTACCT-DESC-", 8) t.Logf("Attempting to create pool %s", poolName) createOpts := pools.CreateOpts{ Name: poolName, Description: poolDescription, 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: %s", err) } th.AssertEquals(t, pool.Name, poolName) th.AssertEquals(t, pool.Description, poolDescription) th.AssertEquals(t, pool.Protocol, string(pools.ProtocolTCP)) th.AssertEquals(t, pool.Loadbalancers[0].ID, lb.ID) th.AssertEquals(t, pool.LBMethod, string(pools.LBMethodLeastConnections)) 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) policyDescription := tools.RandomString("TESTACCT-DESC-", 8) t.Logf("Attempting to create l7 policy %s", policyName) createOpts := l7policies.CreateOpts{ Name: policyName, Description: policyDescription, 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: %s", err) } th.AssertEquals(t, policy.Name, policyName) th.AssertEquals(t, policy.Description, policyDescription) th.AssertEquals(t, policy.ListenerID, listener.ID) th.AssertEquals(t, policy.Action, string(l7policies.ActionRedirectToURL)) th.AssertEquals(t, policy.RedirectURL, "http://www.example.com") 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: %s", err) } th.AssertEquals(t, rule.RuleType, string(l7policies.TypePath)) th.AssertEquals(t, rule.CompareType, string(l7policies.CompareTypeStartWith)) th.AssertEquals(t, rule.Value, "/api") 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 { if _, ok := err.(gophercloud.ErrDefault404); !ok { 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: %s", err) } 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 { if _, ok := err.(gophercloud.ErrDefault404); !ok { 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: %s", err) } 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 { if _, ok := err.(gophercloud.ErrDefault404); !ok { 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: %s", err) } 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 { if _, ok := err.(gophercloud.ErrDefault404); !ok { 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: %s", err) } 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 { if _, ok := err.(gophercloud.ErrDefault404); !ok { 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: %s", err) } 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 { if _, ok := err.(gophercloud.ErrDefault404); !ok { 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: %s", 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, lbID, poolID string) { t.Logf("Attempting to delete pool %s", poolID) if err := pools.Delete(client, poolID).ExtractErr(); err != nil { if _, ok := err.(gophercloud.ErrDefault404); !ok { 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: %s", err) } 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 } if current.ProvisioningStatus == "ERROR" { return false, fmt.Errorf("Load balancer is in ERROR state") } return false, nil }) } loadbalancers_test.go000066400000000000000000000434001367513235700342740ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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" th "github.com/gophercloud/gophercloud/testhelper" ) func TestLoadbalancersList(t *testing.T) { clients.SkipRelease(t, "stable/mitaka") clients.SkipRelease(t, "stable/newton") clients.SkipRelease(t, "stable/ocata") clients.SkipRelease(t, "stable/pike") clients.SkipRelease(t, "stable/queens") clients.SkipRelease(t, "stable/rocky") client, err := clients.NewLoadBalancerV2Client() th.AssertNoErr(t, err) allPages, err := loadbalancers.List(client, nil).AllPages() th.AssertNoErr(t, err) allLoadbalancers, err := loadbalancers.ExtractLoadBalancers(allPages) th.AssertNoErr(t, err) for _, lb := range allLoadbalancers { tools.PrintResource(t, lb) } } func TestLoadbalancersListByTags(t *testing.T) { clients.SkipRelease(t, "stable/mitaka") clients.SkipRelease(t, "stable/newton") clients.SkipRelease(t, "stable/ocata") clients.SkipRelease(t, "stable/pike") clients.SkipRelease(t, "stable/queens") clients.SkipRelease(t, "stable/rocky") netClient, err := clients.NewNetworkV2Client() th.AssertNoErr(t, err) lbClient, err := clients.NewLoadBalancerV2Client() th.AssertNoErr(t, err) network, err := networking.CreateNetwork(t, netClient) th.AssertNoErr(t, err) defer networking.DeleteNetwork(t, netClient, network.ID) subnet, err := networking.CreateSubnet(t, netClient, network.ID) th.AssertNoErr(t, err) defer networking.DeleteSubnet(t, netClient, subnet.ID) // Add "test" tag intentionally to test the "not-tags" parameter. Because "test" tag is also used in other test // cases, we use "test" tag to exclude load balancers created by other test case. tags := []string{"tag1", "tag2", "test"} lb, err := CreateLoadBalancer(t, lbClient, subnet.ID, tags) th.AssertNoErr(t, err) defer DeleteLoadBalancer(t, lbClient, lb.ID) tags = []string{"tag1"} listOpts := loadbalancers.ListOpts{ Tags: tags, } allPages, err := loadbalancers.List(lbClient, listOpts).AllPages() th.AssertNoErr(t, err) allLoadbalancers, err := loadbalancers.ExtractLoadBalancers(allPages) th.AssertNoErr(t, err) th.AssertEquals(t, 1, len(allLoadbalancers)) tags = []string{"test"} listOpts = loadbalancers.ListOpts{ TagsNot: tags, } allPages, err = loadbalancers.List(lbClient, listOpts).AllPages() th.AssertNoErr(t, err) allLoadbalancers, err = loadbalancers.ExtractLoadBalancers(allPages) th.AssertNoErr(t, err) th.AssertEquals(t, 0, len(allLoadbalancers)) tags = []string{"tag1", "tag3"} listOpts = loadbalancers.ListOpts{ TagsAny: tags, } allPages, err = loadbalancers.List(lbClient, listOpts).AllPages() th.AssertNoErr(t, err) allLoadbalancers, err = loadbalancers.ExtractLoadBalancers(allPages) th.AssertNoErr(t, err) th.AssertEquals(t, 1, len(allLoadbalancers)) tags = []string{"tag1", "test"} listOpts = loadbalancers.ListOpts{ TagsNotAny: tags, } allPages, err = loadbalancers.List(lbClient, listOpts).AllPages() th.AssertNoErr(t, err) allLoadbalancers, err = loadbalancers.ExtractLoadBalancers(allPages) th.AssertNoErr(t, err) th.AssertEquals(t, 0, len(allLoadbalancers)) } func TestLoadbalancersCRUD(t *testing.T) { clients.SkipRelease(t, "stable/mitaka") clients.SkipRelease(t, "stable/newton") clients.SkipRelease(t, "stable/ocata") clients.SkipRelease(t, "stable/pike") clients.SkipRelease(t, "stable/queens") clients.SkipRelease(t, "stable/rocky") netClient, err := clients.NewNetworkV2Client() th.AssertNoErr(t, err) lbClient, err := clients.NewLoadBalancerV2Client() th.AssertNoErr(t, err) network, err := networking.CreateNetwork(t, netClient) th.AssertNoErr(t, err) defer networking.DeleteNetwork(t, netClient, network.ID) subnet, err := networking.CreateSubnet(t, netClient, network.ID) th.AssertNoErr(t, err) defer networking.DeleteSubnet(t, netClient, subnet.ID) tags := []string{"test"} lb, err := CreateLoadBalancer(t, lbClient, subnet.ID, tags) th.AssertNoErr(t, err) defer DeleteLoadBalancer(t, lbClient, lb.ID) lbDescription := "" updateLoadBalancerOpts := loadbalancers.UpdateOpts{ Description: &lbDescription, } _, err = loadbalancers.Update(lbClient, lb.ID, updateLoadBalancerOpts).Extract() th.AssertNoErr(t, err) if err = WaitForLoadBalancerState(lbClient, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { t.Fatalf("Timed out waiting for loadbalancer to become active") } newLB, err := loadbalancers.Get(lbClient, lb.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, newLB) th.AssertEquals(t, newLB.Description, lbDescription) lbStats, err := loadbalancers.GetStats(lbClient, lb.ID).Extract() th.AssertNoErr(t, 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) th.AssertNoErr(t, err) defer DeleteListener(t, lbClient, lb.ID, listener.ID) listenerName := "" listenerDescription := "" listenerHeaders := map[string]string{ "X-Forwarded-For": "true", } updateListenerOpts := listeners.UpdateOpts{ Name: &listenerName, Description: &listenerDescription, InsertHeaders: &listenerHeaders, } _, err = listeners.Update(lbClient, listener.ID, updateListenerOpts).Extract() th.AssertNoErr(t, err) 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() th.AssertNoErr(t, err) tools.PrintResource(t, newListener) th.AssertEquals(t, newListener.Name, listenerName) th.AssertEquals(t, newListener.Description, listenerDescription) th.AssertDeepEquals(t, newListener.InsertHeaders, listenerHeaders) listenerStats, err := listeners.GetStats(lbClient, listener.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, listenerStats) // L7 policy policy, err := CreateL7Policy(t, lbClient, listener, lb) th.AssertNoErr(t, err) defer DeleteL7Policy(t, lbClient, lb.ID, policy.ID) newDescription := "" updateL7policyOpts := l7policies.UpdateOpts{ Description: &newDescription, } _, err = l7policies.Update(lbClient, policy.ID, updateL7policyOpts).Extract() th.AssertNoErr(t, err) 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() th.AssertNoErr(t, err) tools.PrintResource(t, newPolicy) th.AssertEquals(t, newPolicy.Description, newDescription) // L7 rule rule, err := CreateL7Rule(t, lbClient, newPolicy.ID, lb) th.AssertNoErr(t, err) defer DeleteL7Rule(t, lbClient, lb.ID, policy.ID, rule.ID) allPages, err := l7policies.ListRules(lbClient, policy.ID, l7policies.ListRulesOpts{}).AllPages() th.AssertNoErr(t, err) allRules, err := l7policies.ExtractRules(allPages) th.AssertNoErr(t, 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() th.AssertNoErr(t, 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() th.AssertNoErr(t, err) tools.PrintResource(t, newRule) // Pool pool, err := CreatePool(t, lbClient, lb) th.AssertNoErr(t, err) defer DeletePool(t, lbClient, lb.ID, pool.ID) poolName := "" poolDescription := "" updatePoolOpts := pools.UpdateOpts{ Name: &poolName, Description: &poolDescription, } _, err = pools.Update(lbClient, pool.ID, updatePoolOpts).Extract() th.AssertNoErr(t, err) 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() th.AssertNoErr(t, err) tools.PrintResource(t, newPool) th.AssertEquals(t, newPool.Name, poolName) th.AssertEquals(t, newPool.Description, poolDescription) // Update L7policy to redirect to pool newRedirectURL := "" updateL7policyOpts = l7policies.UpdateOpts{ Action: l7policies.ActionRedirectToPool, RedirectPoolID: &newPool.ID, RedirectURL: &newRedirectURL, } _, err = l7policies.Update(lbClient, policy.ID, updateL7policyOpts).Extract() th.AssertNoErr(t, err) 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() th.AssertNoErr(t, err) tools.PrintResource(t, newPolicy) th.AssertEquals(t, newPolicy.Description, newDescription) th.AssertEquals(t, newPolicy.Action, string(l7policies.ActionRedirectToPool)) th.AssertEquals(t, newPolicy.RedirectPoolID, newPool.ID) th.AssertEquals(t, newPolicy.RedirectURL, newRedirectURL) // Workaround for proper delete order defer DeleteL7Policy(t, lbClient, lb.ID, policy.ID) defer DeleteL7Rule(t, lbClient, lb.ID, policy.ID, rule.ID) // Update listener's default pool ID and remove headers listenerHeaders = map[string]string{} updateListenerOpts = listeners.UpdateOpts{ DefaultPoolID: &pool.ID, InsertHeaders: &listenerHeaders, } _, err = listeners.Update(lbClient, listener.ID, updateListenerOpts).Extract() th.AssertNoErr(t, err) 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() th.AssertNoErr(t, err) tools.PrintResource(t, newListener) th.AssertEquals(t, newListener.DefaultPoolID, pool.ID) th.AssertDeepEquals(t, newListener.InsertHeaders, listenerHeaders) // Remove listener's default pool ID emptyPoolID := "" updateListenerOpts = listeners.UpdateOpts{ DefaultPoolID: &emptyPoolID, } _, err = listeners.Update(lbClient, listener.ID, updateListenerOpts).Extract() th.AssertNoErr(t, err) 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() th.AssertNoErr(t, err) tools.PrintResource(t, newListener) th.AssertEquals(t, newListener.DefaultPoolID, "") // Member member, err := CreateMember(t, lbClient, lb, newPool, subnet.ID, subnet.CIDR) th.AssertNoErr(t, err) defer DeleteMember(t, lbClient, lb.ID, pool.ID, member.ID) memberName := "" newWeight := tools.RandomInt(11, 100) updateMemberOpts := pools.UpdateMemberOpts{ Name: &memberName, Weight: &newWeight, } _, err = pools.UpdateMember(lbClient, pool.ID, member.ID, updateMemberOpts).Extract() th.AssertNoErr(t, err) 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() th.AssertNoErr(t, err) tools.PrintResource(t, newMember) th.AssertEquals(t, newMember.Name, memberName) 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() th.AssertNoErr(t, err) tools.PrintResource(t, newMember) // delete members from the pool if err = pools.BatchUpdateMembers(lbClient, pool.ID, []pools.BatchUpdateMemberOpts{}).ExtractErr(); err != nil { t.Fatalf("Unable to delete members") } if err = WaitForLoadBalancerState(lbClient, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { t.Fatalf("Timed out waiting for loadbalancer to become active") } pool, err = pools.Get(lbClient, pool.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, pool) // Monitor monitor, err := CreateMonitor(t, lbClient, lb, newPool) th.AssertNoErr(t, err) defer DeleteMonitor(t, lbClient, lb.ID, monitor.ID) monName := "" newDelay := tools.RandomInt(20, 30) newMaxRetriesDown := tools.RandomInt(4, 10) updateMonitorOpts := monitors.UpdateOpts{ Name: &monName, Delay: newDelay, MaxRetriesDown: newMaxRetriesDown, } _, err = monitors.Update(lbClient, monitor.ID, updateMonitorOpts).Extract() th.AssertNoErr(t, err) 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() th.AssertNoErr(t, err) tools.PrintResource(t, newMonitor) th.AssertEquals(t, newMonitor.Name, monName) th.AssertEquals(t, newMonitor.Delay, newDelay) th.AssertEquals(t, newMonitor.MaxRetriesDown, newMaxRetriesDown) } func TestLoadbalancersCascadeCRUD(t *testing.T) { clients.SkipRelease(t, "stable/mitaka") clients.SkipRelease(t, "stable/newton") clients.SkipRelease(t, "stable/ocata") clients.SkipRelease(t, "stable/pike") clients.SkipRelease(t, "stable/queens") clients.SkipRelease(t, "stable/rocky") netClient, err := clients.NewNetworkV2Client() th.AssertNoErr(t, err) lbClient, err := clients.NewLoadBalancerV2Client() th.AssertNoErr(t, err) network, err := networking.CreateNetwork(t, netClient) th.AssertNoErr(t, err) defer networking.DeleteNetwork(t, netClient, network.ID) subnet, err := networking.CreateSubnet(t, netClient, network.ID) th.AssertNoErr(t, err) defer networking.DeleteSubnet(t, netClient, subnet.ID) tags := []string{"test"} lb, err := CreateLoadBalancer(t, lbClient, subnet.ID, tags) th.AssertNoErr(t, err) defer CascadeDeleteLoadBalancer(t, lbClient, lb.ID) newLB, err := loadbalancers.Get(lbClient, lb.ID).Extract() th.AssertNoErr(t, 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) th.AssertNoErr(t, err) listenerDescription := "Some listener description" updateListenerOpts := listeners.UpdateOpts{ Description: &listenerDescription, } _, err = listeners.Update(lbClient, listener.ID, updateListenerOpts).Extract() th.AssertNoErr(t, err) 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() th.AssertNoErr(t, err) tools.PrintResource(t, newListener) // Pool pool, err := CreatePool(t, lbClient, lb) th.AssertNoErr(t, err) poolDescription := "Some pool description" updatePoolOpts := pools.UpdateOpts{ Description: &poolDescription, } _, err = pools.Update(lbClient, pool.ID, updatePoolOpts).Extract() th.AssertNoErr(t, err) 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() th.AssertNoErr(t, err) tools.PrintResource(t, newPool) // Member member, err := CreateMember(t, lbClient, lb, newPool, subnet.ID, subnet.CIDR) th.AssertNoErr(t, err) newWeight := tools.RandomInt(11, 100) updateMemberOpts := pools.UpdateMemberOpts{ Weight: &newWeight, } _, err = pools.UpdateMember(lbClient, pool.ID, member.ID, updateMemberOpts).Extract() th.AssertNoErr(t, err) 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() th.AssertNoErr(t, err) tools.PrintResource(t, newMember) // Monitor monitor, err := CreateMonitor(t, lbClient, lb, newPool) th.AssertNoErr(t, err) newDelay := tools.RandomInt(20, 30) newMaxRetriesDown := tools.RandomInt(4, 10) updateMonitorOpts := monitors.UpdateOpts{ Delay: newDelay, MaxRetriesDown: newMaxRetriesDown, } _, err = monitors.Update(lbClient, monitor.ID, updateMonitorOpts).Extract() th.AssertNoErr(t, err) 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() th.AssertNoErr(t, err) tools.PrintResource(t, newMonitor) th.AssertEquals(t, newMonitor.Delay, newDelay) th.AssertEquals(t, newMonitor.MaxRetriesDown, newMaxRetriesDown) } golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/loadbalancer/v2/monitors_test.go000066400000000000000000000017731367513235700334420ustar00rootroot00000000000000// +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) { clients.SkipRelease(t, "stable/mitaka") clients.SkipRelease(t, "stable/newton") clients.SkipRelease(t, "stable/ocata") clients.SkipRelease(t, "stable/pike") clients.SkipRelease(t, "stable/queens") clients.SkipRelease(t, "stable/rocky") 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) } } golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/loadbalancer/v2/pkg.go000066400000000000000000000000131367513235700312740ustar00rootroot00000000000000package v2 golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/loadbalancer/v2/pools_test.go000066400000000000000000000017271367513235700327230ustar00rootroot00000000000000// +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) { clients.SkipRelease(t, "stable/mitaka") clients.SkipRelease(t, "stable/newton") clients.SkipRelease(t, "stable/ocata") clients.SkipRelease(t, "stable/pike") clients.SkipRelease(t, "stable/queens") clients.SkipRelease(t, "stable/rocky") 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.12.0/acceptance/openstack/loadbalancer/v2/providers_test.go000066400000000000000000000020071367513235700335740ustar00rootroot00000000000000// +build acceptance networking loadbalancer providers package v2 import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/providers" ) func TestProvidersList(t *testing.T) { clients.SkipRelease(t, "stable/mitaka") clients.SkipRelease(t, "stable/newton") clients.SkipRelease(t, "stable/ocata") clients.SkipRelease(t, "stable/pike") clients.SkipRelease(t, "stable/queens") clients.SkipRelease(t, "stable/rocky") client, err := clients.NewLoadBalancerV2Client() if err != nil { t.Fatalf("Unable to create a loadbalancer client: %v", err) } allPages, err := providers.List(client, nil).AllPages() if err != nil { t.Fatalf("Unable to list providers: %v", err) } allProviders, err := providers.ExtractProviders(allPages) if err != nil { t.Fatalf("Unable to extract providers: %v", err) } for _, provider := range allProviders { tools.PrintResource(t, provider) } } golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/messaging/000077500000000000000000000000001367513235700272115ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/messaging/v2/000077500000000000000000000000001367513235700275405ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/messaging/v2/claims_test.go000066400000000000000000000032571367513235700324050ustar00rootroot00000000000000// +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) } } golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/messaging/v2/message_test.go000066400000000000000000000206771367513235700325660ustar00rootroot00000000000000// +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) } } } golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/messaging/v2/messaging.go000066400000000000000000000121741367513235700320510ustar00rootroot00000000000000package 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 } golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/messaging/v2/queue_test.go000066400000000000000000000077411367513235700322630ustar00rootroot00000000000000// +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.12.0/acceptance/openstack/networking/000077500000000000000000000000001367513235700274235ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/networking/v2/000077500000000000000000000000001367513235700277525ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/networking/v2/apiversion_test.go000066400000000000000000000025221367513235700335200ustar00rootroot00000000000000// +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 resources: %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) } } golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/networking/v2/extension_test.go000066400000000000000000000021221367513235700333510ustar00rootroot00000000000000// +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) } golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/networking/v2/extensions/000077500000000000000000000000001367513235700321515ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/networking/v2/extensions/agents/000077500000000000000000000000001367513235700334325ustar00rootroot00000000000000agents_test.go000066400000000000000000000053701367513235700362270ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/networking/v2/extensions/agents// +build acceptance networking agents package agents 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/agents" th "github.com/gophercloud/gophercloud/testhelper" ) func TestAgentsRUD(t *testing.T) { clients.RequireAdmin(t) client, err := clients.NewNetworkV2Client() th.AssertNoErr(t, err) allPages, err := agents.List(client, agents.ListOpts{}).AllPages() th.AssertNoErr(t, err) allAgents, err := agents.ExtractAgents(allPages) th.AssertNoErr(t, err) t.Logf("Retrieved Networking V2 agents") tools.PrintResource(t, allAgents) // List DHCP agents listOpts := &agents.ListOpts{ AgentType: "DHCP agent", } allPages, err = agents.List(client, listOpts).AllPages() th.AssertNoErr(t, err) allAgents, err = agents.ExtractAgents(allPages) th.AssertNoErr(t, err) t.Logf("Retrieved Networking V2 DHCP agents") tools.PrintResource(t, allAgents) // List DHCP agent networks for _, agent := range allAgents { t.Logf("Retrieving DHCP networks from the agent: %s", agent.ID) networks, err := agents.ListDHCPNetworks(client, agent.ID).Extract() th.AssertNoErr(t, err) for _, network := range networks { t.Logf("Retrieved %q network, assigned to a %q DHCP agent", network.ID, agent.ID) } } // Get a single agent agent, err := agents.Get(client, allAgents[0].ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, agent) // Update an agent description := "updated agent" updateOpts := &agents.UpdateOpts{ Description: &description, } agent, err = agents.Update(client, allAgents[0].ID, updateOpts).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, agent.Description, description) // Restore original description agent, err = agents.Update(client, allAgents[0].ID, &agents.UpdateOpts{Description: &allAgents[0].Description}).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, agent.Description, allAgents[0].Description) // skip this part // t.Skip("Skip DHCP agent network scheduling") // Assign a new network to a DHCP agent network, err := networking.CreateNetwork(t, client) th.AssertNoErr(t, err) defer networking.DeleteNetwork(t, client, network.ID) opts := &agents.ScheduleDHCPNetworkOpts{ NetworkID: network.ID, } err = agents.ScheduleDHCPNetwork(client, allAgents[0].ID, opts).ExtractErr() th.AssertNoErr(t, err) err = agents.RemoveDHCPNetwork(client, allAgents[0].ID, network.ID).ExtractErr() th.AssertNoErr(t, err) // skip this part t.Skip("Skip DHCP agent deletion") // Delete a DHCP agent err = agents.Delete(client, allAgents[0].ID).ExtractErr() th.AssertNoErr(t, err) } doc.go000066400000000000000000000000521367513235700344440ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/networking/v2/extensions/agents// agents acceptance tests package agents attributestags_test.go000066400000000000000000000110441367513235700365250ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/networking/v2/extensions// +build acceptance networking tags package extensions import ( "fmt" "sort" "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/attributestags" "github.com/gophercloud/gophercloud/openstack/networking/v2/networks" th "github.com/gophercloud/gophercloud/testhelper" ) func createNetworkWithTags(t *testing.T, client *gophercloud.ServiceClient, tags []string) (network *networks.Network) { // Create Network network, err := networking.CreateNetwork(t, client) th.AssertNoErr(t, err) tagReplaceAllOpts := attributestags.ReplaceAllOpts{ // docs say list of tags, but it's a set e.g no duplicates Tags: tags, } rtags, err := attributestags.ReplaceAll(client, "networks", network.ID, tagReplaceAllOpts).Extract() th.AssertNoErr(t, err) sort.Strings(rtags) // Ensure ordering, older OpenStack versions aren't sorted... th.AssertDeepEquals(t, rtags, tags) // Verify the tags are also set in the object Get response gnetwork, err := networks.Get(client, network.ID).Extract() th.AssertNoErr(t, err) rtags = gnetwork.Tags sort.Strings(rtags) th.AssertDeepEquals(t, rtags, tags) return network } func TestTags(t *testing.T) { client, err := clients.NewNetworkV2Client() th.AssertNoErr(t, err) // Create Network network := createNetworkWithTags(t, client, []string{"a", "b", "c"}) defer networking.DeleteNetwork(t, client, network.ID) // Add a tag err = attributestags.Add(client, "networks", network.ID, "d").ExtractErr() th.AssertNoErr(t, err) // Delete a tag err = attributestags.Delete(client, "networks", network.ID, "a").ExtractErr() th.AssertNoErr(t, err) // Verify expected tags are set in the List response tags, err := attributestags.List(client, "networks", network.ID).Extract() th.AssertNoErr(t, err) sort.Strings(tags) th.AssertDeepEquals(t, []string{"b", "c", "d"}, tags) // Confirm tags exist/don't exist exists, err := attributestags.Confirm(client, "networks", network.ID, "d").Extract() th.AssertNoErr(t, err) th.AssertEquals(t, true, exists) noexists, err := attributestags.Confirm(client, "networks", network.ID, "a").Extract() th.AssertEquals(t, false, noexists) // Delete all tags err = attributestags.DeleteAll(client, "networks", network.ID).ExtractErr() th.AssertNoErr(t, err) tags, err = attributestags.List(client, "networks", network.ID).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, 0, len(tags)) } func listNetworkWithTagOpts(t *testing.T, client *gophercloud.ServiceClient, listOpts networks.ListOpts) (ids []string) { allPages, err := networks.List(client, listOpts).AllPages() th.AssertNoErr(t, err) allNetworks, err := networks.ExtractNetworks(allPages) th.AssertNoErr(t, err) for _, network := range allNetworks { ids = append(ids, network.ID) } return ids } func TestQueryByTags(t *testing.T) { clients.SkipRelease(t, "stable/mitaka") client, err := clients.NewNetworkV2Client() th.AssertNoErr(t, err) // Create a random tag to ensure we only get networks created // by this test testtag := tools.RandomString("zzz-tag-", 8) // Create Networks network1 := createNetworkWithTags( t, client, []string{"a", "b", "c", testtag}) defer networking.DeleteNetwork(t, client, network1.ID) network2 := createNetworkWithTags( t, client, []string{"b", "c", "d", testtag}) defer networking.DeleteNetwork(t, client, network2.ID) // Tags - Networks that match all tags will be returned listOpts := networks.ListOpts{ Tags: fmt.Sprintf("a,b,c,%s", testtag)} ids := listNetworkWithTagOpts(t, client, listOpts) th.AssertDeepEquals(t, []string{network1.ID}, ids) // TagsAny - Networks that match any tag will be returned listOpts = networks.ListOpts{ SortKey: "id", SortDir: "asc", TagsAny: fmt.Sprintf("a,b,c,%s", testtag)} ids = listNetworkWithTagOpts(t, client, listOpts) expected_ids := []string{network1.ID, network2.ID} sort.Strings(expected_ids) th.AssertDeepEquals(t, expected_ids, ids) // NotTags - Networks that match all tags will be excluded listOpts = networks.ListOpts{Tags: testtag, NotTags: "a,b,c"} ids = listNetworkWithTagOpts(t, client, listOpts) th.AssertDeepEquals(t, []string{network2.ID}, ids) // NotTagsAny - Networks that match any tag will be excluded. listOpts = networks.ListOpts{Tags: testtag, NotTagsAny: "d"} ids = listNetworkWithTagOpts(t, client, listOpts) th.AssertDeepEquals(t, []string{network1.ID}, ids) } golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/networking/v2/extensions/dns/000077500000000000000000000000001367513235700327355ustar00rootroot00000000000000dns.go000066400000000000000000000077671367513235700340120ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/networking/v2/extensions/dnspackage dns import ( "testing" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/dns" "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/ports" th "github.com/gophercloud/gophercloud/testhelper" ) // PortWithDNSExt represents a port with the DNS fields type PortWithDNSExt struct { ports.Port dns.PortDNSExt } // FloatingIPWithDNSExt represents a floating IP with the DNS fields type FloatingIPWithDNSExt struct { floatingips.FloatingIP dns.FloatingIPDNSExt } // NetworkWithDNSExt represents a network with the DNS fields type NetworkWithDNSExt struct { networks.Network dns.NetworkDNSExt } // CreatePortDNS will create a port with a DNS name on the specified subnet. An // error will be returned if the port could not be created. func CreatePortDNS(t *testing.T, client *gophercloud.ServiceClient, networkID, subnetID, dnsName string) (*PortWithDNSExt, error) { portName := tools.RandomString("TESTACC-", 8) portDescription := tools.RandomString("TESTACC-PORT-DESC-", 8) iFalse := true t.Logf("Attempting to create port: %s", portName) portCreateOpts := ports.CreateOpts{ NetworkID: networkID, Name: portName, Description: portDescription, AdminStateUp: &iFalse, FixedIPs: []ports.IP{ports.IP{SubnetID: subnetID}}, } createOpts := dns.PortCreateOptsExt{ CreateOptsBuilder: portCreateOpts, DNSName: dnsName, } var port PortWithDNSExt err := ports.Create(client, createOpts).ExtractInto(&port) if err != nil { return &port, err } t.Logf("Successfully created port: %s", portName) th.AssertEquals(t, port.Name, portName) th.AssertEquals(t, port.Description, portDescription) th.AssertEquals(t, port.DNSName, dnsName) return &port, nil } // CreateFloatingIPDNS creates a floating IP with the DNS extension on a given // network and port. An error will be returned if the creation failed. func CreateFloatingIPDNS(t *testing.T, client *gophercloud.ServiceClient, networkID, portID, dnsName, dnsDomain string) (*FloatingIPWithDNSExt, error) { t.Logf("Attempting to create floating IP on port: %s", portID) fipDescription := "Test floating IP" fipCreateOpts := &floatingips.CreateOpts{ Description: fipDescription, FloatingNetworkID: networkID, PortID: portID, } createOpts := dns.FloatingIPCreateOptsExt{ CreateOptsBuilder: fipCreateOpts, DNSName: dnsName, DNSDomain: dnsDomain, } var floatingIP FloatingIPWithDNSExt err := floatingips.Create(client, createOpts).ExtractInto(&floatingIP) if err != nil { return &floatingIP, err } t.Logf("Created floating IP.") th.AssertEquals(t, floatingIP.Description, fipDescription) th.AssertEquals(t, floatingIP.FloatingNetworkID, networkID) th.AssertEquals(t, floatingIP.PortID, portID) th.AssertEquals(t, floatingIP.DNSName, dnsName) th.AssertEquals(t, floatingIP.DNSDomain, dnsDomain) return &floatingIP, err } // CreateNetworkDNS will create a network with a DNS domain set. // An error will be returned if the network could not be created. func CreateNetworkDNS(t *testing.T, client *gophercloud.ServiceClient, dnsDomanin string) (*NetworkWithDNSExt, error) { networkName := tools.RandomString("TESTACC-", 8) networkCreateOpts := networks.CreateOpts{ Name: networkName, AdminStateUp: gophercloud.Enabled, } createOpts := dns.NetworkCreateOptsExt{ CreateOptsBuilder: networkCreateOpts, DNSDomain: dnsDomanin, } t.Logf("Attempting to create network: %s", networkName) var network NetworkWithDNSExt err := networks.Create(client, createOpts).ExtractInto(&network) if err != nil { return &network, err } t.Logf("Successfully created network.") th.AssertEquals(t, network.Name, networkName) th.AssertEquals(t, network.DNSDomain, dnsDomanin) return &network, nil } dns_test.go000066400000000000000000000172621367513235700350400ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/networking/v2/extensions/dns// +build acceptance networking package dns import ( "os" "testing" "github.com/gophercloud/gophercloud/acceptance/clients" networking "github.com/gophercloud/gophercloud/acceptance/openstack/networking/v2" "github.com/gophercloud/gophercloud/acceptance/openstack/networking/v2/extensions/layer3" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/common/extensions" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/dns" "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/ports" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" ) func TestDNSPortCRUDL(t *testing.T) { clients.RequireAdmin(t) client, err := clients.NewNetworkV2Client() th.AssertNoErr(t, err) extension, err := extensions.Get(client, "dns-integration").Extract() if err != nil { t.Skip("This test requires dns-integration Neutron extension") } tools.PrintResource(t, extension) // Create Network networkDNSDomain := "local." network, err := CreateNetworkDNS(t, client, networkDNSDomain) th.AssertNoErr(t, err) defer networking.DeleteNetwork(t, client, network.ID) // Create Subnet subnet, err := networking.CreateSubnet(t, client, network.ID) th.AssertNoErr(t, err) defer networking.DeleteSubnet(t, client, subnet.ID) // Create port portDNSName := "port" port, err := CreatePortDNS(t, client, network.ID, subnet.ID, portDNSName) th.AssertNoErr(t, err) defer networking.DeletePort(t, client, port.ID) tools.PrintResource(t, port) if os.Getenv("OS_BRANCH") == "stable/mitaka" { // List port successfully var listOpts ports.ListOptsBuilder listOpts = dns.PortListOptsExt{ ListOptsBuilder: ports.ListOpts{}, DNSName: portDNSName, } var listedPorts []PortWithDNSExt i := 0 err = ports.List(client, listOpts).EachPage(func(page pagination.Page) (bool, error) { i++ err := ports.ExtractPortsInto(page, &listedPorts) if err != nil { t.Errorf("Failed to extract ports: %v", err) return false, err } tools.PrintResource(t, listedPorts) th.AssertEquals(t, 1, len(listedPorts)) th.CheckDeepEquals(t, *port, listedPorts[0]) return true, nil }) th.AssertNoErr(t, err) th.AssertEquals(t, 1, i) // List port unsuccessfully listOpts = dns.PortListOptsExt{ ListOptsBuilder: ports.ListOpts{}, DNSName: "foo", } i = 0 err = ports.List(client, listOpts).EachPage(func(page pagination.Page) (bool, error) { i++ err := ports.ExtractPortsInto(page, &listedPorts) if err != nil { t.Errorf("Failed to extract ports: %v", err) return false, err } tools.PrintResource(t, listedPorts) th.AssertEquals(t, 1, len(listedPorts)) th.CheckDeepEquals(t, *port, listedPorts[0]) return true, nil }) th.AssertNoErr(t, err) th.AssertEquals(t, 0, i) } // Get port var getPort PortWithDNSExt err = ports.Get(client, port.ID).ExtractInto(&getPort) th.AssertNoErr(t, err) tools.PrintResource(t, getPort) th.AssertDeepEquals(t, *port, getPort) // Update port newPortName := "" newPortDescription := "" newDNSName := "" portUpdateOpts := ports.UpdateOpts{ Name: &newPortName, Description: &newPortDescription, } var updateOpts ports.UpdateOptsBuilder updateOpts = dns.PortUpdateOptsExt{ UpdateOptsBuilder: portUpdateOpts, DNSName: &newDNSName, } var newPort PortWithDNSExt err = ports.Update(client, port.ID, updateOpts).ExtractInto(&newPort) th.AssertNoErr(t, err) tools.PrintResource(t, newPort) th.AssertEquals(t, newPort.Description, newPortName) th.AssertEquals(t, newPort.Description, newPortDescription) th.AssertEquals(t, newPort.DNSName, newDNSName) // Get updated port var getNewPort PortWithDNSExt err = ports.Get(client, port.ID).ExtractInto(&getNewPort) th.AssertNoErr(t, err) tools.PrintResource(t, getNewPort) // workaround for update race condition newPort.DNSAssignment = nil getNewPort.DNSAssignment = nil th.AssertDeepEquals(t, newPort, getNewPort) } func TestDNSFloatingIPCRDL(t *testing.T) { clients.RequireAdmin(t) client, err := clients.NewNetworkV2Client() th.AssertNoErr(t, err) extension, err := extensions.Get(client, "dns-integration").Extract() if err != nil { t.Skip("This test requires dns-integration Neutron extension") } tools.PrintResource(t, extension) choices, err := clients.AcceptanceTestChoicesFromEnv() th.AssertNoErr(t, err) // Create Network networkDNSDomain := "local." network, err := CreateNetworkDNS(t, client, networkDNSDomain) th.AssertNoErr(t, err) defer networking.DeleteNetwork(t, client, network.ID) // Create Subnet subnet, err := networking.CreateSubnet(t, client, network.ID) th.AssertNoErr(t, err) defer networking.DeleteSubnet(t, client, subnet.ID) // Create Router router, err := layer3.CreateExternalRouter(t, client) th.AssertNoErr(t, err) defer layer3.DeleteRouter(t, client, router.ID) // Create router interface routerPort, err := networking.CreatePort(t, client, network.ID, subnet.ID) th.AssertNoErr(t, err) _, err = layer3.CreateRouterInterface(t, client, routerPort.ID, router.ID) th.AssertNoErr(t, err) defer layer3.DeleteRouterInterface(t, client, routerPort.ID, router.ID) // Create port portDNSName := "port" port, err := CreatePortDNS(t, client, network.ID, subnet.ID, portDNSName) th.AssertNoErr(t, err) defer networking.DeletePort(t, client, port.ID) tools.PrintResource(t, port) // Create floating IP fipDNSName := "fip" fipDNSDomain := "local." fip, err := CreateFloatingIPDNS(t, client, choices.ExternalNetworkID, port.ID, fipDNSName, fipDNSDomain) th.AssertNoErr(t, err) defer layer3.DeleteFloatingIP(t, client, fip.ID) // Get floating IP var getFip FloatingIPWithDNSExt err = floatingips.Get(client, fip.ID).ExtractInto(&getFip) th.AssertNoErr(t, err) tools.PrintResource(t, getFip) th.AssertDeepEquals(t, *fip, getFip) } func TestDNSNetwork(t *testing.T) { clients.RequireAdmin(t) client, err := clients.NewNetworkV2Client() th.AssertNoErr(t, err) extension, err := extensions.Get(client, "dns-integration").Extract() if err != nil { t.Skip("This test requires dns-integration Neutron extension") } tools.PrintResource(t, extension) // Create Network networkDNSDomain := "local." network, err := CreateNetworkDNS(t, client, networkDNSDomain) th.AssertNoErr(t, err) defer networking.DeleteNetwork(t, client, network.ID) // Get network var getNetwork NetworkWithDNSExt err = networks.Get(client, network.ID).ExtractInto(&getNetwork) th.AssertNoErr(t, err) tools.PrintResource(t, getNetwork) th.AssertDeepEquals(t, *network, getNetwork) // Update network newNetworkName := "" newNetworkDescription := "" newNetworkDNSDomain := "" networkUpdateOpts := networks.UpdateOpts{ Name: &newNetworkName, Description: &newNetworkDescription, } var updateOpts networks.UpdateOptsBuilder updateOpts = dns.NetworkUpdateOptsExt{ UpdateOptsBuilder: networkUpdateOpts, DNSDomain: &newNetworkDNSDomain, } var newNetwork NetworkWithDNSExt err = networks.Update(client, network.ID, updateOpts).ExtractInto(&newNetwork) th.AssertNoErr(t, err) tools.PrintResource(t, newNetwork) th.AssertEquals(t, newNetwork.Description, newNetworkName) th.AssertEquals(t, newNetwork.Description, newNetworkDescription) th.AssertEquals(t, newNetwork.DNSDomain, newNetworkDNSDomain) // Get updated network var getNewNetwork NetworkWithDNSExt err = networks.Get(client, network.ID).ExtractInto(&getNewNetwork) th.AssertNoErr(t, err) tools.PrintResource(t, getNewNetwork) th.AssertDeepEquals(t, newNetwork, getNewNetwork) } extensions.go000066400000000000000000000123471367513235700346270ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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" th "github.com/gophercloud/gophercloud/testhelper" ) // 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) networkDescription := tools.RandomString("TESTACC-DESC-", 8) t.Logf("Attempting to create external network: %s", networkName) adminStateUp := true isExternal := true networkCreateOpts := networks.CreateOpts{ Name: networkName, Description: networkDescription, 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) th.AssertEquals(t, network.Name, networkName) th.AssertEquals(t, network.Description, networkDescription) 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) portDescription := tools.RandomString("TESTACC-DESC-", 8) iFalse := false t.Logf("Attempting to create port: %s", portName) createOpts := ports.CreateOpts{ NetworkID: networkID, Name: portName, Description: portDescription, 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) th.AssertEquals(t, port.Name, portName) th.AssertEquals(t, port.Description, portDescription) th.AssertEquals(t, port.NetworkID, networkID) 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) secGroupDescription := tools.RandomString("TESTACC-DESC-", 8) t.Logf("Attempting to create security group: %s", secGroupName) createOpts := groups.CreateOpts{ Name: secGroupName, Description: secGroupDescription, } secGroup, err := groups.Create(client, createOpts).Extract() if err != nil { return secGroup, err } t.Logf("Created security group: %s", secGroup.ID) th.AssertEquals(t, secGroup.Name, secGroupName) th.AssertEquals(t, secGroup.Description, secGroupDescription) 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) description := "Rule description" fromPort := tools.RandomInt(80, 89) toPort := tools.RandomInt(90, 99) createOpts := rules.CreateOpts{ Description: description, 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) th.AssertEquals(t, rule.SecGroupID, secGroupID) th.AssertEquals(t, rule.Description, description) 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) } } golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/networking/v2/extensions/fwaas/000077500000000000000000000000001367513235700332525ustar00rootroot00000000000000firewall_test.go000066400000000000000000000106671367513235700364000ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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" th "github.com/gophercloud/gophercloud/testhelper" ) func TestFirewallCRUD(t *testing.T) { client, err := clients.NewNetworkV2Client() th.AssertNoErr(t, err) router, err := layer3.CreateExternalRouter(t, client) th.AssertNoErr(t, err) defer layer3.DeleteRouter(t, client, router.ID) rule, err := CreateRule(t, client) th.AssertNoErr(t, err) defer DeleteRule(t, client, rule.ID) tools.PrintResource(t, rule) policy, err := CreatePolicy(t, client, rule.ID) th.AssertNoErr(t, err) defer DeletePolicy(t, client, policy.ID) tools.PrintResource(t, policy) firewall, err := CreateFirewall(t, client, policy.ID) th.AssertNoErr(t, err) defer DeleteFirewall(t, client, firewall.ID) tools.PrintResource(t, firewall) fwName := "" fwDescription := "" fwUpdateOpts := firewalls.UpdateOpts{ Name: &fwName, Description: &fwDescription, PolicyID: policy.ID, } _, err = firewalls.Update(client, firewall.ID, fwUpdateOpts).Extract() th.AssertNoErr(t, err) newFirewall, err := firewalls.Get(client, firewall.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, newFirewall) th.AssertEquals(t, newFirewall.Name, fwName) th.AssertEquals(t, newFirewall.Description, fwDescription) th.AssertEquals(t, newFirewall.PolicyID, policy.ID) allPages, err := firewalls.List(client, nil).AllPages() th.AssertNoErr(t, err) allFirewalls, err := firewalls.ExtractFirewalls(allPages) th.AssertNoErr(t, err) var found bool for _, firewall := range allFirewalls { if firewall.ID == newFirewall.ID { found = true } } th.AssertEquals(t, found, true) } func TestFirewallCRUDRouter(t *testing.T) { client, err := clients.NewNetworkV2Client() th.AssertNoErr(t, err) router, err := layer3.CreateExternalRouter(t, client) th.AssertNoErr(t, err) defer layer3.DeleteRouter(t, client, router.ID) rule, err := CreateRule(t, client) th.AssertNoErr(t, err) defer DeleteRule(t, client, rule.ID) tools.PrintResource(t, rule) policy, err := CreatePolicy(t, client, rule.ID) th.AssertNoErr(t, err) defer DeletePolicy(t, client, policy.ID) tools.PrintResource(t, policy) firewall, err := CreateFirewallOnRouter(t, client, policy.ID, router.ID) th.AssertNoErr(t, err) defer DeleteFirewall(t, client, firewall.ID) tools.PrintResource(t, firewall) router2, err := layer3.CreateExternalRouter(t, client) th.AssertNoErr(t, err) defer layer3.DeleteRouter(t, client, router2.ID) description := "Some firewall description" firewallUpdateOpts := firewalls.UpdateOpts{ PolicyID: policy.ID, Description: &description, } updateOpts := routerinsertion.UpdateOptsExt{ firewallUpdateOpts, []string{router2.ID}, } _, err = firewalls.Update(client, firewall.ID, updateOpts).Extract() th.AssertNoErr(t, err) newFirewall, err := firewalls.Get(client, firewall.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, newFirewall) } func TestFirewallCRUDRemoveRouter(t *testing.T) { client, err := clients.NewNetworkV2Client() th.AssertNoErr(t, err) router, err := layer3.CreateExternalRouter(t, client) th.AssertNoErr(t, err) defer layer3.DeleteRouter(t, client, router.ID) rule, err := CreateRule(t, client) th.AssertNoErr(t, err) defer DeleteRule(t, client, rule.ID) tools.PrintResource(t, rule) policy, err := CreatePolicy(t, client, rule.ID) th.AssertNoErr(t, err) defer DeletePolicy(t, client, policy.ID) tools.PrintResource(t, policy) firewall, err := CreateFirewallOnRouter(t, client, policy.ID, router.ID) th.AssertNoErr(t, err) defer DeleteFirewall(t, client, firewall.ID) tools.PrintResource(t, firewall) description := "Some firewall description" firewallUpdateOpts := firewalls.UpdateOpts{ PolicyID: policy.ID, Description: &description, } updateOpts := routerinsertion.UpdateOptsExt{ firewallUpdateOpts, []string{}, } _, err = firewalls.Update(client, firewall.ID, updateOpts).Extract() th.AssertNoErr(t, err) newFirewall, err := firewalls.Get(client, firewall.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, newFirewall) } fwaas.go000066400000000000000000000166111367513235700346300ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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" th "github.com/gophercloud/gophercloud/testhelper" ) // 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) firewallDescription := tools.RandomString("TESTACC-DESC-", 8) t.Logf("Attempting to create firewall %s", firewallName) iTrue := true createOpts := firewalls.CreateOpts{ Name: firewallName, Description: firewallDescription, 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) th.AssertEquals(t, firewall.Name, firewallName) th.AssertEquals(t, firewall.Description, firewallDescription) 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) firewallDescription := tools.RandomString("TESTACC-DESC-", 8) t.Logf("Attempting to create firewall %s", firewallName) firewallCreateOpts := firewalls.CreateOpts{ Name: firewallName, Description: firewallDescription, 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) th.AssertEquals(t, firewall.Name, firewallName) th.AssertEquals(t, firewall.Description, firewallDescription) 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) policyDescription := tools.RandomString("TESTACC-DESC-", 8) t.Logf("Attempting to create policy %s", policyName) createOpts := policies.CreateOpts{ Name: policyName, Description: policyDescription, Rules: []string{ ruleID, }, } policy, err := policies.Create(client, createOpts).Extract() if err != nil { return policy, err } t.Logf("Successfully created policy %s", policyName) th.AssertEquals(t, policy.Name, policyName) th.AssertEquals(t, policy.Description, policyDescription) th.AssertEquals(t, len(policy.Rules), 1) 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) th.AssertEquals(t, rule.Name, ruleName) th.AssertEquals(t, rule.Protocol, rules.ProtocolTCP) th.AssertEquals(t, rule.SourceIPAddress, sourceAddress) th.AssertEquals(t, rule.SourcePort, sourcePort) th.AssertEquals(t, rule.DestinationIPAddress, destinationAddress) th.AssertEquals(t, rule.DestinationPort, destinationPort) 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 }) } policy_test.go000066400000000000000000000026611367513235700360650ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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" th "github.com/gophercloud/gophercloud/testhelper" ) func TestPolicyCRUD(t *testing.T) { client, err := clients.NewNetworkV2Client() th.AssertNoErr(t, err) rule, err := CreateRule(t, client) th.AssertNoErr(t, err) defer DeleteRule(t, client, rule.ID) tools.PrintResource(t, rule) policy, err := CreatePolicy(t, client, rule.ID) th.AssertNoErr(t, err) defer DeletePolicy(t, client, policy.ID) tools.PrintResource(t, policy) name := "" description := "" updateOpts := policies.UpdateOpts{ Name: &name, Description: &description, } _, err = policies.Update(client, policy.ID, updateOpts).Extract() th.AssertNoErr(t, err) newPolicy, err := policies.Get(client, policy.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, newPolicy) th.AssertEquals(t, newPolicy.Name, name) th.AssertEquals(t, newPolicy.Description, description) allPages, err := policies.List(client, nil).AllPages() th.AssertNoErr(t, err) allPolicies, err := policies.ExtractPolicies(allPages) th.AssertNoErr(t, err) var found bool for _, policy := range allPolicies { if policy.ID == newPolicy.ID { found = true } } th.AssertEquals(t, found, true) } rule_test.go000066400000000000000000000022121367513235700355250ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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" th "github.com/gophercloud/gophercloud/testhelper" ) func TestRuleCRUD(t *testing.T) { client, err := clients.NewNetworkV2Client() th.AssertNoErr(t, err) rule, err := CreateRule(t, client) th.AssertNoErr(t, 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() th.AssertNoErr(t, err) newRule, err := rules.Get(client, rule.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, newRule) allPages, err := rules.List(client, nil).AllPages() th.AssertNoErr(t, err) allRules, err := rules.ExtractRules(allPages) th.AssertNoErr(t, err) var found bool for _, rule := range allRules { if rule.ID == newRule.ID { found = true } } th.AssertEquals(t, found, true) } golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/networking/v2/extensions/fwaas_v2/000077500000000000000000000000001367513235700336615ustar00rootroot00000000000000fwaas_v2.go000066400000000000000000000045071367513235700356470ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/networking/v2/extensions/fwaas_v2package fwaas_v2 import ( "fmt" "strconv" "testing" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/fwaas_v2/rules" th "github.com/gophercloud/gophercloud/testhelper" ) // 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: rules.ActionAllow, 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) th.AssertEquals(t, rule.Name, ruleName) th.AssertEquals(t, rule.Protocol, string(rules.ProtocolTCP)) th.AssertEquals(t, rule.Action, string(rules.ActionAllow)) th.AssertEquals(t, rule.SourceIPAddress, sourceAddress) th.AssertEquals(t, rule.SourcePort, sourcePort) th.AssertEquals(t, rule.DestinationIPAddress, destinationAddress) th.AssertEquals(t, rule.DestinationPort, destinationPort) return rule, nil } // 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) } rule_test.go000066400000000000000000000026741367513235700361500ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/networking/v2/extensions/fwaas_v2// +build acceptance networking fwaas_v2 package fwaas_v2 import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/fwaas_v2/rules" th "github.com/gophercloud/gophercloud/testhelper" ) func TestRuleCRUD(t *testing.T) { client, err := clients.NewNetworkV2Client() th.AssertNoErr(t, err) rule, err := CreateRule(t, client) th.AssertNoErr(t, err) defer DeleteRule(t, client, rule.ID) tools.PrintResource(t, rule) ruleDescription := "Some rule description" ruleProtocol := rules.ProtocolICMP updateOpts := rules.UpdateOpts{ Description: &ruleDescription, Protocol: &ruleProtocol, } ruleUpdated, err := rules.Update(client, rule.ID, updateOpts).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, ruleUpdated.Description, ruleDescription) th.AssertEquals(t, ruleUpdated.Protocol, string(ruleProtocol)) newRule, err := rules.Get(client, rule.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, newRule) allPages, err := rules.List(client, nil).AllPages() th.AssertNoErr(t, err) allRules, err := rules.ExtractRules(allPages) th.AssertNoErr(t, err) t.Logf("Attempting to find rule %s\n", newRule.ID) var found bool for _, rule := range allRules { if rule.ID == newRule.ID { found = true t.Logf("Found rule %s\n", newRule.ID) } } th.AssertEquals(t, found, true) } golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/networking/v2/extensions/layer3/000077500000000000000000000000001367513235700333505ustar00rootroot00000000000000addressscopes_test.go000066400000000000000000000026171367513235700375270ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/networking/v2/extensions/layer3// +build acceptance networking layer3 addressscopes package layer3 import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/layer3/addressscopes" th "github.com/gophercloud/gophercloud/testhelper" ) func TestAddressScopesCRUD(t *testing.T) { client, err := clients.NewNetworkV2Client() th.AssertNoErr(t, err) // Create an address-scope addressScope, err := CreateAddressScope(t, client) th.AssertNoErr(t, err) defer DeleteAddressScope(t, client, addressScope.ID) tools.PrintResource(t, addressScope) newName := tools.RandomString("TESTACC-", 8) updateOpts := &addressscopes.UpdateOpts{ Name: &newName, } _, err = addressscopes.Update(client, addressScope.ID, updateOpts).Extract() th.AssertNoErr(t, err) newAddressScope, err := addressscopes.Get(client, addressScope.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, newAddressScope) th.AssertEquals(t, newAddressScope.Name, newName) allPages, err := addressscopes.List(client, nil).AllPages() th.AssertNoErr(t, err) allAddressScopes, err := addressscopes.ExtractAddressScopes(allPages) th.AssertNoErr(t, err) var found bool for _, addressScope := range allAddressScopes { if addressScope.ID == newAddressScope.ID { found = true } } th.AssertEquals(t, found, true) } floatingips_test.go000066400000000000000000000130561367513235700372030ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/networking/v2/extensions/layer3// +build acceptance networking layer3 floatingips 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/floatingips" "github.com/gophercloud/gophercloud/openstack/networking/v2/subnets" th "github.com/gophercloud/gophercloud/testhelper" ) func TestLayer3FloatingIPsCreateDelete(t *testing.T) { client, err := clients.NewNetworkV2Client() th.AssertNoErr(t, err) choices, err := clients.AcceptanceTestChoicesFromEnv() th.AssertNoErr(t, err) fip, err := CreateFloatingIP(t, client, choices.ExternalNetworkID, "") th.AssertNoErr(t, err) defer DeleteFloatingIP(t, client, fip.ID) newFip, err := floatingips.Get(client, fip.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, newFip) allPages, err := floatingips.List(client, floatingips.ListOpts{}).AllPages() th.AssertNoErr(t, err) allFIPs, err := floatingips.ExtractFloatingIPs(allPages) th.AssertNoErr(t, err) var found bool for _, fip := range allFIPs { if fip.ID == newFip.ID { found = true } } th.AssertEquals(t, found, true) } func TestLayer3FloatingIPsExternalCreateDelete(t *testing.T) { clients.RequireAdmin(t) client, err := clients.NewNetworkV2Client() th.AssertNoErr(t, err) choices, err := clients.AcceptanceTestChoicesFromEnv() th.AssertNoErr(t, err) // Create Network network, err := networking.CreateNetwork(t, client) th.AssertNoErr(t, err) defer networking.DeleteNetwork(t, client, network.ID) subnet, err := networking.CreateSubnet(t, client, network.ID) th.AssertNoErr(t, err) defer networking.DeleteSubnet(t, client, subnet.ID) router, err := CreateExternalRouter(t, client) th.AssertNoErr(t, err) defer DeleteRouter(t, client, router.ID) port, err := networking.CreatePort(t, client, network.ID, subnet.ID) th.AssertNoErr(t, err) // not required, since "DeleteRouterInterface" below removes the port // defer networking.DeletePort(t, client, port.ID) _, err = CreateRouterInterface(t, client, port.ID, router.ID) th.AssertNoErr(t, err) defer DeleteRouterInterface(t, client, port.ID, router.ID) fip, err := CreateFloatingIP(t, client, choices.ExternalNetworkID, port.ID) th.AssertNoErr(t, err) defer DeleteFloatingIP(t, client, fip.ID) newFip, err := floatingips.Get(client, fip.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, newFip) // Disassociate the floating IP updateOpts := floatingips.UpdateOpts{ PortID: new(string), } _, err = floatingips.Update(client, fip.ID, updateOpts).Extract() th.AssertNoErr(t, err) newFip, err = floatingips.Get(client, fip.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, newFip) th.AssertEquals(t, newFip.PortID, "") } func TestLayer3FloatingIPsWithFixedIPsExternalCreateDelete(t *testing.T) { clients.RequireAdmin(t) client, err := clients.NewNetworkV2Client() th.AssertNoErr(t, err) choices, err := clients.AcceptanceTestChoicesFromEnv() th.AssertNoErr(t, err) // Create Network network, err := networking.CreateNetwork(t, client) th.AssertNoErr(t, err) defer networking.DeleteNetwork(t, client, network.ID) subnet, err := networking.CreateSubnet(t, client, network.ID) th.AssertNoErr(t, err) defer networking.DeleteSubnet(t, client, subnet.ID) router, err := CreateExternalRouter(t, client) th.AssertNoErr(t, err) defer DeleteRouter(t, client, router.ID) port, err := networking.CreatePortWithMultipleFixedIPs(t, client, network.ID, subnet.ID) th.AssertNoErr(t, err) defer networking.DeletePort(t, client, port.ID) var fixedIPs []string for _, fixedIP := range port.FixedIPs { fixedIPs = append(fixedIPs, fixedIP.IPAddress) } iface, err := CreateRouterInterfaceOnSubnet(t, client, subnet.ID, router.ID) th.AssertNoErr(t, err) defer DeleteRouterInterface(t, client, iface.PortID, router.ID) fip, err := CreateFloatingIPWithFixedIP(t, client, choices.ExternalNetworkID, port.ID, fixedIPs[0]) th.AssertNoErr(t, err) defer DeleteFloatingIP(t, client, fip.ID) newFip, err := floatingips.Get(client, fip.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, newFip) // Associate the floating IP with another fixed IP updateOpts := floatingips.UpdateOpts{ PortID: &port.ID, FixedIP: fixedIPs[1], } _, err = floatingips.Update(client, fip.ID, updateOpts).Extract() th.AssertNoErr(t, err) newFip, err = floatingips.Get(client, fip.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, newFip) th.AssertEquals(t, newFip.FixedIP, fixedIPs[1]) // Disassociate the floating IP updateOpts = floatingips.UpdateOpts{ PortID: new(string), } _, err = floatingips.Update(client, fip.ID, updateOpts).Extract() th.AssertNoErr(t, err) } func TestLayer3FloatingIPsCreateDeleteBySubnetID(t *testing.T) { clients.RequireAdmin(t) client, err := clients.NewNetworkV2Client() th.AssertNoErr(t, err) choices, err := clients.AcceptanceTestChoicesFromEnv() th.AssertNoErr(t, err) listOpts := subnets.ListOpts{ NetworkID: choices.ExternalNetworkID, IPVersion: 4, } subnetPages, err := subnets.List(client, listOpts).AllPages() th.AssertNoErr(t, err) allSubnets, err := subnets.ExtractSubnets(subnetPages) th.AssertNoErr(t, err) createOpts := floatingips.CreateOpts{ FloatingNetworkID: choices.ExternalNetworkID, SubnetID: allSubnets[0].ID, } fip, err := floatingips.Create(client, createOpts).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, fip) DeleteFloatingIP(t, client, fip.ID) } layer3.go000066400000000000000000000276221367513235700350300ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/networking/v2/extensions/layer3package layer3 import ( "testing" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/layer3/addressscopes" "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/portforwarding" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/layer3/routers" "github.com/gophercloud/gophercloud/openstack/networking/v2/ports" th "github.com/gophercloud/gophercloud/testhelper" ) // 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) fipDescription := "Test floating IP" createOpts := &floatingips.CreateOpts{ Description: fipDescription, FloatingNetworkID: networkID, PortID: portID, } floatingIP, err := floatingips.Create(client, createOpts).Extract() if err != nil { return floatingIP, err } t.Logf("Created floating IP.") th.AssertEquals(t, floatingIP.Description, fipDescription) return floatingIP, err } // CreateFloatingIPWithFixedIP creates a floating IP on a given network and port with a // defined fixed IP. An error will be returned if the creation failed. func CreateFloatingIPWithFixedIP(t *testing.T, client *gophercloud.ServiceClient, networkID, portID, fixedIP string) (*floatingips.FloatingIP, error) { t.Logf("Attempting to create floating IP on port: %s and address: %s", portID, fixedIP) fipDescription := "Test floating IP" createOpts := &floatingips.CreateOpts{ Description: fipDescription, FloatingNetworkID: networkID, PortID: portID, FixedIP: fixedIP, } floatingIP, err := floatingips.Create(client, createOpts).Extract() if err != nil { return floatingIP, err } t.Logf("Created floating IP.") th.AssertEquals(t, floatingIP.Description, fipDescription) th.AssertEquals(t, floatingIP.FixedIP, fixedIP) return floatingIP, err } // CreatePortForwarding creates a port forwarding for a given floating IP // and port. An error will be returned if the creation failed. func CreatePortForwarding(t *testing.T, client *gophercloud.ServiceClient, fipID string, portID string, portFixedIPs []ports.IP) (*portforwarding.PortForwarding, error) { t.Logf("Attempting to create Port forwarding for floating IP with ID: %s", fipID) fixedIP := portFixedIPs[0] internalIP := fixedIP.IPAddress createOpts := &portforwarding.CreateOpts{ Protocol: "tcp", InternalPort: 25, ExternalPort: 2230, InternalIPAddress: internalIP, InternalPortID: portID, } pf, err := portforwarding.Create(client, fipID, createOpts).Extract() if err != nil { return pf, err } t.Logf("Created Port Forwarding.") th.AssertEquals(t, pf.Protocol, "tcp") return pf, err } // DeletePortForwarding deletes a Port Forwarding with a given ID and a given floating IP ID. // A fatal error is returned if the deletion fails. Works best as a deferred function func DeletePortForwarding(t *testing.T, client *gophercloud.ServiceClient, fipID string, pfID string) { t.Logf("Attempting to delete the port forwarding with ID %s for floating IP with ID %s", pfID, fipID) err := portforwarding.Delete(client, fipID, pfID).ExtractErr() if err != nil { t.Fatalf("Failed to delete Port forwarding with ID %s for floating IP with ID %s", pfID, fipID) } t.Logf("Successfully deleted the port forwarding with ID %s for floating IP with ID %s", pfID, fipID) } // 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) routerDescription := tools.RandomString("TESTACC-DESC-", 8) t.Logf("Attempting to create external router: %s", routerName) adminStateUp := true gatewayInfo := routers.GatewayInfo{ NetworkID: choices.ExternalNetworkID, } createOpts := routers.CreateOpts{ Name: routerName, Description: routerDescription, 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) th.AssertEquals(t, router.Name, routerName) th.AssertEquals(t, router.Description, routerDescription) 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) routerDescription := tools.RandomString("TESTACC-DESC-", 8) t.Logf("Attempting to create router: %s", routerName) adminStateUp := true createOpts := routers.CreateOpts{ Name: routerName, Description: routerDescription, AdminStateUp: &adminStateUp, } 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) th.AssertEquals(t, router.Name, routerName) th.AssertEquals(t, router.Description, routerDescription) 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 } // CreateRouterInterfaceOnSubnet will attach a subnet to a router. An error will be // returned if the operation fails. func CreateRouterInterfaceOnSubnet(t *testing.T, client *gophercloud.ServiceClient, subnetID, routerID string) (*routers.InterfaceInfo, error) { t.Logf("Attempting to add subnet %s to router %s", subnetID, routerID) aiOpts := routers.AddInterfaceOpts{ SubnetID: subnetID, } iface, err := routers.AddInterface(client, routerID, aiOpts).Extract() if err != nil { return iface, err } if err := WaitForRouterInterfaceToAttach(client, iface.PortID, 60); err != nil { return iface, err } t.Logf("Successfully added subnet %s to router %s", subnetID, 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 }) } // CreateAddressScope will create an address-scope. An error will be returned if // the address-scope could not be created. func CreateAddressScope(t *testing.T, client *gophercloud.ServiceClient) (*addressscopes.AddressScope, error) { addressScopeName := tools.RandomString("TESTACC-", 8) createOpts := addressscopes.CreateOpts{ Name: addressScopeName, IPVersion: 4, } t.Logf("Attempting to create an address-scope: %s", addressScopeName) addressScope, err := addressscopes.Create(client, createOpts).Extract() if err != nil { return nil, err } t.Logf("Successfully created the addressscopes.") th.AssertEquals(t, addressScope.Name, addressScopeName) th.AssertEquals(t, addressScope.IPVersion, int(gophercloud.IPv4)) return addressScope, nil } // DeleteAddressScope will delete an address-scope with the specified ID. // A fatal error will occur if the delete was not successful. func DeleteAddressScope(t *testing.T, client *gophercloud.ServiceClient, addressScopeID string) { t.Logf("Attempting to delete the address-scope: %s", addressScopeID) err := addressscopes.Delete(client, addressScopeID).ExtractErr() if err != nil { t.Fatalf("Unable to delete address-scope %s: %v", addressScopeID, err) } t.Logf("Deleted address-scope: %s", addressScopeID) } portforwardings_test.go000066400000000000000000000051531367513235700401150ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/networking/v2/extensions/layer3package 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/floatingips" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/layer3/portforwarding" th "github.com/gophercloud/gophercloud/testhelper" ) func TestLayer3PortForwardingsCreateDelete(t *testing.T) { clients.RequirePortForwarding(t) client, err := clients.NewNetworkV2Client() th.AssertNoErr(t, err) choices, err := clients.AcceptanceTestChoicesFromEnv() th.AssertNoErr(t, err) // Create Network network, err := networking.CreateNetwork(t, client) th.AssertNoErr(t, err) defer networking.DeleteNetwork(t, client, network.ID) subnet, err := networking.CreateSubnet(t, client, network.ID) th.AssertNoErr(t, err) defer networking.DeleteSubnet(t, client, subnet.ID) router, err := CreateExternalRouter(t, client) th.AssertNoErr(t, err) defer DeleteRouter(t, client, router.ID) port, err := networking.CreatePort(t, client, network.ID, subnet.ID) th.AssertNoErr(t, err) // not required, since "DeleteRouterInterface" below removes the port // defer networking.DeletePort(t, client, port.ID) _, err = CreateRouterInterface(t, client, port.ID, router.ID) th.AssertNoErr(t, err) defer DeleteRouterInterface(t, client, port.ID, router.ID) fip, err := CreateFloatingIP(t, client, choices.ExternalNetworkID, "") th.AssertNoErr(t, err) defer DeleteFloatingIP(t, client, fip.ID) newFip, err := floatingips.Get(client, fip.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, newFip) pf, err := CreatePortForwarding(t, client, fip.ID, port.ID, port.FixedIPs) th.AssertNoErr(t, err) defer DeletePortForwarding(t, client, fip.ID, pf.ID) tools.PrintResource(t, pf) newPf, err := portforwarding.Get(client, fip.ID, pf.ID).Extract() th.AssertNoErr(t, err) updateOpts := portforwarding.UpdateOpts{ Protocol: "udp", InternalPort: 30, ExternalPort: 678, } _, err = portforwarding.Update(client, fip.ID, newPf.ID, updateOpts).Extract() th.AssertNoErr(t, err) newPf, err = portforwarding.Get(client, fip.ID, pf.ID).Extract() th.AssertNoErr(t, err) allPages, err := portforwarding.List(client, portforwarding.ListOpts{}, fip.ID).AllPages() th.AssertNoErr(t, err) allPFs, err := portforwarding.ExtractPortForwardings(allPages) th.AssertNoErr(t, err) var found bool for _, pf := range allPFs { if pf.ID == newPf.ID { found = true } } th.AssertEquals(t, true, found) } routers_test.go000066400000000000000000000104351367513235700363650ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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" th "github.com/gophercloud/gophercloud/testhelper" ) func TestLayer3RouterCreateDelete(t *testing.T) { client, err := clients.NewNetworkV2Client() th.AssertNoErr(t, err) network, err := networking.CreateNetwork(t, client) th.AssertNoErr(t, err) defer networking.DeleteNetwork(t, client, network.ID) router, err := CreateRouter(t, client, network.ID) th.AssertNoErr(t, err) defer DeleteRouter(t, client, router.ID) tools.PrintResource(t, router) newName := tools.RandomString("TESTACC-", 8) newDescription := "" updateOpts := routers.UpdateOpts{ Name: newName, Description: &newDescription, } _, err = routers.Update(client, router.ID, updateOpts).Extract() th.AssertNoErr(t, err) newRouter, err := routers.Get(client, router.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, newRouter) th.AssertEquals(t, newRouter.Name, newName) th.AssertEquals(t, newRouter.Description, newDescription) listOpts := routers.ListOpts{} allPages, err := routers.List(client, listOpts).AllPages() th.AssertNoErr(t, err) allRouters, err := routers.ExtractRouters(allPages) th.AssertNoErr(t, err) var found bool for _, router := range allRouters { if router.ID == newRouter.ID { found = true } } th.AssertEquals(t, found, true) } func TestLayer3ExternalRouterCreateDelete(t *testing.T) { clients.RequireAdmin(t) client, err := clients.NewNetworkV2Client() th.AssertNoErr(t, err) router, err := CreateExternalRouter(t, client) th.AssertNoErr(t, err) defer DeleteRouter(t, client, router.ID) tools.PrintResource(t, router) efi := []routers.ExternalFixedIP{} for _, extIP := range router.GatewayInfo.ExternalFixedIPs { efi = append(efi, routers.ExternalFixedIP{ IPAddress: extIP.IPAddress, SubnetID: extIP.SubnetID, }, ) } // Add a new external router IP efi = append(efi, routers.ExternalFixedIP{ SubnetID: router.GatewayInfo.ExternalFixedIPs[0].SubnetID, }, ) enableSNAT := true gatewayInfo := routers.GatewayInfo{ NetworkID: router.GatewayInfo.NetworkID, EnableSNAT: &enableSNAT, ExternalFixedIPs: efi, } newName := tools.RandomString("TESTACC-", 8) newDescription := "" updateOpts := routers.UpdateOpts{ Name: newName, Description: &newDescription, GatewayInfo: &gatewayInfo, } _, err = routers.Update(client, router.ID, updateOpts).Extract() th.AssertNoErr(t, err) newRouter, err := routers.Get(client, router.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, newRouter) th.AssertEquals(t, newRouter.Name, newName) th.AssertEquals(t, newRouter.Description, newDescription) th.AssertEquals(t, *newRouter.GatewayInfo.EnableSNAT, enableSNAT) th.AssertDeepEquals(t, newRouter.GatewayInfo.ExternalFixedIPs, efi) // Test Gateway removal updateOpts = routers.UpdateOpts{ GatewayInfo: &routers.GatewayInfo{}, } newRouter, err = routers.Update(client, router.ID, updateOpts).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, newRouter.GatewayInfo, routers.GatewayInfo{}) } func TestLayer3RouterInterface(t *testing.T) { clients.RequireAdmin(t) client, err := clients.NewNetworkV2Client() th.AssertNoErr(t, err) // Create Network network, err := networking.CreateNetwork(t, client) th.AssertNoErr(t, err) defer networking.DeleteNetwork(t, client, network.ID) subnet, err := networking.CreateSubnet(t, client, network.ID) th.AssertNoErr(t, err) defer networking.DeleteSubnet(t, client, subnet.ID) tools.PrintResource(t, subnet) router, err := CreateExternalRouter(t, client) th.AssertNoErr(t, err) defer DeleteRouter(t, client, router.ID) aiOpts := routers.AddInterfaceOpts{ SubnetID: subnet.ID, } iface, err := routers.AddInterface(client, router.ID, aiOpts).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, router) tools.PrintResource(t, iface) riOpts := routers.RemoveInterfaceOpts{ SubnetID: subnet.ID, } _, err = routers.RemoveInterface(client, router.ID, riOpts).Extract() th.AssertNoErr(t, err) } golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/networking/v2/extensions/lbaas/000077500000000000000000000000001367513235700332335ustar00rootroot00000000000000lbaas.go000066400000000000000000000117651367513235700345770ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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.go000066400000000000000000000041321367513235700361740ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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", err) } newMember, err := members.Get(client, member.ID).Extract() if err != nil { t.Fatalf("Unable to get member: %v", err) } tools.PrintResource(t, newMember) } monitors_test.go000066400000000000000000000027361367513235700364240ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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", err) } newMonitor, err := monitors.Get(client, monitor.ID).Extract() if err != nil { t.Fatalf("Unable to get monitor: %v", err) } tools.PrintResource(t, newMonitor) } pools_test.go000066400000000000000000000061151367513235700357010ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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", err) } newPool, err := pools.Get(client, pool.ID).Extract() if err != nil { t.Fatalf("Unable to get pool: %v", err) } 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.go000066400000000000000000000037561367513235700355360ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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", err) } newVIP, err := vips.Get(client, vip.ID).Extract() if err != nil { t.Fatalf("Unable to get vip: %v", err) } tools.PrintResource(t, newVIP) } golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/networking/v2/extensions/lbaas_v2/000077500000000000000000000000001367513235700336425ustar00rootroot00000000000000l7policies_test.go000066400000000000000000000014551367513235700372300ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/networking/v2/extensions/lbaas_v2// +build acceptance networking loadbalancer l7policies 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/l7policies" ) func TestL7PoliciesList(t *testing.T) { client, err := clients.NewNetworkV2Client() 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) } } lbaas_v2.go000066400000000000000000000365401367513235700356130ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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/l7policies" "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" th "github.com/gophercloud/gophercloud/testhelper" ) const loadbalancerActiveTimeoutSeconds = 600 const loadbalancerDeleteTimeoutSeconds = 600 // 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) listenerDescription := tools.RandomString("TESTACCT-DESC-", 8) listenerPort := tools.RandomInt(1, 100) t.Logf("Attempting to create listener %s on port %d", listenerName, listenerPort) createOpts := listeners.CreateOpts{ Name: listenerName, Description: listenerDescription, LoadbalancerID: lb.ID, Protocol: listeners.ProtocolHTTP, 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: %s", err) } th.AssertEquals(t, listener.Name, listenerName) th.AssertEquals(t, listener.Description, listenerDescription) th.AssertEquals(t, listener.Loadbalancers[0].ID, lb.ID) th.AssertEquals(t, listener.Protocol, string(listeners.ProtocolHTTP)) th.AssertEquals(t, listener.ProtocolPort, listenerPort) 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) lbDescription := tools.RandomString("TESTACCT-DESC-", 8) t.Logf("Attempting to create loadbalancer %s on subnet %s", lbName, subnetID) createOpts := loadbalancers.CreateOpts{ Name: lbName, Description: lbDescription, 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) th.AssertEquals(t, lb.Name, lbName) th.AssertEquals(t, lb.Description, lbDescription) th.AssertEquals(t, lb.VipSubnetID, subnetID) th.AssertEquals(t, lb.AdminStateUp, true) 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: %s", err) } th.AssertEquals(t, member.Name, memberName) 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: monitors.TypePING, } 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: %s", err) } th.AssertEquals(t, monitor.Name, monitorName) th.AssertEquals(t, monitor.Type, monitors.TypePING) 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) poolDescription := tools.RandomString("TESTACCT-DESC-", 8) t.Logf("Attempting to create pool %s", poolName) createOpts := pools.CreateOpts{ Name: poolName, Description: poolDescription, Protocol: pools.ProtocolHTTP, 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: %s", err) } th.AssertEquals(t, pool.Name, poolName) th.AssertEquals(t, pool.Description, poolDescription) th.AssertEquals(t, pool.Protocol, string(pools.ProtocolHTTP)) th.AssertEquals(t, pool.Loadbalancers[0].ID, lb.ID) th.AssertEquals(t, pool.LBMethod, string(pools.LBMethodLeastConnections)) 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) policyDescription := tools.RandomString("TESTACCT-DESC-", 8) t.Logf("Attempting to create l7 policy %s on the %s listener ID", policyName, listener.ID) createOpts := l7policies.CreateOpts{ Name: policyName, Description: policyDescription, 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: %s", err) } th.AssertEquals(t, policy.Name, policyName) th.AssertEquals(t, policy.Description, policyDescription) th.AssertEquals(t, policy.ListenerID, listener.ID) th.AssertEquals(t, policy.Action, string(l7policies.ActionRedirectToURL)) th.AssertEquals(t, policy.RedirectURL, "http://www.example.com") 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: %s", err) } th.AssertEquals(t, rule.RuleType, string(l7policies.TypePath)) th.AssertEquals(t, rule.CompareType, string(l7policies.CompareTypeStartWith)) th.AssertEquals(t, rule.Value, "/api") 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 { if _, ok := err.(gophercloud.ErrDefault404); !ok { 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: %s", err) } 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 { if _, ok := err.(gophercloud.ErrDefault404); !ok { 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: %s", err) } 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 { if _, ok := err.(gophercloud.ErrDefault404); !ok { 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: %s", err) } 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 { if _, ok := err.(gophercloud.ErrDefault404); !ok { 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: %s", err) } 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 { if _, ok := err.(gophercloud.ErrDefault404); !ok { 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: %s", err) } 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 { if _, ok := err.(gophercloud.ErrDefault404); !ok { 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: %s", 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, lbID, poolID string) { t.Logf("Attempting to delete pool %s", poolID) if err := pools.Delete(client, poolID).ExtractErr(); err != nil { if _, ok := err.(gophercloud.ErrDefault404); !ok { 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: %s", err) } 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 } if current.ProvisioningStatus == "ERROR" { return false, fmt.Errorf("Load balancer is in ERROR state") } return false, nil }) } listeners_test.go000066400000000000000000000014471367513235700371670ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/networking/v2/extensions/lbaas_v2// +build acceptance networking loadbalancer 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 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) } } loadbalancers_test.go000066400000000000000000000226521367513235700377520ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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/l7policies" "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" th "github.com/gophercloud/gophercloud/testhelper" ) func TestLoadbalancersList(t *testing.T) { client, err := clients.NewNetworkV2Client() th.AssertNoErr(t, err) allPages, err := loadbalancers.List(client, nil).AllPages() th.AssertNoErr(t, err) allLoadbalancers, err := loadbalancers.ExtractLoadBalancers(allPages) th.AssertNoErr(t, err) for _, lb := range allLoadbalancers { tools.PrintResource(t, lb) } } func TestLoadbalancersCRUD(t *testing.T) { client, err := clients.NewNetworkV2Client() th.AssertNoErr(t, err) network, err := networking.CreateNetwork(t, client) th.AssertNoErr(t, err) defer networking.DeleteNetwork(t, client, network.ID) subnet, err := networking.CreateSubnet(t, client, network.ID) th.AssertNoErr(t, err) defer networking.DeleteSubnet(t, client, subnet.ID) lb, err := CreateLoadBalancer(t, client, subnet.ID) th.AssertNoErr(t, err) defer DeleteLoadBalancer(t, client, lb.ID) lbDescription := "" updateLoadBalancerOpts := loadbalancers.UpdateOpts{ Description: &lbDescription, } _, err = loadbalancers.Update(client, lb.ID, updateLoadBalancerOpts).Extract() th.AssertNoErr(t, err) if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { t.Fatalf("Timed out waiting for loadbalancer to become active") } newLB, err := loadbalancers.Get(client, lb.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, newLB) th.AssertEquals(t, newLB.Description, lbDescription) lbStats, err := loadbalancers.GetStats(client, lb.ID).Extract() th.AssertNoErr(t, 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, client, lb) th.AssertNoErr(t, err) defer DeleteListener(t, client, lb.ID, listener.ID) listenerName := "" listenerDescription := "" updateListenerOpts := listeners.UpdateOpts{ Name: &listenerName, Description: &listenerDescription, } _, err = listeners.Update(client, listener.ID, updateListenerOpts).Extract() th.AssertNoErr(t, err) 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() th.AssertNoErr(t, err) tools.PrintResource(t, newListener) th.AssertEquals(t, newListener.Name, listenerName) th.AssertEquals(t, newListener.Description, listenerDescription) // L7 policy policy, err := CreateL7Policy(t, client, listener, lb) th.AssertNoErr(t, err) defer DeleteL7Policy(t, client, lb.ID, policy.ID) newDescription := "" updateL7policyOpts := l7policies.UpdateOpts{ Description: &newDescription, } _, err = l7policies.Update(client, policy.ID, updateL7policyOpts).Extract() th.AssertNoErr(t, err) if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { t.Fatalf("Timed out waiting for loadbalancer to become active") } newPolicy, err := l7policies.Get(client, policy.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, newPolicy) th.AssertEquals(t, newPolicy.Description, newDescription) // L7 rule rule, err := CreateL7Rule(t, client, newPolicy.ID, lb) th.AssertNoErr(t, err) defer DeleteL7Rule(t, client, lb.ID, policy.ID, rule.ID) allPages, err := l7policies.ListRules(client, policy.ID, l7policies.ListRulesOpts{}).AllPages() th.AssertNoErr(t, err) allRules, err := l7policies.ExtractRules(allPages) th.AssertNoErr(t, err) for _, rule := range allRules { tools.PrintResource(t, rule) } /* NOT supported on F5 driver */ updateL7ruleOpts := l7policies.UpdateRuleOpts{ RuleType: l7policies.TypePath, CompareType: l7policies.CompareTypeRegex, Value: "/images/special*", } _, err = l7policies.UpdateRule(client, policy.ID, rule.ID, updateL7ruleOpts).Extract() th.AssertNoErr(t, err) if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { t.Fatalf("Timed out waiting for loadbalancer to become active") } newRule, err := l7policies.GetRule(client, newPolicy.ID, rule.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, newRule) // Pool pool, err := CreatePool(t, client, lb) th.AssertNoErr(t, err) defer DeletePool(t, client, lb.ID, pool.ID) poolName := "" poolDescription := "" updatePoolOpts := pools.UpdateOpts{ Name: &poolName, Description: &poolDescription, } _, err = pools.Update(client, pool.ID, updatePoolOpts).Extract() th.AssertNoErr(t, err) 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() th.AssertNoErr(t, err) tools.PrintResource(t, newPool) th.AssertEquals(t, newPool.Name, poolName) th.AssertEquals(t, newPool.Description, poolDescription) // Update L7policy to redirect to pool newRedirectURL := "" updateL7policyOpts = l7policies.UpdateOpts{ Action: l7policies.ActionRedirectToPool, RedirectPoolID: &newPool.ID, RedirectURL: &newRedirectURL, } _, err = l7policies.Update(client, policy.ID, updateL7policyOpts).Extract() th.AssertNoErr(t, err) if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { t.Fatalf("Timed out waiting for loadbalancer to become active") } newPolicy, err = l7policies.Get(client, policy.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, newPolicy) th.AssertEquals(t, newPolicy.Description, newDescription) th.AssertEquals(t, newPolicy.Action, string(l7policies.ActionRedirectToPool)) th.AssertEquals(t, newPolicy.RedirectPoolID, newPool.ID) th.AssertEquals(t, newPolicy.RedirectURL, newRedirectURL) // Workaround for proper delete order defer DeleteL7Policy(t, client, lb.ID, policy.ID) defer DeleteL7Rule(t, client, lb.ID, policy.ID, rule.ID) // Update listener's default pool ID updateListenerOpts = listeners.UpdateOpts{ DefaultPoolID: &pool.ID, } _, err = listeners.Update(client, listener.ID, updateListenerOpts).Extract() th.AssertNoErr(t, err) 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() th.AssertNoErr(t, err) tools.PrintResource(t, newListener) th.AssertEquals(t, newListener.DefaultPoolID, pool.ID) // Remove listener's default pool ID emptyPoolID := "" updateListenerOpts = listeners.UpdateOpts{ DefaultPoolID: &emptyPoolID, } _, err = listeners.Update(client, listener.ID, updateListenerOpts).Extract() th.AssertNoErr(t, err) 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() th.AssertNoErr(t, err) tools.PrintResource(t, newListener) th.AssertEquals(t, newListener.DefaultPoolID, "") // Member member, err := CreateMember(t, client, lb, newPool, subnet.ID, subnet.CIDR) th.AssertNoErr(t, err) defer DeleteMember(t, client, lb.ID, pool.ID, member.ID) memberName := "" newWeight := tools.RandomInt(11, 100) updateMemberOpts := pools.UpdateMemberOpts{ Name: &memberName, Weight: &newWeight, } _, err = pools.UpdateMember(client, pool.ID, member.ID, updateMemberOpts).Extract() th.AssertNoErr(t, err) 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() th.AssertNoErr(t, err) tools.PrintResource(t, newMember) th.AssertEquals(t, newMember.Name, memberName) th.AssertEquals(t, newMember.Weight, newWeight) // Monitor monitor, err := CreateMonitor(t, client, lb, newPool) th.AssertNoErr(t, err) defer DeleteMonitor(t, client, lb.ID, monitor.ID) monName := "" newDelay := tools.RandomInt(20, 30) updateMonitorOpts := monitors.UpdateOpts{ Name: &monName, Delay: newDelay, } _, err = monitors.Update(client, monitor.ID, updateMonitorOpts).Extract() th.AssertNoErr(t, err) 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() th.AssertNoErr(t, err) tools.PrintResource(t, newMonitor) th.AssertEquals(t, newMonitor.Name, monName) th.AssertEquals(t, newMonitor.Delay, newDelay) } monitors_test.go000066400000000000000000000014331367513235700370240ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/networking/v2/extensions/lbaas_v2// +build acceptance networking loadbalancer 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 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) } } pools_test.go000066400000000000000000000013671367513235700363140ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/networking/v2/extensions/lbaas_v2// +build acceptance networking loadbalancer 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 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.12.0/acceptance/openstack/networking/v2/extensions/mtu/000077500000000000000000000000001367513235700327565ustar00rootroot00000000000000mtu.go000066400000000000000000000031561367513235700340400ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/networking/v2/extensions/mtupackage mtu import ( "testing" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/mtu" "github.com/gophercloud/gophercloud/openstack/networking/v2/networks" th "github.com/gophercloud/gophercloud/testhelper" ) type NetworkMTU struct { networks.Network mtu.NetworkMTUExt } // CreateNetworkWithMTU will create a network with custom MTU. An error will be // returned if the creation failed. func CreateNetworkWithMTU(t *testing.T, client *gophercloud.ServiceClient, networkMTU *int) (*NetworkMTU, error) { networkName := tools.RandomString("TESTACC-", 8) networkDescription := tools.RandomString("TESTACC-DESC-", 8) t.Logf("Attempting to create a network with custom MTU: %s", networkName) adminStateUp := true var createOpts networks.CreateOptsBuilder createOpts = networks.CreateOpts{ Name: networkName, Description: networkDescription, AdminStateUp: &adminStateUp, } if *networkMTU > 0 { createOpts = mtu.CreateOptsExt{ CreateOptsBuilder: createOpts, MTU: *networkMTU, } } var network NetworkMTU err := networks.Create(client, createOpts).ExtractInto(&network) if err != nil { return &network, err } t.Logf("Created a network with custom MTU: %s", networkName) th.AssertEquals(t, network.Name, networkName) th.AssertEquals(t, network.Description, networkDescription) th.AssertEquals(t, network.AdminStateUp, adminStateUp) if *networkMTU > 0 { th.AssertEquals(t, network.MTU, *networkMTU) } else { *networkMTU = network.MTU } return &network, nil } mtu_test.go000066400000000000000000000075271367513235700351050ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/networking/v2/extensions/mtu// +build acceptance networking package mtu 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/common/extensions" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/mtu" "github.com/gophercloud/gophercloud/openstack/networking/v2/networks" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" ) func TestMTUNetworkCRUDL(t *testing.T) { clients.RequireAdmin(t) clients.SkipRelease(t, "stable/mitaka") clients.SkipRelease(t, "stable/newton") clients.SkipRelease(t, "stable/ocata") client, err := clients.NewNetworkV2Client() th.AssertNoErr(t, err) extension, err := extensions.Get(client, "net-mtu").Extract() if err != nil { t.Skip("This test requires net-mtu Neutron extension") } tools.PrintResource(t, extension) mtuWritable, _ := extensions.Get(client, "net-mtu-writable").Extract() tools.PrintResource(t, mtuWritable) // Create Network var networkMTU int if mtuWritable != nil { networkMTU = 1449 } network, err := CreateNetworkWithMTU(t, client, &networkMTU) th.AssertNoErr(t, err) defer networking.DeleteNetwork(t, client, network.ID) // MTU filtering is supported only in read-only MTU extension // https://bugs.launchpad.net/neutron/+bug/1818317 if mtuWritable == nil { // List network successfully var listOpts networks.ListOptsBuilder listOpts = mtu.ListOptsExt{ ListOptsBuilder: networks.ListOpts{}, MTU: networkMTU, } var listedNetworks []NetworkMTU i := 0 err = networks.List(client, listOpts).EachPage(func(page pagination.Page) (bool, error) { i++ err := networks.ExtractNetworksInto(page, &listedNetworks) if err != nil { t.Errorf("Failed to extract networks: %v", err) return false, err } tools.PrintResource(t, listedNetworks) th.AssertEquals(t, 1, len(listedNetworks)) th.CheckDeepEquals(t, *network, listedNetworks[0]) return true, nil }) th.AssertNoErr(t, err) th.AssertEquals(t, 1, i) // List network unsuccessfully listOpts = mtu.ListOptsExt{ ListOptsBuilder: networks.ListOpts{}, MTU: 1, } i = 0 err = networks.List(client, listOpts).EachPage(func(page pagination.Page) (bool, error) { i++ err := networks.ExtractNetworksInto(page, &listedNetworks) if err != nil { t.Errorf("Failed to extract networks: %v", err) return false, err } tools.PrintResource(t, listedNetworks) th.AssertEquals(t, 1, len(listedNetworks)) th.CheckDeepEquals(t, *network, listedNetworks[0]) return true, nil }) th.AssertNoErr(t, err) th.AssertEquals(t, 0, i) } // Get network var getNetwork NetworkMTU err = networks.Get(client, network.ID).ExtractInto(&getNetwork) th.AssertNoErr(t, err) tools.PrintResource(t, getNetwork) th.AssertDeepEquals(t, *network, getNetwork) if mtuWritable != nil { // Update network newNetworkDescription := "" newNetworkMTU := 1350 networkUpdateOpts := networks.UpdateOpts{ Description: &newNetworkDescription, } var updateOpts networks.UpdateOptsBuilder updateOpts = mtu.UpdateOptsExt{ UpdateOptsBuilder: networkUpdateOpts, MTU: newNetworkMTU, } var newNetwork NetworkMTU err = networks.Update(client, network.ID, updateOpts).ExtractInto(&newNetwork) th.AssertNoErr(t, err) tools.PrintResource(t, newNetwork) th.AssertEquals(t, newNetwork.Description, newNetworkDescription) th.AssertEquals(t, newNetwork.MTU, newNetworkMTU) // Get updated network var getNewNetwork NetworkMTU err = networks.Get(client, network.ID).ExtractInto(&getNewNetwork) th.AssertNoErr(t, err) tools.PrintResource(t, getNewNetwork) th.AssertDeepEquals(t, newNetwork, getNewNetwork) } } networkipavailabilities/000077500000000000000000000000001367513235700370175ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/networking/v2/extensionsnetworkipavailabilities_test.go000066400000000000000000000016661367513235700453430ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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" th "github.com/gophercloud/gophercloud/testhelper" ) func TestNetworkIPAvailabilityList(t *testing.T) { client, err := clients.NewNetworkV2Client() th.AssertNoErr(t, err) allPages, err := networkipavailabilities.List(client, nil).AllPages() th.AssertNoErr(t, err) allAvailabilities, err := networkipavailabilities.ExtractNetworkIPAvailabilities(allPages) th.AssertNoErr(t, err) for _, availability := range allAvailabilities { for _, subnet := range availability.SubnetIPAvailabilities { tools.PrintResource(t, subnet) tools.PrintResource(t, subnet.TotalIPs) tools.PrintResource(t, subnet.UsedIPs) } } } portsbinding/000077500000000000000000000000001367513235700345745ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/networking/v2/extensionsportsbinding.go000066400000000000000000000031021367513235700376210ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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" th "github.com/gophercloud/gophercloud/testhelper" ) // 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, profile map[string]interface{}) (PortWithBindingExt, error) { portName := tools.RandomString("TESTACC-", 8) portDescription := tools.RandomString("TESTACC-PORT-DESC-", 8) iFalse := false t.Logf("Attempting to create port: %s", portName) portCreateOpts := ports.CreateOpts{ NetworkID: networkID, Name: portName, Description: portDescription, AdminStateUp: &iFalse, FixedIPs: []ports.IP{ports.IP{SubnetID: subnetID}}, } createOpts := portsbinding.CreateOptsExt{ CreateOptsBuilder: portCreateOpts, HostID: hostID, Profile: profile, } var s PortWithBindingExt err := ports.Create(client, createOpts).ExtractInto(&s) if err != nil { return s, err } t.Logf("Successfully created port: %s", portName) th.AssertEquals(t, s.Name, portName) th.AssertEquals(t, s.Description, portDescription) return s, nil } portsbinding_test.go000066400000000000000000000044571367513235700406760ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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/extensions/portsbinding" "github.com/gophercloud/gophercloud/openstack/networking/v2/ports" th "github.com/gophercloud/gophercloud/testhelper" ) func TestPortsbindingCRUD(t *testing.T) { clients.RequireAdmin(t) client, err := clients.NewNetworkV2Client() th.AssertNoErr(t, err) // Create Network network, err := networking.CreateNetwork(t, client) th.AssertNoErr(t, err) defer networking.DeleteNetwork(t, client, network.ID) // Create Subnet subnet, err := networking.CreateSubnet(t, client, network.ID) th.AssertNoErr(t, err) defer networking.DeleteSubnet(t, client, subnet.ID) // Define a host hostID := "localhost" profile := map[string]interface{}{"foo": "bar"} // Create port port, err := CreatePortsbinding(t, client, network.ID, subnet.ID, hostID, profile) th.AssertNoErr(t, err) defer networking.DeletePort(t, client, port.ID) tools.PrintResource(t, port) th.AssertEquals(t, port.HostID, hostID) th.AssertEquals(t, port.VNICType, "normal") th.AssertDeepEquals(t, port.Profile, profile) // Update port newPortName := "" newPortDescription := "" newHostID := "127.0.0.1" newProfile := map[string]interface{}{} updateOpts := ports.UpdateOpts{ Name: &newPortName, Description: &newPortDescription, } var finalUpdateOpts ports.UpdateOptsBuilder finalUpdateOpts = portsbinding.UpdateOptsExt{ UpdateOptsBuilder: updateOpts, HostID: &newHostID, VNICType: "baremetal", Profile: newProfile, } var newPort PortWithBindingExt _, err = ports.Update(client, port.ID, finalUpdateOpts).Extract() th.AssertNoErr(t, err) // Read the updated port err = ports.Get(client, port.ID).ExtractInto(&newPort) th.AssertNoErr(t, err) tools.PrintResource(t, newPort) th.AssertEquals(t, newPort.Description, newPortName) th.AssertEquals(t, newPort.Description, newPortDescription) th.AssertEquals(t, newPort.HostID, newHostID) th.AssertEquals(t, newPort.VNICType, "baremetal") th.AssertDeepEquals(t, newPort.Profile, newProfile) } provider_test.go000066400000000000000000000014711367513235700353150ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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" th "github.com/gophercloud/gophercloud/testhelper" ) func TestNetworksProviderCRUD(t *testing.T) { client, err := clients.NewNetworkV2Client() th.AssertNoErr(t, err) // Create a network network, err := networking.CreateNetwork(t, client) th.AssertNoErr(t, err) defer networking.DeleteNetwork(t, client, network.ID) getResult := networks.Get(client, network.ID) newNetwork, err := getResult.Extract() th.AssertNoErr(t, err) tools.PrintResource(t, newNetwork) } golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/networking/v2/extensions/qos/000077500000000000000000000000001367513235700327535ustar00rootroot00000000000000policies/000077500000000000000000000000001367513235700345035ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/networking/v2/extensions/qospolicies.go000066400000000000000000000027311367513235700366440ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/networking/v2/extensions/qos/policiespackage policies import ( "testing" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/qos/policies" th "github.com/gophercloud/gophercloud/testhelper" ) // CreateQoSPolicy will create a QoS policy. An error will be returned if the // QoS policy could not be created. func CreateQoSPolicy(t *testing.T, client *gophercloud.ServiceClient) (*policies.Policy, error) { policyName := tools.RandomString("TESTACC-", 8) policyDescription := tools.RandomString("TESTACC-DESC-", 8) createOpts := policies.CreateOpts{ Name: policyName, Description: policyDescription, } t.Logf("Attempting to create a QoS policy: %s", policyName) policy, err := policies.Create(client, createOpts).Extract() if err != nil { return nil, err } t.Logf("Succesfully created a QoS policy") th.AssertEquals(t, policyName, policy.Name) th.AssertEquals(t, policyDescription, policy.Description) return policy, nil } // DeleteQoSPolicy will delete a QoS policy with a specified ID. // A fatal error will occur if the delete was not successful. func DeleteQoSPolicy(t *testing.T, client *gophercloud.ServiceClient, policyID string) { t.Logf("Attempting to delete the QoS policy: %s", policyID) err := policies.Delete(client, policyID).ExtractErr() if err != nil { t.Fatalf("Unable to delete QoS policy %s: %v", policyID, err) } t.Logf("Deleted QoS policy: %s", policyID) } policies_test.go000066400000000000000000000026001367513235700376760ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/networking/v2/extensions/qos/policies// +build acceptance networking qos policies package policies import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/qos/policies" th "github.com/gophercloud/gophercloud/testhelper" ) func TestPoliciesCRUD(t *testing.T) { client, err := clients.NewNetworkV2Client() th.AssertNoErr(t, err) // Create a QoS policy. policy, err := CreateQoSPolicy(t, client) th.AssertNoErr(t, err) defer DeleteQoSPolicy(t, client, policy.ID) tools.PrintResource(t, policy) newName := tools.RandomString("TESTACC-", 8) newDescription := "" updateOpts := &policies.UpdateOpts{ Name: newName, Description: &newDescription, } _, err = policies.Update(client, policy.ID, updateOpts).Extract() th.AssertNoErr(t, err) newPolicy, err := policies.Get(client, policy.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, newPolicy) th.AssertEquals(t, newPolicy.Name, newName) th.AssertEquals(t, newPolicy.Description, newDescription) allPages, err := policies.List(client, nil).AllPages() th.AssertNoErr(t, err) allPolicies, err := policies.ExtractPolicies(allPages) th.AssertNoErr(t, err) var found bool for _, policy := range allPolicies { if policy.ID == newPolicy.ID { found = true } } th.AssertEquals(t, found, true) } rules/000077500000000000000000000000001367513235700340265ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/networking/v2/extensions/qosrules.go000066400000000000000000000050561367513235700355150ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/networking/v2/extensions/qos/rulespackage rules import ( "testing" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/qos/rules" th "github.com/gophercloud/gophercloud/testhelper" ) // CreateBandwidthLimitRule will create a QoS BandwidthLimitRule associated with the provided QoS policy. // An error will be returned if the QoS rule could not be created. func CreateBandwidthLimitRule(t *testing.T, client *gophercloud.ServiceClient, policyID string) (*rules.BandwidthLimitRule, error) { maxKBps := 3000 maxBurstKBps := 300 createOpts := rules.CreateBandwidthLimitRuleOpts{ MaxKBps: maxKBps, MaxBurstKBps: maxBurstKBps, } t.Logf("Attempting to create a QoS bandwidth limit rule with max_kbps: %d, max_burst_kbps: %d", maxKBps, maxBurstKBps) rule, err := rules.CreateBandwidthLimitRule(client, policyID, createOpts).ExtractBandwidthLimitRule() if err != nil { return nil, err } t.Logf("Succesfully created a QoS bandwidth limit rule") th.AssertEquals(t, maxKBps, rule.MaxKBps) th.AssertEquals(t, maxBurstKBps, rule.MaxBurstKBps) return rule, nil } // CreateDSCPMarkingRule will create a QoS DSCPMarkingRule associated with the provided QoS policy. // An error will be returned if the QoS rule could not be created. func CreateDSCPMarkingRule(t *testing.T, client *gophercloud.ServiceClient, policyID string) (*rules.DSCPMarkingRule, error) { dscpMark := 26 createOpts := rules.CreateDSCPMarkingRuleOpts{ DSCPMark: dscpMark, } t.Logf("Attempting to create a QoS DSCP marking rule with dscp_mark: %d", dscpMark) rule, err := rules.CreateDSCPMarkingRule(client, policyID, createOpts).ExtractDSCPMarkingRule() if err != nil { return nil, err } t.Logf("Succesfully created a QoS DSCP marking rule") th.AssertEquals(t, dscpMark, rule.DSCPMark) return rule, nil } // CreateMinimumBandwidthRule will create a QoS MinimumBandwidthRule associated with the provided QoS policy. // An error will be returned if the QoS rule could not be created. func CreateMinimumBandwidthRule(t *testing.T, client *gophercloud.ServiceClient, policyID string) (*rules.MinimumBandwidthRule, error) { minKBps := 1000 createOpts := rules.CreateMinimumBandwidthRuleOpts{ MinKBps: minKBps, } t.Logf("Attempting to create a QoS minimum bandwidth rule with min_kbps: %d", minKBps) rule, err := rules.CreateMinimumBandwidthRule(client, policyID, createOpts).ExtractMinimumBandwidthRule() if err != nil { return nil, err } t.Logf("Succesfully created a QoS minimum bandwidth rule") th.AssertEquals(t, minKBps, rule.MinKBps) return rule, nil } rules_test.go000066400000000000000000000100431367513235700365440ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/networking/v2/extensions/qos/rulespackage rules import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" accpolicies "github.com/gophercloud/gophercloud/acceptance/openstack/networking/v2/extensions/qos/policies" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/qos/policies" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/qos/rules" th "github.com/gophercloud/gophercloud/testhelper" ) func TestBandwidthLimitRulesCRUD(t *testing.T) { client, err := clients.NewNetworkV2Client() th.AssertNoErr(t, err) // Create a QoS policy policy, err := accpolicies.CreateQoSPolicy(t, client) th.AssertNoErr(t, err) defer policies.Delete(client, policy.ID) tools.PrintResource(t, policy) // Create a QoS policy rule. rule, err := CreateBandwidthLimitRule(t, client, policy.ID) th.AssertNoErr(t, err) defer rules.DeleteBandwidthLimitRule(client, policy.ID, rule.ID) // Update the QoS policy rule. newMaxBurstKBps := 0 updateOpts := rules.UpdateBandwidthLimitRuleOpts{ MaxBurstKBps: &newMaxBurstKBps, } newRule, err := rules.UpdateBandwidthLimitRule(client, policy.ID, rule.ID, updateOpts).ExtractBandwidthLimitRule() th.AssertNoErr(t, err) tools.PrintResource(t, newRule) th.AssertEquals(t, newRule.MaxBurstKBps, 0) allPages, err := rules.ListBandwidthLimitRules(client, policy.ID, rules.BandwidthLimitRulesListOpts{}).AllPages() th.AssertNoErr(t, err) allRules, err := rules.ExtractBandwidthLimitRules(allPages) th.AssertNoErr(t, err) var found bool for _, rule := range allRules { if rule.ID == newRule.ID { found = true } } th.AssertEquals(t, found, true) } func TestDSCPMarkingRulesCRUD(t *testing.T) { clients.SkipRelease(t, "stable/mitaka") client, err := clients.NewNetworkV2Client() th.AssertNoErr(t, err) // Create a QoS policy policy, err := accpolicies.CreateQoSPolicy(t, client) th.AssertNoErr(t, err) defer policies.Delete(client, policy.ID) tools.PrintResource(t, policy) // Create a QoS policy rule. rule, err := CreateDSCPMarkingRule(t, client, policy.ID) th.AssertNoErr(t, err) defer rules.DeleteDSCPMarkingRule(client, policy.ID, rule.ID) // Update the QoS policy rule. dscpMark := 20 updateOpts := rules.UpdateDSCPMarkingRuleOpts{ DSCPMark: &dscpMark, } newRule, err := rules.UpdateDSCPMarkingRule(client, policy.ID, rule.ID, updateOpts).ExtractDSCPMarkingRule() th.AssertNoErr(t, err) tools.PrintResource(t, newRule) th.AssertEquals(t, newRule.DSCPMark, 20) allPages, err := rules.ListDSCPMarkingRules(client, policy.ID, rules.DSCPMarkingRulesListOpts{}).AllPages() th.AssertNoErr(t, err) allRules, err := rules.ExtractDSCPMarkingRules(allPages) th.AssertNoErr(t, err) var found bool for _, rule := range allRules { if rule.ID == newRule.ID { found = true } } th.AssertEquals(t, found, true) } func TestMinimumBandwidthRulesCRUD(t *testing.T) { clients.SkipRelease(t, "stable/mitaka") client, err := clients.NewNetworkV2Client() th.AssertNoErr(t, err) // Create a QoS policy policy, err := accpolicies.CreateQoSPolicy(t, client) th.AssertNoErr(t, err) defer policies.Delete(client, policy.ID) tools.PrintResource(t, policy) // Create a QoS policy rule. rule, err := CreateMinimumBandwidthRule(t, client, policy.ID) th.AssertNoErr(t, err) defer rules.DeleteMinimumBandwidthRule(client, policy.ID, rule.ID) // Update the QoS policy rule. minKBps := 500 updateOpts := rules.UpdateMinimumBandwidthRuleOpts{ MinKBps: &minKBps, } newRule, err := rules.UpdateMinimumBandwidthRule(client, policy.ID, rule.ID, updateOpts).ExtractMinimumBandwidthRule() th.AssertNoErr(t, err) tools.PrintResource(t, newRule) th.AssertEquals(t, newRule.MinKBps, 500) allPages, err := rules.ListMinimumBandwidthRules(client, policy.ID, rules.MinimumBandwidthRulesListOpts{}).AllPages() th.AssertNoErr(t, err) allRules, err := rules.ExtractMinimumBandwidthRules(allPages) th.AssertNoErr(t, err) var found bool for _, rule := range allRules { if rule.ID == newRule.ID { found = true } } th.AssertEquals(t, found, true) } ruletypes/000077500000000000000000000000001367513235700347305ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/networking/v2/extensions/qosruletypes_test.go000066400000000000000000000021611367513235700403520ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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 TestRuleTypes(t *testing.T) { clients.SkipRelease(t, "stable/mitaka") clients.SkipRelease(t, "stable/newton") clients.SkipRelease(t, "stable/ocata") 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) if len(ruleTypes) > 0 { t.Logf("Trying to get rule type: %s", ruleTypes[0].Type) ruleType, err := ruletypes.GetRuleType(client, ruleTypes[0].Type).Extract() if err != nil { t.Fatalf("Failed to get rule type %s: %s", ruleTypes[0].Type, err) } tools.PrintResource(t, ruleType) } } golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/networking/v2/extensions/quotas/000077500000000000000000000000001367513235700334655ustar00rootroot00000000000000quotas.go000066400000000000000000000011711367513235700352510ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/networking/v2/extensions/quotaspackage quotas import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/quotas" ) var updateOpts = quotas.UpdateOpts{ FloatingIP: gophercloud.IntToPointer(10), Network: gophercloud.IntToPointer(-1), Port: gophercloud.IntToPointer(11), RBACPolicy: gophercloud.IntToPointer(0), Router: gophercloud.IntToPointer(-1), SecurityGroup: gophercloud.IntToPointer(12), SecurityGroupRule: gophercloud.IntToPointer(13), Subnet: gophercloud.IntToPointer(14), SubnetPool: gophercloud.IntToPointer(15), } quotas_test.go000066400000000000000000000034501367513235700363120ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/networking/v2/extensions/quotas// +build acceptance networking quotas package quotas import ( "log" "os" "reflect" "testing" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/quotas" th "github.com/gophercloud/gophercloud/testhelper" ) func TestQuotasGet(t *testing.T) { clients.RequireAdmin(t) client, err := clients.NewNetworkV2Client() th.AssertNoErr(t, err) quotasInfo, err := quotas.Get(client, os.Getenv("OS_PROJECT_NAME")).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, quotasInfo) } func TestQuotasUpdate(t *testing.T) { clients.RequireAdmin(t) client, err := clients.NewNetworkV2Client() th.AssertNoErr(t, err) originalQuotas, err := quotas.Get(client, os.Getenv("OS_PROJECT_NAME")).Extract() th.AssertNoErr(t, err) newQuotas, err := quotas.Update(client, os.Getenv("OS_PROJECT_NAME"), updateOpts).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, newQuotas) if reflect.DeepEqual(originalQuotas, newQuotas) { log.Fatal("Original and New Networking Quotas are the same") } // Restore original quotas. restoredQuotas, err := quotas.Update(client, os.Getenv("OS_PROJECT_NAME"), quotas.UpdateOpts{ FloatingIP: &originalQuotas.FloatingIP, Network: &originalQuotas.Network, Port: &originalQuotas.Port, RBACPolicy: &originalQuotas.RBACPolicy, Router: &originalQuotas.Router, SecurityGroup: &originalQuotas.SecurityGroup, SecurityGroupRule: &originalQuotas.SecurityGroupRule, Subnet: &originalQuotas.Subnet, SubnetPool: &originalQuotas.SubnetPool, }).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, originalQuotas, restoredQuotas) tools.PrintResource(t, restoredQuotas) } rbacpolicies/000077500000000000000000000000001367513235700345315ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/networking/v2/extensionsrbacpolicies.go000066400000000000000000000026741367513235700375300ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/networking/v2/extensions/rbacpoliciespackage rbacpolicies import ( "testing" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/rbacpolicies" th "github.com/gophercloud/gophercloud/testhelper" ) // 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") th.AssertEquals(t, rbacPolicy.ObjectID, networkID) 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.go000066400000000000000000000046101367513235700405570ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/networking/v2/extensions/rbacpolicies// +build acceptance package rbacpolicies import ( "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" th "github.com/gophercloud/gophercloud/testhelper" ) func TestRBACPolicyCRUD(t *testing.T) { clients.RequireAdmin(t) client, err := clients.NewNetworkV2Client() th.AssertNoErr(t, err) // Create a network network, err := networking.CreateNetwork(t, client) th.AssertNoErr(t, err) defer networking.DeleteNetwork(t, client, network.ID) tools.PrintResource(t, network) identityClient, err := clients.NewIdentityV3Client() th.AssertNoErr(t, err) // Create a project/tenant project, err := projects.CreateProject(t, identityClient, nil) th.AssertNoErr(t, 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) th.AssertNoErr(t, 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) th.AssertNoErr(t, 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() th.AssertNoErr(t, err) // Get the rbac-policy by ID t.Logf("Get rbac_policy by ID") newrbacPolicy, err := rbacpolicies.Get(client, rbacPolicy.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, newrbacPolicy) } func TestRBACPolicyList(t *testing.T) { clients.RequireAdmin(t) client, err := clients.NewNetworkV2Client() th.AssertNoErr(t, err) type rbacPolicy struct { rbacpolicies.RBACPolicy } var allRBACPolicies []rbacPolicy allPages, err := rbacpolicies.List(client, nil).AllPages() th.AssertNoErr(t, err) err = rbacpolicies.ExtractRBACPolicesInto(allPages, &allRBACPolicies) th.AssertNoErr(t, err) for _, rbacpolicy := range allRBACPolicies { tools.PrintResource(t, rbacpolicy) } } security_test.go000066400000000000000000000045001367513235700353260ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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" th "github.com/gophercloud/gophercloud/testhelper" ) func TestSecurityGroupsCreateUpdateDelete(t *testing.T) { client, err := clients.NewNetworkV2Client() th.AssertNoErr(t, err) group, err := CreateSecurityGroup(t, client) th.AssertNoErr(t, err) defer DeleteSecurityGroup(t, client, group.ID) rule, err := CreateSecurityGroupRule(t, client, group.ID) th.AssertNoErr(t, err) defer DeleteSecurityGroupRule(t, client, rule.ID) tools.PrintResource(t, group) var name = "Update group" var description = "" updateOpts := groups.UpdateOpts{ Name: name, Description: &description, } newGroup, err := groups.Update(client, group.ID, updateOpts).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, newGroup) th.AssertEquals(t, newGroup.Name, name) th.AssertEquals(t, newGroup.Description, description) listOpts := groups.ListOpts{} allPages, err := groups.List(client, listOpts).AllPages() th.AssertNoErr(t, err) allGroups, err := groups.ExtractGroups(allPages) th.AssertNoErr(t, err) var found bool for _, group := range allGroups { if group.ID == newGroup.ID { found = true } } th.AssertEquals(t, found, true) } func TestSecurityGroupsPort(t *testing.T) { client, err := clients.NewNetworkV2Client() th.AssertNoErr(t, err) network, err := networking.CreateNetwork(t, client) th.AssertNoErr(t, err) defer networking.DeleteNetwork(t, client, network.ID) subnet, err := networking.CreateSubnet(t, client, network.ID) th.AssertNoErr(t, err) defer networking.DeleteSubnet(t, client, subnet.ID) group, err := CreateSecurityGroup(t, client) th.AssertNoErr(t, err) defer DeleteSecurityGroup(t, client, group.ID) rule, err := CreateSecurityGroupRule(t, client, group.ID) th.AssertNoErr(t, err) defer DeleteSecurityGroupRule(t, client, rule.ID) port, err := CreatePortWithSecurityGroup(t, client, network.ID, subnet.ID, group.ID) th.AssertNoErr(t, err) defer networking.DeletePort(t, client, port.ID) tools.PrintResource(t, port) } subnetpools/000077500000000000000000000000001367513235700344475ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/networking/v2/extensionssubnetpools.go000066400000000000000000000032471367513235700373610ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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" th "github.com/gophercloud/gophercloud/testhelper" ) // 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) subnetPoolDescription := tools.RandomString("TESTACC-DESC-", 8) subnetPoolPrefixes := []string{ "10.0.0.0/8", } createOpts := subnetpools.CreateOpts{ Name: subnetPoolName, Description: subnetPoolDescription, Prefixes: subnetPoolPrefixes, DefaultPrefixLen: 24, } 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.") th.AssertEquals(t, subnetPool.Name, subnetPoolName) th.AssertEquals(t, subnetPool.Description, subnetPoolDescription) 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.go000066400000000000000000000027041367513235700404150ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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" th "github.com/gophercloud/gophercloud/testhelper" ) func TestSubnetPoolsCRUD(t *testing.T) { client, err := clients.NewNetworkV2Client() th.AssertNoErr(t, err) // Create a subnetpool subnetPool, err := CreateSubnetPool(t, client) th.AssertNoErr(t, err) defer DeleteSubnetPool(t, client, subnetPool.ID) tools.PrintResource(t, subnetPool) newName := tools.RandomString("TESTACC-", 8) newDescription := "" updateOpts := &subnetpools.UpdateOpts{ Name: newName, Description: &newDescription, } _, err = subnetpools.Update(client, subnetPool.ID, updateOpts).Extract() th.AssertNoErr(t, err) newSubnetPool, err := subnetpools.Get(client, subnetPool.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, newSubnetPool) th.AssertEquals(t, newSubnetPool.Name, newName) th.AssertEquals(t, newSubnetPool.Description, newDescription) allPages, err := subnetpools.List(client, nil).AllPages() th.AssertNoErr(t, err) allSubnetPools, err := subnetpools.ExtractSubnetPools(allPages) th.AssertNoErr(t, err) var found bool for _, subnetpool := range allSubnetPools { if subnetpool.ID == newSubnetPool.ID { found = true } } th.AssertEquals(t, found, true) } golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/networking/v2/extensions/trunks/000077500000000000000000000000001367513235700334775ustar00rootroot00000000000000trunks.go000066400000000000000000000024201367513235700352730ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/networking/v2/extensions/trunkspackage trunks import ( "testing" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/trunks" ) func CreateTrunk(t *testing.T, client *gophercloud.ServiceClient, parentPortID string, subportIDs ...string) (trunk *trunks.Trunk, err error) { trunkName := tools.RandomString("TESTACC-", 8) iTrue := true opts := trunks.CreateOpts{ Name: trunkName, Description: "Trunk created by gophercloud", AdminStateUp: &iTrue, PortID: parentPortID, } opts.Subports = make([]trunks.Subport, len(subportIDs)) for id, subportID := range subportIDs { opts.Subports[id] = trunks.Subport{ SegmentationID: id + 1, SegmentationType: "vlan", PortID: subportID, } } t.Logf("Attempting to create trunk: %s", opts.Name) trunk, err = trunks.Create(client, opts).Extract() if err == nil { t.Logf("Successfully created trunk") } return } func DeleteTrunk(t *testing.T, client *gophercloud.ServiceClient, trunkID string) { t.Logf("Attempting to delete trunk: %s", trunkID) err := trunks.Delete(client, trunkID).ExtractErr() if err != nil { t.Fatalf("Unable to delete trunk %s: %v", trunkID, err) } t.Logf("Deleted trunk: %s", trunkID) } trunks_test.go000066400000000000000000000203251367513235700363360ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/networking/v2/extensions/trunks// +build acceptance trunks package trunks import ( "sort" "testing" "github.com/gophercloud/gophercloud/acceptance/clients" v2 "github.com/gophercloud/gophercloud/acceptance/openstack/networking/v2" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/attributestags" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/trunks" th "github.com/gophercloud/gophercloud/testhelper" ) func TestTrunkCRUD(t *testing.T) { clients.SkipRelease(t, "stable/mitaka") client, err := clients.NewNetworkV2Client() if err != nil { t.Fatalf("Unable to create a network client: %v", err) } // Create Network network, err := v2.CreateNetwork(t, client) if err != nil { t.Fatalf("Unable to create network: %v", err) } defer v2.DeleteNetwork(t, client, network.ID) // Create Subnet subnet, err := v2.CreateSubnet(t, client, network.ID) if err != nil { t.Fatalf("Unable to create subnet: %v", err) } defer v2.DeleteSubnet(t, client, subnet.ID) // Create port parentPort, err := v2.CreatePort(t, client, network.ID, subnet.ID) if err != nil { t.Fatalf("Unable to create port: %v", err) } defer v2.DeletePort(t, client, parentPort.ID) subport1, err := v2.CreatePort(t, client, network.ID, subnet.ID) if err != nil { t.Fatalf("Unable to create port: %v", err) } defer v2.DeletePort(t, client, subport1.ID) subport2, err := v2.CreatePort(t, client, network.ID, subnet.ID) if err != nil { t.Fatalf("Unable to create port: %v", err) } defer v2.DeletePort(t, client, subport2.ID) trunk, err := CreateTrunk(t, client, parentPort.ID, subport1.ID, subport2.ID) if err != nil { t.Fatalf("Unable to create trunk: %v", err) } defer DeleteTrunk(t, client, trunk.ID) _, err = trunks.Get(client, trunk.ID).Extract() if err != nil { t.Fatalf("Unable to get trunk: %v", err) } // Update Trunk name := "" description := "" updateOpts := trunks.UpdateOpts{ Name: &name, Description: &description, } updatedTrunk, err := trunks.Update(client, trunk.ID, updateOpts).Extract() if err != nil { t.Fatalf("Unable to update trunk: %v", err) } if trunk.Name == updatedTrunk.Name { t.Fatalf("Trunk name was not updated correctly") } if trunk.Description == updatedTrunk.Description { t.Fatalf("Trunk description was not updated correctly") } th.AssertDeepEquals(t, updatedTrunk.Name, name) th.AssertDeepEquals(t, updatedTrunk.Description, description) // Get subports subports, err := trunks.GetSubports(client, trunk.ID).Extract() if err != nil { t.Fatalf("Unable to get subports from the Trunk: %v", err) } th.AssertDeepEquals(t, trunk.Subports[0], subports[0]) th.AssertDeepEquals(t, trunk.Subports[1], subports[1]) tools.PrintResource(t, trunk) } func TestTrunkList(t *testing.T) { clients.SkipRelease(t, "stable/mitaka") client, err := clients.NewNetworkV2Client() if err != nil { t.Fatalf("Unable to create a network client: %v", err) } allPages, err := trunks.List(client, nil).AllPages() if err != nil { t.Fatalf("Unable to list trunks: %v", err) } allTrunks, err := trunks.ExtractTrunks(allPages) if err != nil { t.Fatalf("Unable to extract trunks: %v", err) } for _, trunk := range allTrunks { tools.PrintResource(t, trunk) } } func TestTrunkSubportOperation(t *testing.T) { clients.SkipRelease(t, "stable/mitaka") client, err := clients.NewNetworkV2Client() if err != nil { t.Fatalf("Unable to create a network client: %v", err) } // Create Network network, err := v2.CreateNetwork(t, client) if err != nil { t.Fatalf("Unable to create network: %v", err) } defer v2.DeleteNetwork(t, client, network.ID) // Create Subnet subnet, err := v2.CreateSubnet(t, client, network.ID) if err != nil { t.Fatalf("Unable to create subnet: %v", err) } defer v2.DeleteSubnet(t, client, subnet.ID) // Create port parentPort, err := v2.CreatePort(t, client, network.ID, subnet.ID) if err != nil { t.Fatalf("Unable to create port: %v", err) } defer v2.DeletePort(t, client, parentPort.ID) subport1, err := v2.CreatePort(t, client, network.ID, subnet.ID) if err != nil { t.Fatalf("Unable to create port: %v", err) } defer v2.DeletePort(t, client, subport1.ID) subport2, err := v2.CreatePort(t, client, network.ID, subnet.ID) if err != nil { t.Fatalf("Unable to create port: %v", err) } defer v2.DeletePort(t, client, subport2.ID) trunk, err := CreateTrunk(t, client, parentPort.ID) if err != nil { t.Fatalf("Unable to create trunk: %v", err) } defer DeleteTrunk(t, client, trunk.ID) // Add subports to the trunk addSubportsOpts := trunks.AddSubportsOpts{ Subports: []trunks.Subport{ { SegmentationID: 1, SegmentationType: "vlan", PortID: subport1.ID, }, { SegmentationID: 11, SegmentationType: "vlan", PortID: subport2.ID, }, }, } updatedTrunk, err := trunks.AddSubports(client, trunk.ID, addSubportsOpts).Extract() if err != nil { t.Fatalf("Unable to add subports to the Trunk: %v", err) } th.AssertEquals(t, 2, len(updatedTrunk.Subports)) th.AssertDeepEquals(t, addSubportsOpts.Subports[0], updatedTrunk.Subports[0]) th.AssertDeepEquals(t, addSubportsOpts.Subports[1], updatedTrunk.Subports[1]) // Remove the Subports from the trunk subRemoveOpts := trunks.RemoveSubportsOpts{ Subports: []trunks.RemoveSubport{ {PortID: subport1.ID}, {PortID: subport2.ID}, }, } updatedAgainTrunk, err := trunks.RemoveSubports(client, trunk.ID, subRemoveOpts).Extract() if err != nil { t.Fatalf("Unable to remove subports from the Trunk: %v", err) } th.AssertDeepEquals(t, trunk.Subports, updatedAgainTrunk.Subports) } func TestTrunkTags(t *testing.T) { clients.SkipRelease(t, "stable/mitaka") clients.SkipRelease(t, "stable/newton") clients.SkipRelease(t, "stable/ocata") client, err := clients.NewNetworkV2Client() if err != nil { t.Fatalf("Unable to create a network client: %v", err) } // Create Network network, err := v2.CreateNetwork(t, client) if err != nil { t.Fatalf("Unable to create network: %v", err) } defer v2.DeleteNetwork(t, client, network.ID) // Create Subnet subnet, err := v2.CreateSubnet(t, client, network.ID) if err != nil { t.Fatalf("Unable to create subnet: %v", err) } defer v2.DeleteSubnet(t, client, subnet.ID) // Create port parentPort, err := v2.CreatePort(t, client, network.ID, subnet.ID) if err != nil { t.Fatalf("Unable to create port: %v", err) } defer v2.DeletePort(t, client, parentPort.ID) subport1, err := v2.CreatePort(t, client, network.ID, subnet.ID) if err != nil { t.Fatalf("Unable to create port: %v", err) } defer v2.DeletePort(t, client, subport1.ID) subport2, err := v2.CreatePort(t, client, network.ID, subnet.ID) if err != nil { t.Fatalf("Unable to create port: %v", err) } defer v2.DeletePort(t, client, subport2.ID) trunk, err := CreateTrunk(t, client, parentPort.ID, subport1.ID, subport2.ID) if err != nil { t.Fatalf("Unable to create trunk: %v", err) } defer DeleteTrunk(t, client, trunk.ID) tagReplaceAllOpts := attributestags.ReplaceAllOpts{ // docs say list of tags, but it's a set e.g no duplicates Tags: []string{"a", "b", "c"}, } tags, err := attributestags.ReplaceAll(client, "trunks", trunk.ID, tagReplaceAllOpts).Extract() if err != nil { t.Fatalf("Unable to set trunk tags: %v", err) } gtrunk, err := trunks.Get(client, trunk.ID).Extract() if err != nil { t.Fatalf("Unable to get trunk: %v", err) } tags = gtrunk.Tags sort.Strings(tags) // Ensure ordering, older OpenStack versions aren't sorted... th.AssertDeepEquals(t, []string{"a", "b", "c"}, tags) // Add a tag err = attributestags.Add(client, "trunks", trunk.ID, "d").ExtractErr() th.AssertNoErr(t, err) // Delete a tag err = attributestags.Delete(client, "trunks", trunk.ID, "a").ExtractErr() th.AssertNoErr(t, err) // Verify expected tags are set in the List response tags, err = attributestags.List(client, "trunks", trunk.ID).Extract() th.AssertNoErr(t, err) sort.Strings(tags) th.AssertDeepEquals(t, []string{"b", "c", "d"}, tags) // Delete all tags err = attributestags.DeleteAll(client, "trunks", trunk.ID).ExtractErr() th.AssertNoErr(t, err) tags, err = attributestags.List(client, "trunks", trunk.ID).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, 0, len(tags)) } vlantransparent/000077500000000000000000000000001367513235700353145ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/networking/v2/extensionsvlantransparent.go000066400000000000000000000060651367513235700410740ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/networking/v2/extensions/vlantransparentpackage v2 import ( "testing" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/vlantransparent" "github.com/gophercloud/gophercloud/openstack/networking/v2/networks" th "github.com/gophercloud/gophercloud/testhelper" ) // VLANTransparentNetwork represents OpenStack V2 Networking Network with the // "vlan-transparent" extension enabled. type VLANTransparentNetwork struct { networks.Network vlantransparent.TransparentExt } // ListVLANTransparentNetworks will list networks with the "vlan-transparent" // extension. An error will be returned networks could not be listed. func ListVLANTransparentNetworks(t *testing.T, client *gophercloud.ServiceClient) ([]*VLANTransparentNetwork, error) { iTrue := true networkListOpts := networks.ListOpts{} listOpts := vlantransparent.ListOptsExt{ ListOptsBuilder: networkListOpts, VLANTransparent: &iTrue, } var allNetworks []*VLANTransparentNetwork t.Log("Attempting to list VLAN-transparent networks") allPages, err := networks.List(client, listOpts).AllPages() if err != nil { return nil, err } err = networks.ExtractNetworksInto(allPages, &allNetworks) if err != nil { return nil, err } t.Log("Successfully retrieved networks.") return allNetworks, nil } // CreateVLANTransparentNetwork will create a network with the // "vlan-transparent" extension. An error will be returned if the network could // not be created. func CreateVLANTransparentNetwork(t *testing.T, client *gophercloud.ServiceClient) (*VLANTransparentNetwork, error) { networkName := tools.RandomString("TESTACC-", 8) networkCreateOpts := networks.CreateOpts{ Name: networkName, } iTrue := true createOpts := vlantransparent.CreateOptsExt{ CreateOptsBuilder: &networkCreateOpts, VLANTransparent: &iTrue, } t.Logf("Attempting to create a VLAN-transparent network: %s", networkName) var network VLANTransparentNetwork err := networks.Create(client, createOpts).ExtractInto(&network) if err != nil { return nil, err } t.Logf("Successfully created the network.") th.AssertEquals(t, networkName, network.Name) return &network, nil } // UpdateVLANTransparentNetwork will update a network with the // "vlan-transparent" extension. An error will be returned if the network could // not be updated. func UpdateVLANTransparentNetwork(t *testing.T, client *gophercloud.ServiceClient, networkID string) (*VLANTransparentNetwork, error) { networkName := tools.RandomString("TESTACC-NEW-", 6) networkUpdateOpts := networks.UpdateOpts{ Name: &networkName, } iFalse := false updateOpts := vlantransparent.UpdateOptsExt{ UpdateOptsBuilder: &networkUpdateOpts, VLANTransparent: &iFalse, } t.Logf("Attempting to update a VLAN-transparent network: %s", networkID) var network VLANTransparentNetwork err := networks.Update(client, networkID, updateOpts).ExtractInto(&network) if err != nil { return nil, err } t.Logf("Successfully updated the network.") th.AssertEquals(t, networkName, network.Name) return &network, nil } vlantransparent_test.go000066400000000000000000000023471367513235700421320ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/networking/v2/extensions/vlantransparent// +build acceptance networking vlantransparent package v2 import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" networkingv2 "github.com/gophercloud/gophercloud/acceptance/openstack/networking/v2" "github.com/gophercloud/gophercloud/acceptance/tools" th "github.com/gophercloud/gophercloud/testhelper" ) func TestVLANTransparentCRUD(t *testing.T) { t.Skip("We don't have VLAN transparent extension in OpenLab.") client, err := clients.NewNetworkV2Client() th.AssertNoErr(t, err) // Create a VLAN transparent network. network, err := CreateVLANTransparentNetwork(t, client) th.AssertNoErr(t, err) defer networkingv2.DeleteNetwork(t, client, network.ID) tools.PrintResource(t, network) // Update the created VLAN transparent network. newNetwork, err := UpdateVLANTransparentNetwork(t, client, network.ID) th.AssertNoErr(t, err) tools.PrintResource(t, newNetwork) // Check that the created VLAN transparent network exists. vlanTransparentNetworks, err := ListVLANTransparentNetworks(t, client) th.AssertNoErr(t, err) var found bool for _, vlanTransparentNetwork := range vlanTransparentNetworks { if vlanTransparentNetwork.ID == network.ID { found = true } } th.AssertEquals(t, found, true) } golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/networking/v2/extensions/vpnaas/000077500000000000000000000000001367513235700334415ustar00rootroot00000000000000group_test.go000066400000000000000000000025771367513235700361170ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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" th "github.com/gophercloud/gophercloud/testhelper" ) func TestGroupList(t *testing.T) { client, err := clients.NewNetworkV2Client() th.AssertNoErr(t, err) allPages, err := endpointgroups.List(client, nil).AllPages() th.AssertNoErr(t, err) allGroups, err := endpointgroups.ExtractEndpointGroups(allPages) th.AssertNoErr(t, err) for _, group := range allGroups { tools.PrintResource(t, group) } } func TestGroupCRUD(t *testing.T) { client, err := clients.NewNetworkV2Client() th.AssertNoErr(t, err) group, err := CreateEndpointGroup(t, client) th.AssertNoErr(t, err) defer DeleteEndpointGroup(t, client, group.ID) tools.PrintResource(t, group) newGroup, err := endpointgroups.Get(client, group.ID).Extract() th.AssertNoErr(t, 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() th.AssertNoErr(t, err) tools.PrintResource(t, updatedGroup) } ikepolicy_test.go000066400000000000000000000026641367513235700367500ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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" th "github.com/gophercloud/gophercloud/testhelper" ) func TestIKEPolicyList(t *testing.T) { client, err := clients.NewNetworkV2Client() th.AssertNoErr(t, err) allPages, err := ikepolicies.List(client, nil).AllPages() th.AssertNoErr(t, err) allPolicies, err := ikepolicies.ExtractPolicies(allPages) th.AssertNoErr(t, err) for _, policy := range allPolicies { tools.PrintResource(t, policy) } } func TestIKEPolicyCRUD(t *testing.T) { client, err := clients.NewNetworkV2Client() th.AssertNoErr(t, err) policy, err := CreateIKEPolicy(t, client) th.AssertNoErr(t, err) defer DeleteIKEPolicy(t, client, policy.ID) tools.PrintResource(t, policy) newPolicy, err := ikepolicies.Get(client, policy.ID).Extract() th.AssertNoErr(t, 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() th.AssertNoErr(t, err) tools.PrintResource(t, updatedPolicy) } ipsecpolicy_test.go000066400000000000000000000025101367513235700372710ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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" th "github.com/gophercloud/gophercloud/testhelper" ) func TestIPSecPolicyList(t *testing.T) { client, err := clients.NewNetworkV2Client() th.AssertNoErr(t, err) allPages, err := ipsecpolicies.List(client, nil).AllPages() th.AssertNoErr(t, err) allPolicies, err := ipsecpolicies.ExtractPolicies(allPages) th.AssertNoErr(t, err) for _, policy := range allPolicies { tools.PrintResource(t, policy) } } func TestIPSecPolicyCRUD(t *testing.T) { client, err := clients.NewNetworkV2Client() th.AssertNoErr(t, err) policy, err := CreateIPSecPolicy(t, client) th.AssertNoErr(t, 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() th.AssertNoErr(t, err) tools.PrintResource(t, policy) newPolicy, err := ipsecpolicies.Get(client, policy.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, newPolicy) } service_test.go000066400000000000000000000024171367513235700364140ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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" th "github.com/gophercloud/gophercloud/testhelper" ) func TestServiceList(t *testing.T) { client, err := clients.NewNetworkV2Client() th.AssertNoErr(t, err) allPages, err := services.List(client, nil).AllPages() th.AssertNoErr(t, err) allServices, err := services.ExtractServices(allPages) th.AssertNoErr(t, err) for _, service := range allServices { tools.PrintResource(t, service) } } func TestServiceCRUD(t *testing.T) { client, err := clients.NewNetworkV2Client() th.AssertNoErr(t, err) router, err := layer3.CreateExternalRouter(t, client) th.AssertNoErr(t, err) defer layer3.DeleteRouter(t, client, router.ID) service, err := CreateService(t, client, router.ID) th.AssertNoErr(t, err) defer DeleteService(t, client, service.ID) newService, err := services.Get(client, service.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, service) tools.PrintResource(t, newService) } siteconnection_test.go000066400000000000000000000055511367513235700400020ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/layer3/routers" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/vpnaas/siteconnections" th "github.com/gophercloud/gophercloud/testhelper" ) func TestConnectionList(t *testing.T) { client, err := clients.NewNetworkV2Client() th.AssertNoErr(t, err) allPages, err := siteconnections.List(client, nil).AllPages() th.AssertNoErr(t, err) allConnections, err := siteconnections.ExtractConnections(allPages) th.AssertNoErr(t, err) for _, connection := range allConnections { tools.PrintResource(t, connection) } } func TestConnectionCRUD(t *testing.T) { client, err := clients.NewNetworkV2Client() th.AssertNoErr(t, err) // Create Network network, err := networks.CreateNetwork(t, client) th.AssertNoErr(t, err) defer networks.DeleteNetwork(t, client, network.ID) // Create Subnet subnet, err := networks.CreateSubnet(t, client, network.ID) th.AssertNoErr(t, err) defer networks.DeleteSubnet(t, client, subnet.ID) router, err := layer3.CreateExternalRouter(t, client) th.AssertNoErr(t, 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() th.AssertNoErr(t, 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) th.AssertNoErr(t, err) defer DeleteService(t, client, service.ID) ikepolicy, err := CreateIKEPolicy(t, client) th.AssertNoErr(t, err) defer DeleteIKEPolicy(t, client, ikepolicy.ID) ipsecpolicy, err := CreateIPSecPolicy(t, client) th.AssertNoErr(t, err) defer DeleteIPSecPolicy(t, client, ipsecpolicy.ID) peerEPGroup, err := CreateEndpointGroup(t, client) th.AssertNoErr(t, err) defer DeleteEndpointGroup(t, client, peerEPGroup.ID) localEPGroup, err := CreateEndpointGroupWithSubnet(t, client, subnet.ID) th.AssertNoErr(t, err) defer DeleteEndpointGroup(t, client, localEPGroup.ID) conn, err := CreateSiteConnection(t, client, ikepolicy.ID, ipsecpolicy.ID, service.ID, peerEPGroup.ID, localEPGroup.ID) th.AssertNoErr(t, err) defer DeleteSiteConnection(t, client, conn.ID) newConnection, err := siteconnections.Get(client, conn.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, conn) tools.PrintResource(t, newConnection) } vpnaas.go000066400000000000000000000216501367513235700352050ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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" th "github.com/gophercloud/gophercloud/testhelper" ) // 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) th.AssertEquals(t, service.Name, 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) th.AssertEquals(t, policy.Name, 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) th.AssertEquals(t, policy.Name, 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) th.AssertEquals(t, group.Name, 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) th.AssertEquals(t, group.Name, groupName) 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) th.AssertEquals(t, group.Name, 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) th.AssertEquals(t, connection.Name, 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) } golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/networking/v2/networking.go000066400000000000000000000411651367513235700324770ustar00rootroot00000000000000package 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" th "github.com/gophercloud/gophercloud/testhelper" ) // PortWithExtraDHCPOpts represents a port with extra DHCP options configuration. type PortWithExtraDHCPOpts struct { ports.Port extradhcpopts.ExtraDHCPOptsExt } // 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) networkDescription := tools.RandomString("TESTACC-DESC-", 8) createOpts := networks.CreateOpts{ Name: networkName, Description: networkDescription, 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.") th.AssertEquals(t, network.Name, networkName) th.AssertEquals(t, network.Description, networkDescription) 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.") th.AssertEquals(t, network.Name, networkName) 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) portDescription := tools.RandomString("TESTACC-DESC-", 8) t.Logf("Attempting to create port: %s", portName) createOpts := ports.CreateOpts{ NetworkID: networkID, Name: portName, Description: portDescription, 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) th.AssertEquals(t, port.Name, portName) th.AssertEquals(t, port.Description, portDescription) 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) th.AssertEquals(t, port.Name, 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) th.AssertEquals(t, port.Name, portName) return newPort, nil } // 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 } // CreatePortWithMultipleFixedIPs will create a port with two FixedIPs on the // specified subnet. An error will be returned if the port could not be created. func CreatePortWithMultipleFixedIPs(t *testing.T, client *gophercloud.ServiceClient, networkID, subnetID string) (*ports.Port, error) { portName := tools.RandomString("TESTACC-", 8) portDescription := tools.RandomString("TESTACC-DESC-", 8) t.Logf("Attempting to create port with two fixed IPs: %s", portName) createOpts := ports.CreateOpts{ NetworkID: networkID, Name: portName, Description: portDescription, AdminStateUp: gophercloud.Enabled, FixedIPs: []ports.IP{ports.IP{SubnetID: subnetID}, 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) th.AssertEquals(t, port.Name, portName) th.AssertEquals(t, port.Description, portDescription) if len(port.FixedIPs) != 2 { t.Fatalf("Failed to create a port with two fixed IPs: %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) subnetDescription := tools.RandomString("TESTACC-DESC-", 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, Description: subnetDescription, 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.") th.AssertEquals(t, subnet.Name, subnetName) th.AssertEquals(t, subnet.Description, subnetDescription) th.AssertEquals(t, subnet.GatewayIP, subnetGateway) th.AssertEquals(t, subnet.CIDR, subnetCIDR) 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) defaultGateway := fmt.Sprintf("192.168.%d.1", 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.") th.AssertEquals(t, subnet.Name, subnetName) th.AssertEquals(t, subnet.GatewayIP, defaultGateway) th.AssertEquals(t, subnet.CIDR, subnetCIDR) 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.") th.AssertEquals(t, subnet.Name, subnetName) th.AssertEquals(t, subnet.GatewayIP, "") th.AssertEquals(t, subnet.CIDR, subnetCIDR) 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.") th.AssertEquals(t, subnet.Name, subnetName) th.AssertEquals(t, subnet.CIDR, subnetCIDR) 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.") th.AssertEquals(t, subnet.Name, subnetName) return subnet, nil } // CreateSubnetWithSubnetPoolPrefixlen will create a subnet associated with the // provided subnetpool on the specified Network ID and with overwritten // prefixlen instead of the default subnetpool prefixlen. // An error will be returned if the subnet or the subnetpool could not be created. func CreateSubnetWithSubnetPoolPrefixlen(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, Prefixlen: 12, } 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.") th.AssertEquals(t, subnet.Name, subnetName) 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 }) } // This is duplicated from https://github.com/gophercloud/utils // so that Gophercloud "core" doesn't have a dependency on the // complementary utils repository. func IDFromName(client *gophercloud.ServiceClient, name string) (string, error) { count := 0 id := "" listOpts := networks.ListOpts{ Name: name, } pages, err := networks.List(client, listOpts).AllPages() if err != nil { return "", err } all, err := networks.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"} } } golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/networking/v2/networks_test.go000066400000000000000000000073561367513235700332270ustar00rootroot00000000000000// +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 TestNetworksExternalList(t *testing.T) { client, err := clients.NewNetworkV2Client() th.AssertNoErr(t, err) choices, err := clients.AcceptanceTestChoicesFromEnv() th.AssertNoErr(t, 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() th.AssertNoErr(t, err) err = networks.ExtractNetworksInto(allPages, &allNetworks) th.AssertNoErr(t, 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() th.AssertNoErr(t, err) v, err := networks.ExtractNetworks(allPages) th.AssertEquals(t, len(v), 0) } func TestNetworksCRUD(t *testing.T) { client, err := clients.NewNetworkV2Client() th.AssertNoErr(t, err) // Create a network network, err := CreateNetwork(t, client) th.AssertNoErr(t, err) defer DeleteNetwork(t, client, network.ID) tools.PrintResource(t, network) newName := tools.RandomString("TESTACC-", 8) newDescription := "" updateOpts := &networks.UpdateOpts{ Name: &newName, Description: &newDescription, } _, err = networks.Update(client, network.ID, updateOpts).Extract() th.AssertNoErr(t, err) newNetwork, err := networks.Get(client, network.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, newNetwork) th.AssertEquals(t, newNetwork.Name, newName) th.AssertEquals(t, newNetwork.Description, newDescription) type networkWithExt struct { networks.Network portsecurity.PortSecurityExt } var allNetworks []networkWithExt allPages, err := networks.List(client, nil).AllPages() th.AssertNoErr(t, err) err = networks.ExtractNetworksInto(allPages, &allNetworks) th.AssertNoErr(t, err) var found bool for _, network := range allNetworks { if network.ID == newNetwork.ID { found = true } } th.AssertEquals(t, found, true) } func TestNetworksPortSecurityCRUD(t *testing.T) { client, err := clients.NewNetworkV2Client() th.AssertNoErr(t, 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) th.AssertNoErr(t, 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) th.AssertNoErr(t, err) tools.PrintResource(t, networkWithExtensions) } golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/networking/v2/ports_test.go000066400000000000000000000231121367513235700325060ustar00rootroot00000000000000// +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" th "github.com/gophercloud/gophercloud/testhelper" ) func TestPortsCRUD(t *testing.T) { client, err := clients.NewNetworkV2Client() th.AssertNoErr(t, err) // Create Network network, err := CreateNetwork(t, client) th.AssertNoErr(t, err) defer DeleteNetwork(t, client, network.ID) // Create Subnet subnet, err := CreateSubnet(t, client, network.ID) th.AssertNoErr(t, err) defer DeleteSubnet(t, client, subnet.ID) // Create port port, err := CreatePort(t, client, network.ID, subnet.ID) th.AssertNoErr(t, 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 := "" newPortDescription := "" updateOpts := ports.UpdateOpts{ Name: &newPortName, Description: &newPortDescription, } newPort, err := ports.Update(client, port.ID, updateOpts).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, newPort) th.AssertEquals(t, newPort.Name, newPortName) th.AssertEquals(t, newPort.Description, newPortDescription) allPages, err := ports.List(client, nil).AllPages() th.AssertNoErr(t, err) allPorts, err := ports.ExtractPorts(allPages) th.AssertNoErr(t, err) var found bool for _, port := range allPorts { if port.ID == newPort.ID { found = true } } th.AssertEquals(t, found, true) } func TestPortsRemoveSecurityGroups(t *testing.T) { client, err := clients.NewNetworkV2Client() th.AssertNoErr(t, err) // Create Network network, err := CreateNetwork(t, client) th.AssertNoErr(t, err) defer DeleteNetwork(t, client, network.ID) // Create Subnet subnet, err := CreateSubnet(t, client, network.ID) th.AssertNoErr(t, err) defer DeleteSubnet(t, client, subnet.ID) // Create port port, err := CreatePort(t, client, network.ID, subnet.ID) th.AssertNoErr(t, err) defer DeletePort(t, client, port.ID) tools.PrintResource(t, port) // Create a Security Group group, err := extensions.CreateSecurityGroup(t, client) th.AssertNoErr(t, 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() th.AssertNoErr(t, err) // Remove the group updateOpts = ports.UpdateOpts{ SecurityGroups: &[]string{}, } newPort, err = ports.Update(client, port.ID, updateOpts).Extract() th.AssertNoErr(t, 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() th.AssertNoErr(t, err) // Create Network network, err := CreateNetwork(t, client) th.AssertNoErr(t, err) defer DeleteNetwork(t, client, network.ID) // Create Subnet subnet, err := CreateSubnet(t, client, network.ID) th.AssertNoErr(t, err) defer DeleteSubnet(t, client, subnet.ID) // Create a Security Group group, err := extensions.CreateSecurityGroup(t, client) th.AssertNoErr(t, err) defer extensions.DeleteSecurityGroup(t, client, group.ID) // Create port port, err := CreatePort(t, client, network.ID, subnet.ID) th.AssertNoErr(t, 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() th.AssertNoErr(t, err) // Update the port again var name = "some_port" updateOpts = ports.UpdateOpts{ Name: &name, } newPort, err = ports.Update(client, port.ID, updateOpts).Extract() th.AssertNoErr(t, 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() th.AssertNoErr(t, err) // Create Network network, err := CreateNetwork(t, client) th.AssertNoErr(t, err) defer DeleteNetwork(t, client, network.ID) // Create Subnet subnet, err := CreateSubnet(t, client, network.ID) th.AssertNoErr(t, err) defer DeleteSubnet(t, client, subnet.ID) // Create port port, err := CreatePortWithNoSecurityGroup(t, client, network.ID, subnet.ID) th.AssertNoErr(t, 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() th.AssertNoErr(t, err) // Create Network network, err := CreateNetwork(t, client) th.AssertNoErr(t, err) defer DeleteNetwork(t, client, network.ID) // Create Subnet subnet, err := CreateSubnet(t, client, network.ID) th.AssertNoErr(t, err) defer DeleteSubnet(t, client, subnet.ID) // Create port port, err := CreatePort(t, client, network.ID, subnet.ID) th.AssertNoErr(t, 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() th.AssertNoErr(t, err) // Remove the address pair updateOpts = ports.UpdateOpts{ AllowedAddressPairs: &[]ports.AddressPair{}, } newPort, err = ports.Update(client, port.ID, updateOpts).Extract() th.AssertNoErr(t, 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() th.AssertNoErr(t, err) // Create Network network, err := CreateNetwork(t, client) th.AssertNoErr(t, err) defer DeleteNetwork(t, client, network.ID) // Create Subnet subnet, err := CreateSubnet(t, client, network.ID) th.AssertNoErr(t, err) defer DeleteSubnet(t, client, subnet.ID) // Create port port, err := CreatePort(t, client, network.ID, subnet.ID) th.AssertNoErr(t, 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() th.AssertNoErr(t, err) tools.PrintResource(t, newPort) // Remove the address pair var name = "some_port" updateOpts = ports.UpdateOpts{ Name: &name, } newPort, err = ports.Update(client, port.ID, updateOpts).Extract() th.AssertNoErr(t, 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() th.AssertNoErr(t, err) // Create Network network, err := CreateNetwork(t, client) th.AssertNoErr(t, err) defer DeleteNetwork(t, client, network.ID) // Create Subnet subnet, err := CreateSubnet(t, client, network.ID) th.AssertNoErr(t, err) defer DeleteSubnet(t, client, subnet.ID) // Create port port, err := CreatePortWithoutPortSecurity(t, client, network.ID, subnet.ID) th.AssertNoErr(t, err) defer DeletePort(t, client, port.ID) var portWithExt struct { ports.Port portsecurity.PortSecurityExt } err = ports.Get(client, port.ID).ExtractInto(&portWithExt) th.AssertNoErr(t, 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) th.AssertNoErr(t, err) tools.PrintResource(t, portWithExt) } func TestPortsWithExtraDHCPOptsCRUD(t *testing.T) { client, err := clients.NewNetworkV2Client() th.AssertNoErr(t, err) // Create a Network network, err := CreateNetwork(t, client) th.AssertNoErr(t, err) defer DeleteNetwork(t, client, network.ID) // Create a Subnet subnet, err := CreateSubnet(t, client, network.ID) th.AssertNoErr(t, err) defer DeleteSubnet(t, client, subnet.ID) // Create a port with extra DHCP options. port, err := CreatePortWithExtraDHCPOpts(t, client, network.ID, subnet.ID) th.AssertNoErr(t, 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) th.AssertNoErr(t, err) tools.PrintResource(t, newPort) } golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/networking/v2/subnets_test.go000066400000000000000000000155461367513235700330360ustar00rootroot00000000000000// +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" th "github.com/gophercloud/gophercloud/testhelper" ) func TestSubnetCRUD(t *testing.T) { client, err := clients.NewNetworkV2Client() th.AssertNoErr(t, err) // Create Network network, err := CreateNetwork(t, client) th.AssertNoErr(t, err) defer DeleteNetwork(t, client, network.ID) // Create Subnet subnet, err := CreateSubnet(t, client, network.ID) th.AssertNoErr(t, err) defer DeleteSubnet(t, client, subnet.ID) tools.PrintResource(t, subnet) // Update Subnet newSubnetName := tools.RandomString("TESTACC-", 8) newSubnetDescription := "" updateOpts := subnets.UpdateOpts{ Name: &newSubnetName, Description: &newSubnetDescription, } _, err = subnets.Update(client, subnet.ID, updateOpts).Extract() th.AssertNoErr(t, err) // Get subnet newSubnet, err := subnets.Get(client, subnet.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, newSubnet) th.AssertEquals(t, newSubnet.Name, newSubnetName) th.AssertEquals(t, newSubnet.Description, newSubnetDescription) allPages, err := subnets.List(client, nil).AllPages() th.AssertNoErr(t, err) allSubnets, err := subnets.ExtractSubnets(allPages) th.AssertNoErr(t, err) var found bool for _, subnet := range allSubnets { if subnet.ID == newSubnet.ID { found = true } } th.AssertEquals(t, found, true) } func TestSubnetsDefaultGateway(t *testing.T) { client, err := clients.NewNetworkV2Client() th.AssertNoErr(t, err) // Create Network network, err := CreateNetwork(t, client) th.AssertNoErr(t, err) defer DeleteNetwork(t, client, network.ID) // Create Subnet subnet, err := CreateSubnetWithDefaultGateway(t, client, network.ID) th.AssertNoErr(t, 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() th.AssertNoErr(t, err) if newSubnet.GatewayIP != "" { t.Fatalf("Gateway was not updated correctly") } } func TestSubnetsNoGateway(t *testing.T) { client, err := clients.NewNetworkV2Client() th.AssertNoErr(t, err) // Create Network network, err := CreateNetwork(t, client) th.AssertNoErr(t, err) defer DeleteNetwork(t, client, network.ID) // Create Subnet subnet, err := CreateSubnetWithNoGateway(t, client, network.ID) th.AssertNoErr(t, 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() th.AssertNoErr(t, err) if newSubnet.GatewayIP == "" { t.Fatalf("Gateway was not updated correctly") } } func TestSubnetsWithSubnetPool(t *testing.T) { client, err := clients.NewNetworkV2Client() th.AssertNoErr(t, err) // Create Network network, err := CreateNetwork(t, client) th.AssertNoErr(t, err) defer DeleteNetwork(t, client, network.ID) // Create SubnetPool subnetPool, err := subnetpools.CreateSubnetPool(t, client) th.AssertNoErr(t, err) defer subnetpools.DeleteSubnetPool(t, client, subnetPool.ID) // Create Subnet subnet, err := CreateSubnetWithSubnetPool(t, client, network.ID, subnetPool.ID) th.AssertNoErr(t, 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() th.AssertNoErr(t, err) // Create Network network, err := CreateNetwork(t, client) th.AssertNoErr(t, err) defer DeleteNetwork(t, client, network.ID) // Create SubnetPool subnetPool, err := subnetpools.CreateSubnetPool(t, client) th.AssertNoErr(t, err) defer subnetpools.DeleteSubnetPool(t, client, subnetPool.ID) // Create Subnet subnet, err := CreateSubnetWithSubnetPoolNoCIDR(t, client, network.ID, subnetPool.ID) th.AssertNoErr(t, err) defer DeleteSubnet(t, client, subnet.ID) tools.PrintResource(t, subnet) if subnet.GatewayIP == "" { t.Fatalf("A subnet pool was not associated.") } } func TestSubnetsWithSubnetPoolPrefixlen(t *testing.T) { client, err := clients.NewNetworkV2Client() th.AssertNoErr(t, err) // Create Network network, err := CreateNetwork(t, client) th.AssertNoErr(t, err) defer DeleteNetwork(t, client, network.ID) // Create SubnetPool subnetPool, err := subnetpools.CreateSubnetPool(t, client) th.AssertNoErr(t, err) defer subnetpools.DeleteSubnetPool(t, client, subnetPool.ID) // Create Subnet subnet, err := CreateSubnetWithSubnetPoolPrefixlen(t, client, network.ID, subnetPool.ID) th.AssertNoErr(t, err) defer DeleteSubnet(t, client, subnet.ID) tools.PrintResource(t, subnet) if subnet.GatewayIP == "" { t.Fatalf("A subnet pool was not associated.") } cidrParts := strings.Split(subnet.CIDR, "/") if len(cidrParts) != 2 { t.Fatalf("Got invalid CIDR for subnet '%s': %s", subnet.ID, subnet.CIDR) } if cidrParts[1] != "12" { t.Fatalf("Got invalid prefix length for subnet '%s': wanted 12 but got '%s'", subnet.ID, cidrParts[1]) } } func TestSubnetDNSNameservers(t *testing.T) { client, err := clients.NewNetworkV2Client() th.AssertNoErr(t, err) // Create Network network, err := CreateNetwork(t, client) th.AssertNoErr(t, err) defer DeleteNetwork(t, client, network.ID) // Create Subnet subnet, err := CreateSubnet(t, client, network.ID) th.AssertNoErr(t, err) defer DeleteSubnet(t, client, subnet.ID) tools.PrintResource(t, subnet) // Update Subnet dnsNameservers := []string{"1.1.1.1"} updateOpts := subnets.UpdateOpts{ DNSNameservers: &dnsNameservers, } _, err = subnets.Update(client, subnet.ID, updateOpts).Extract() th.AssertNoErr(t, err) // Get subnet newSubnet, err := subnets.Get(client, subnet.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, newSubnet) th.AssertEquals(t, len(newSubnet.DNSNameservers), 1) // Update Subnet again dnsNameservers = []string{} updateOpts = subnets.UpdateOpts{ DNSNameservers: &dnsNameservers, } _, err = subnets.Update(client, subnet.ID, updateOpts).Extract() th.AssertNoErr(t, err) // Get subnet newSubnet, err = subnets.Get(client, subnet.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, newSubnet) th.AssertEquals(t, len(newSubnet.DNSNameservers), 0) } golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/objectstorage/000077500000000000000000000000001367513235700300675ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/objectstorage/v1/000077500000000000000000000000001367513235700304155ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/objectstorage/v1/accounts_test.go000066400000000000000000000026411367513235700336250ustar00rootroot00000000000000// +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.go000066400000000000000000000135511367513235700340760ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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() { temp := []string{} for k, _ := range metadata { temp = append(temp, k) } res := containers.Update(client, cNames[0], &containers.UpdateOpts{RemoveMetadata: temp}) th.AssertNoErr(t, res.Err) // confirm the metadata was removed getOpts := containers.GetOpts{ Newest: true, } cm, err := containers.Get(client, cNames[0], getOpts).ExtractMetadata() th.AssertNoErr(t, err) for k, _ := range metadata { if _, ok := cm[k]; ok { t.Errorf("Unexpected custom metadata with key: %s", k) } } }() // 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)) } func TestBulkDeleteContainers(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("test&happy?-", 8) } // Create numContainers containers. for i := 0; i < len(cNames); i++ { res := containers.Create(client, cNames[i], nil) th.AssertNoErr(t, res.Err) } expectedResp := containers.BulkDeleteResponse{ ResponseStatus: "200 OK", Errors: [][]string{}, NumberDeleted: numContainers, } resp, err := containers.BulkDelete(client, cNames).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, *resp) th.AssertDeepEquals(t, *resp, expectedResp) for _, c := range cNames { _, err = containers.Get(client, c, nil).Extract() th.AssertErr(t, err) } } golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/objectstorage/v1/objects_test.go000066400000000000000000000226131367513235700334400ustar00rootroot00000000000000// +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) } func TestObjectsBulkDelete(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("don't worry & be happy?-", 8) cSubdir2 := tools.RandomString("don't worry & be happy?-", 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("stranger?things-", 8) } oNames2 := make([]string, numObjects) for i := 0; i < len(oNames2); i++ { oNames2[i] = cSubdir2 + "/" + tools.RandomString("freddy's coming for you?-", 8) } // Create a container to hold the test objects. cName := tools.RandomString("test&happy?-", 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) } expectedResp := objects.BulkDeleteResponse{ ResponseStatus: "200 OK", Errors: [][]string{}, NumberDeleted: numObjects * 2, } 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. resp, err := objects.BulkDelete(client, cName, append(oNames1, oNames2...)).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, *resp, expectedResp) // Verify deletion 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) } th.AssertEquals(t, len(allObjects), 0) } golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/objectstorage/v1/pkg.go000066400000000000000000000000131367513235700315170ustar00rootroot00000000000000package v1 golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/orchestration/000077500000000000000000000000001367513235700301205ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/orchestration/v1/000077500000000000000000000000001367513235700304465ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/orchestration/v1/buildinfo_test.go000066400000000000000000000007741367513235700340170ustar00rootroot00000000000000// +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) { clients.SkipRelease(t, "stable/mitaka") 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) } golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/orchestration/v1/orchestration.go000066400000000000000000000077741367513235700337000ustar00rootroot00000000000000package 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 }) } // CreateStackWithFile will create a heat stack with a randomly generated name that uses get_file. // An error will be returned if the stack failed to be created. func CreateStackWithFile(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(`heat_template_version: 2015-04-30 resources: test_resource: type: OS::Heat::TestResource properties: value: get_file: testdata/samplefile`) createOpts := stacks.CreateOpts{ Name: stackName, Timeout: 1, 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 } stackevents_test.go000066400000000000000000000020611367513235700343060ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/orchestration/v1// +build acceptance package v1 import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/openstack/orchestration/v1/stackevents" th "github.com/gophercloud/gophercloud/testhelper" ) func TestStackEvents(t *testing.T) { clients.SkipRelease(t, "stable/mitaka") 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.go000066400000000000000000000034111367513235700350140ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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) { clients.SkipRelease(t, "stable/mitaka") 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) markUnhealthyOpts := &stackresources.MarkUnhealthyOpts{ MarkUnhealthy: true, ResourceStatusReason: "Wrong security policy is detected.", } err = stackresources.MarkUnhealthy(client, stack.Name, stack.ID, basicTemplateResourceName, markUnhealthyOpts).ExtractErr() th.AssertNoErr(t, err) unhealthyResource, err := stackresources.Get(client, stack.Name, stack.ID, basicTemplateResourceName).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, "CHECK_FAILED", unhealthyResource.Status) tools.PrintResource(t, unhealthyResource) 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) } golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/orchestration/v1/stacks_test.go000066400000000000000000000025051367513235700333260ustar00rootroot00000000000000// +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) { clients.SkipRelease(t, "stable/mitaka") 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.go000066400000000000000000000027641367513235700350120ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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) { clients.SkipRelease(t, "stable/mitaka") 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) { clients.SkipRelease(t, "stable/mitaka") 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) } func TestStackTemplateWithFile(t *testing.T) { clients.SkipRelease(t, "stable/mitaka") client, err := clients.NewOrchestrationV1Client() th.AssertNoErr(t, err) stack, err := CreateStackWithFile(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) } golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/orchestration/v1/testdata/000077500000000000000000000000001367513235700322575ustar00rootroot00000000000000samplefile000066400000000000000000000000271367513235700342430ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/orchestration/v1/testdata@this is not valid yamlgolang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/pkg.go000066400000000000000000000000501367513235700263370ustar00rootroot00000000000000// +build acceptance package openstack golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/placement/000077500000000000000000000000001367513235700272045ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/placement/v1/000077500000000000000000000000001367513235700275325ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/placement/v1/pkg.go000066400000000000000000000001371367513235700306430ustar00rootroot00000000000000// Package placement contains acceptance tests for the Openstack Placement service. package v1 resourceproviders_test.go000066400000000000000000000063601367513235700346330ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/placement/v1package v1 import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/placement/v1/resourceproviders" th "github.com/gophercloud/gophercloud/testhelper" ) func TestResourceProviderList(t *testing.T) { clients.RequireAdmin(t) client, err := clients.NewPlacementV1Client() th.AssertNoErr(t, err) allPages, err := resourceproviders.List(client, resourceproviders.ListOpts{}).AllPages() th.AssertNoErr(t, err) allResourceProviders, err := resourceproviders.ExtractResourceProviders(allPages) th.AssertNoErr(t, err) for _, v := range allResourceProviders { tools.PrintResource(t, v) } } func TestResourceProviderCreate(t *testing.T) { clients.RequireAdmin(t) client, err := clients.NewPlacementV1Client() th.AssertNoErr(t, err) name := tools.RandomString("TESTACC-", 8) t.Logf("Attempting to create resource provider: %s", name) createOpts := resourceproviders.CreateOpts{ Name: name, } client.Microversion = "1.20" resourceProvider, err := resourceproviders.Create(client, createOpts).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, resourceProvider) } func TestResourceProviderUsages(t *testing.T) { clients.RequireAdmin(t) client, err := clients.NewPlacementV1Client() th.AssertNoErr(t, err) // first create new resource provider name := tools.RandomString("TESTACC-", 8) t.Logf("Attempting to create resource provider: %s", name) createOpts := resourceproviders.CreateOpts{ Name: name, } client.Microversion = "1.20" resourceProvider, err := resourceproviders.Create(client, createOpts).Extract() th.AssertNoErr(t, err) // now get the usages for the newly created resource provider usage, err := resourceproviders.GetUsages(client, resourceProvider.UUID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, usage) } func TestResourceProviderInventories(t *testing.T) { clients.RequireAdmin(t) client, err := clients.NewPlacementV1Client() th.AssertNoErr(t, err) // first create new resource provider name := tools.RandomString("TESTACC-", 8) t.Logf("Attempting to create resource provider: %s", name) createOpts := resourceproviders.CreateOpts{ Name: name, } client.Microversion = "1.20" resourceProvider, err := resourceproviders.Create(client, createOpts).Extract() th.AssertNoErr(t, err) // now get the inventories for the newly created resource provider usage, err := resourceproviders.GetInventories(client, resourceProvider.UUID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, usage) } func TestResourceProviderTraits(t *testing.T) { clients.RequireAdmin(t) client, err := clients.NewPlacementV1Client() th.AssertNoErr(t, err) // first create new resource provider name := tools.RandomString("TESTACC-", 8) t.Logf("Attempting to create resource provider: %s", name) createOpts := resourceproviders.CreateOpts{ Name: name, } client.Microversion = "1.20" resourceProvider, err := resourceproviders.Create(client, createOpts).Extract() th.AssertNoErr(t, err) // now get the traits for the newly created resource provider usage, err := resourceproviders.GetTraits(client, resourceProvider.UUID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, usage) } golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/sharedfilesystems/000077500000000000000000000000001367513235700307725ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/sharedfilesystems/v2/000077500000000000000000000000001367513235700313215ustar00rootroot00000000000000availabilityzones_test.go000066400000000000000000000013571367513235700363670ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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") } } golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/sharedfilesystems/v2/messages/000077500000000000000000000000001367513235700331305ustar00rootroot00000000000000messages.go000066400000000000000000000010411367513235700352030ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/sharedfilesystems/v2/messagespackage messages import ( "testing" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack/sharedfilesystems/v2/messages" ) // DeleteMessage will delete a message. An error will occur if // the message was unable to be deleted. func DeleteMessage(t *testing.T, client *gophercloud.ServiceClient, message *messages.Message) { err := messages.Delete(client, message.ID).ExtractErr() if err != nil { t.Fatalf("Failed to delete message %s: %v", message.ID, err) } t.Logf("Deleted message: %s", message.ID) } messages_test.go000066400000000000000000000057541367513235700362610ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/sharedfilesystems/v2/messagespackage messages import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/sharedfilesystems/v2/messages" ) const requestID = "req-6f52cd8b-25a1-42cf-b497-7babf70f55f4" const minimumManilaMessagesMicroVersion = "2.37" func TestMessageList(t *testing.T) { client, err := clients.NewSharedFileSystemV2Client() if err != nil { t.Fatalf("Unable to create a shared file system client: %v", err) } client.Microversion = minimumManilaMessagesMicroVersion allPages, err := messages.List(client, messages.ListOpts{}).AllPages() if err != nil { t.Fatalf("Unable to retrieve messages: %v", err) } allMessages, err := messages.ExtractMessages(allPages) if err != nil { t.Fatalf("Unable to extract messages: %v", err) } for _, message := range allMessages { tools.PrintResource(t, message) } } // The test creates 2 messages and verifies that only the one(s) with // a particular name are being listed func TestMessageListFiltering(t *testing.T) { client, err := clients.NewSharedFileSystemV2Client() if err != nil { t.Fatalf("Unable to create a shared file system client: %v", err) } client.Microversion = minimumManilaMessagesMicroVersion options := messages.ListOpts{ RequestID: requestID, } allPages, err := messages.List(client, options).AllPages() if err != nil { t.Fatalf("Unable to retrieve messages: %v", err) } allMessages, err := messages.ExtractMessages(allPages) if err != nil { t.Fatalf("Unable to extract messages: %v", err) } for _, listedMessage := range allMessages { if listedMessage.RequestID != options.RequestID { t.Fatalf("The request id of the message was expected to be %s", options.RequestID) } tools.PrintResource(t, listedMessage) } } // Create a message and update the name and description. Get the ity // service and verify that the name and description have been updated func TestMessageDelete(t *testing.T) { client, err := clients.NewSharedFileSystemV2Client() if err != nil { t.Fatalf("Unable to create shared file system client: %v", err) } client.Microversion = minimumManilaMessagesMicroVersion options := messages.ListOpts{ RequestID: requestID, } allPages, err := messages.List(client, options).AllPages() if err != nil { t.Fatalf("Unable to retrieve messages: %v", err) } allMessages, err := messages.ExtractMessages(allPages) if err != nil { t.Fatalf("Unable to extract messages: %v", err) } if len(allMessages) == 0 { t.Skipf("No messages were found") } var messageID string for _, listedMessage := range allMessages { if listedMessage.RequestID != options.RequestID { t.Fatalf("The request id of the message was expected to be %s", options.RequestID) } tools.PrintResource(t, listedMessage) messageID = listedMessage.ID } message, err := messages.Get(client, messageID).Extract() if err != nil { t.Fatalf("Unable to retrieve the message: %v", err) } DeleteMessage(t, client, message) } pkg.go000066400000000000000000000001531367513235700341600ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/sharedfilesystems/v2/messages// The v2 package contains acceptance tests for the Openstack Manila V2 messages package package messages golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/sharedfilesystems/v2/pkg.go000066400000000000000000000001351367513235700324300ustar00rootroot00000000000000// The v2 package contains acceptance tests for the Openstack Manila V2 service. package v2 securityservices.go000066400000000000000000000031131367513235700352020ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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) securityServiceDescription := tools.RandomString("ACPTTEST-DESC", 16) t.Logf("Attempting to create security service: %s", securityServiceName) createOpts := securityservices.CreateOpts{ Name: securityServiceName, Description: securityServiceDescription, 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) } securityservices_test.go000066400000000000000000000107011367513235700362420ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/sharedfilesystems/v2package v2 import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "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) } if newSecurityService.Description != securityService.Description { t.Fatalf("Security service description was expeted to be: %s", securityService.Description) } tools.PrintResource(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 { tools.PrintResource(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) } tools.PrintResource(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) } name := "NewName" description := "" options := securityservices.UpdateOpts{ Name: &name, Description: &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 != name { t.Fatalf("Security service name was expeted to be: %s", name) } if newSecurityService.Description != description { t.Fatalf("Security service description was expeted to be: %s", description) } if newSecurityService.Type != options.Type { t.Fatalf("Security service type was expected to be: %s", options.Type) } tools.PrintResource(t, securityService) defer DeleteSecurityService(t, client, securityService) } sharenetworks.go000066400000000000000000000031471367513235700344750ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/sharedfilesystems/v2package v2 import ( "testing" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/acceptance/clients" "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.") } choices, err := clients.AcceptanceTestChoicesFromEnv() if err != nil { return nil, err } shareNetworkName := tools.RandomString("ACPTTEST", 16) t.Logf("Attempting to create share network: %s", shareNetworkName) createOpts := sharenetworks.CreateOpts{ Name: shareNetworkName, NeutronNetID: choices.NetworkID, NeutronSubnetID: choices.SubnetID, 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, shareNetworkID string) { err := sharenetworks.Delete(client, shareNetworkID).ExtractErr() if err != nil { t.Fatalf("Failed to delete share network %s: %v", shareNetworkID, err) } t.Logf("Deleted share network: %s", shareNetworkID) } sharenetworks_test.go000066400000000000000000000150501367513235700355300ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/sharedfilesystems/v2package v2 import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "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) } if newShareNetwork.Description != shareNetwork.Description { t.Fatalf("Share network description was expeted to be: %s", shareNetwork.Description) } tools.PrintResource(t, shareNetwork) defer DeleteShareNetwork(t, client, shareNetwork.ID) } // 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) } name := "NewName" description := "" options := sharenetworks.UpdateOpts{ Name: &name, Description: &description, } expectedShareNetwork.Name = name expectedShareNetwork.Description = 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) tools.PrintResource(t, shareNetwork) defer DeleteShareNetwork(t, client, shareNetwork.ID) } 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 { tools.PrintResource(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.ID) shareNetwork, err = CreateShareNetwork(t, client) if err != nil { t.Fatalf("Unable to create share network: %v", err) } defer DeleteShareNetwork(t, client, shareNetwork.ID) 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) } tools.PrintResource(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.ID) shareNetwork, err = CreateShareNetwork(t, client) if err != nil { t.Fatalf("Unable to create share network: %v", err) } defer DeleteShareNetwork(t, client, shareNetwork.ID) 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.ID) 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) } tools.PrintResource(t, shareNetwork) } golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/sharedfilesystems/v2/shares.go000066400000000000000000000117331367513235700331420ustar00rootroot00000000000000package v2 import ( "fmt" "strings" "testing" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack/sharedfilesystems/v2/messages" "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.") } iTrue := true createOpts := shares.CreateOpts{ Size: 1, Name: "My Test Share", Description: "My Test Description", ShareProto: "NFS", ShareType: "dhss_false", IsPublic: &iTrue, } share, err := shares.Create(client, createOpts).Extract() if err != nil { t.Logf("Failed to create share") return nil, err } err = waitForStatus(t, client, share.ID, "available", 600) if err != nil { t.Logf("Failed to get %s share status", share.ID) DeleteShare(t, client, share) 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: "ro", }).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 { if _, ok := err.(gophercloud.ErrDefault404); ok { return } t.Errorf("Unable to delete share %s: %v", share.ID, err) } err = waitForStatus(t, client, share.ID, "deleted", 600) if err != nil { t.Errorf("Failed to wait for 'deleted' status for %s share: %v", share.ID, err) } else { t.Logf("Deleted share: %s", share.ID) } } // 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 PrintMessages(t *testing.T, c *gophercloud.ServiceClient, id string) error { c.Microversion = "2.37" allPages, err := messages.List(c, messages.ListOpts{ResourceID: id}).AllPages() if err != nil { return fmt.Errorf("Unable to retrieve messages: %v", err) } allMessages, err := messages.ExtractMessages(allPages) if err != nil { return fmt.Errorf("Unable to extract messages: %v", err) } for _, message := range allMessages { tools.PrintResource(t, message) } return nil } func waitForStatus(t *testing.T, c *gophercloud.ServiceClient, id, status string, secs int) error { err := gophercloud.WaitFor(secs, func() (bool, error) { current, err := shares.Get(c, id).Extract() if err != nil { if _, ok := err.(gophercloud.ErrDefault404); ok { switch status { case "deleted": return true, nil default: return false, err } } return false, err } if current.Status == status { return true, nil } if strings.Contains(current.Status, "error") { return true, fmt.Errorf("An error occurred, wrong status: %s", current.Status) } return false, nil }) if err != nil { mErr := PrintMessages(t, c, id) if mErr != nil { return fmt.Errorf("Share status is '%s' and unable to get manila messages: %s", err, mErr) } } return err } shares_test.go000066400000000000000000000302411367513235700341150ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/sharedfilesystems/v2package v2 import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/sharedfilesystems/v2/shares" th "github.com/gophercloud/gophercloud/testhelper" ) func TestShareCreate(t *testing.T) { clients.SkipRelease(t, "stable/mitaka") clients.SkipRelease(t, "stable/newton") client, err := clients.NewSharedFileSystemV2Client() if err != nil { t.Fatalf("Unable to create a shared file system 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) } tools.PrintResource(t, created) } func TestShareExportLocations(t *testing.T) { clients.SkipRelease(t, "stable/mitaka") clients.SkipRelease(t, "stable/newton") client, err := clients.NewSharedFileSystemV2Client() if err != nil { t.Fatalf("Unable to create a shared file system 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 = waitForStatus(t, client, share.ID, "available", 120) if err != nil { t.Fatalf("Share status error: %v", err) } client.Microversion = "2.9" exportLocations, err := shares.ListExportLocations(client, share.ID).Extract() if err != nil { t.Errorf("Unable to list share export locations: %v", err) } tools.PrintResource(t, exportLocations) exportLocation, err := shares.GetExportLocation(client, share.ID, exportLocations[0].ID).Extract() if err != nil { t.Errorf("Unable to get share export location: %v", err) } tools.PrintResource(t, exportLocation) th.AssertEquals(t, exportLocations[0], *exportLocation) } func TestShareUpdate(t *testing.T) { clients.SkipRelease(t, "stable/mitaka") clients.SkipRelease(t, "stable/newton") client, err := clients.NewSharedFileSystemV2Client() if err != nil { t.Fatalf("Unable to create shared file system client: %v", err) } share, err := CreateShare(t, client) if err != nil { t.Fatalf("Unable to create share: %v", err) } defer DeleteShare(t, client, share) expectedShare, err := shares.Get(client, share.ID).Extract() if err != nil { t.Errorf("Unable to retrieve share: %v", err) } name := "NewName" description := "" iFalse := false options := shares.UpdateOpts{ DisplayName: &name, DisplayDescription: &description, IsPublic: &iFalse, } expectedShare.Name = name expectedShare.Description = description expectedShare.IsPublic = iFalse _, err = shares.Update(client, share.ID, options).Extract() if err != nil { t.Errorf("Unable to update share: %v", err) } updatedShare, err := shares.Get(client, share.ID).Extract() if err != nil { t.Errorf("Unable to retrieve share: %v", err) } // Update time has to be set in order to get the assert equal to pass expectedShare.UpdatedAt = updatedShare.UpdatedAt tools.PrintResource(t, share) th.CheckDeepEquals(t, expectedShare, updatedShare) } func TestShareListDetail(t *testing.T) { clients.SkipRelease(t, "stable/mitaka") clients.SkipRelease(t, "stable/newton") client, err := clients.NewSharedFileSystemV2Client() if err != nil { t.Fatalf("Unable to create a shared file system 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 { tools.PrintResource(t, &ss[i]) } } func TestGrantAndRevokeAccess(t *testing.T) { t.Skip("Currently failing in OpenLab") clients.SkipRelease(t, "stable/mitaka") clients.SkipRelease(t, "stable/newton") client, err := clients.NewSharedFileSystemV2Client() if err != nil { t.Fatalf("Unable to create a shared file system client: %v", err) } client.Microversion = "2.49" 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) } tools.PrintResource(t, accessRight) if err = RevokeAccess(t, client, share, accessRight); err != nil { t.Fatalf("Unable to revoke access: %v", err) } } func TestListAccessRights(t *testing.T) { t.Skip("Currently failing in OpenLab") clients.SkipRelease(t, "stable/mitaka") clients.SkipRelease(t, "stable/newton") client, err := clients.NewSharedFileSystemV2Client() if err != nil { t.Fatalf("Unable to create a shared file system client: %v", err) } client.Microversion = "2.7" 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 { tools.PrintResource(t, &r) } } func TestExtendAndShrink(t *testing.T) { clients.SkipRelease(t, "stable/mitaka") clients.SkipRelease(t, "stable/newton") client, err := clients.NewSharedFileSystemV2Client() if err != nil { t.Fatalf("Unable to create a shared file system client: %v", err) } client.Microversion = "2.7" 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(t, client, share.ID, "available", 120) if err != nil { t.Fatalf("Share status error: %v", err) } t.Logf("Share %s successfuly extended", share.ID) /* disable shrinking for the LVM dhss=false 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(t, client, share.ID, "available", 300) if err != nil { t.Fatalf("Share status error: %v", err) } t.Logf("Share %s successfuly shrunk", share.ID) */ } func TestShareMetadata(t *testing.T) { clients.SkipRelease(t, "stable/mitaka") clients.SkipRelease(t, "stable/newton") client, err := clients.NewSharedFileSystemV2Client() if err != nil { t.Fatalf("Unable to create a shared file system client: %v", err) } client.Microversion = "2.7" share, err := CreateShare(t, client) if err != nil { t.Fatalf("Unable to create a share: %v", err) } defer DeleteShare(t, client, share) const ( k = "key" v1 = "value1" v2 = "value2" ) checkMetadataEq := func(m map[string]string, value string) { if m == nil || len(m) != 1 || m[k] != value { t.Fatalf("Unexpected metadata contents %v", m) } } metadata, err := shares.SetMetadata(client, share.ID, shares.SetMetadataOpts{Metadata: map[string]string{k: v1}}).Extract() if err != nil { t.Fatalf("Unable to set share metadata: %v", err) } checkMetadataEq(metadata, v1) metadata, err = shares.UpdateMetadata(client, share.ID, shares.UpdateMetadataOpts{Metadata: map[string]string{k: v2}}).Extract() if err != nil { t.Fatalf("Unable to update share metadata: %v", err) } checkMetadataEq(metadata, v2) metadata, err = shares.GetMetadatum(client, share.ID, k).Extract() if err != nil { t.Fatalf("Unable to get share metadatum: %v", err) } checkMetadataEq(metadata, v2) err = shares.DeleteMetadatum(client, share.ID, k).ExtractErr() if err != nil { t.Fatalf("Unable to delete share metadatum: %v", err) } metadata, err = shares.GetMetadata(client, share.ID).Extract() if err != nil { t.Fatalf("Unable to get share metadata: %v", err) } if metadata == nil || len(metadata) != 0 { t.Fatalf("Unexpected metadata contents %v, expected an empty map", metadata) } } func TestRevert(t *testing.T) { clients.SkipRelease(t, "stable/mitaka") clients.SkipRelease(t, "stable/newton") client, err := clients.NewSharedFileSystemV2Client() if err != nil { t.Fatalf("Unable to create a shared file system client: %v", err) } client.Microversion = "2.27" share, err := CreateShare(t, client) if err != nil { t.Fatalf("Unable to create a share: %v", err) } defer DeleteShare(t, client, share) err = waitForStatus(t, client, share.ID, "available", 120) if err != nil { t.Fatalf("Share status error: %v", err) } snapshot, err := CreateSnapshot(t, client, share.ID) if err != nil { t.Fatalf("Unable to create a snapshot: %v", err) } defer DeleteSnapshot(t, client, snapshot) err = waitForSnapshotStatus(t, client, snapshot.ID, "available", 120) if err != nil { t.Fatalf("Snapshot status error: %v", err) } revertOpts := &shares.RevertOpts{ SnapshotID: snapshot.ID, } err = shares.Revert(client, share.ID, revertOpts).ExtractErr() if err != nil { t.Fatalf("Unable to revert a snapshot: %v", err) } // We need to wait till the Extend operation is done err = waitForStatus(t, client, share.ID, "available", 120) if err != nil { t.Fatalf("Share status error: %v", err) } t.Logf("Share %s successfuly reverted", share.ID) } func TestResetStatus(t *testing.T) { clients.SkipRelease(t, "stable/mitaka") clients.SkipRelease(t, "stable/newton") client, err := clients.NewSharedFileSystemV2Client() if err != nil { t.Fatalf("Unable to create a shared file system client: %v", err) } client.Microversion = "2.7" share, err := CreateShare(t, client) if err != nil { t.Fatalf("Unable to create a share: %v", err) } defer DeleteShare(t, client, share) err = waitForStatus(t, client, share.ID, "available", 120) if err != nil { t.Fatalf("Share status error: %v", err) } resetStatusOpts := &shares.ResetStatusOpts{ Status: "error", } err = shares.ResetStatus(client, share.ID, resetStatusOpts).ExtractErr() if err != nil { t.Fatalf("Unable to reset a share status: %v", err) } // We need to wait till the Extend operation is done err = waitForStatus(t, client, share.ID, "error", 120) if err != nil { t.Fatalf("Share status error: %v", err) } t.Logf("Share %s status successfuly reset", share.ID) } func TestForceDelete(t *testing.T) { clients.SkipRelease(t, "stable/mitaka") clients.SkipRelease(t, "stable/newton") client, err := clients.NewSharedFileSystemV2Client() if err != nil { t.Fatalf("Unable to create a shared file system client: %v", err) } client.Microversion = "2.7" share, err := CreateShare(t, client) if err != nil { t.Fatalf("Unable to create a share: %v", err) } defer DeleteShare(t, client, share) err = waitForStatus(t, client, share.ID, "available", 120) if err != nil { t.Fatalf("Share status error: %v", err) } err = shares.ForceDelete(client, share.ID).ExtractErr() if err != nil { t.Fatalf("Unable to force delete a share: %v", err) } err = waitForStatus(t, client, share.ID, "deleted", 120) if err != nil { t.Fatalf("Share status error: %v", err) } t.Logf("Share %s was successfuly deleted", share.ID) } func TestUnmanage(t *testing.T) { clients.SkipRelease(t, "stable/mitaka") clients.SkipRelease(t, "stable/newton") clients.RequireAdmin(t) client, err := clients.NewSharedFileSystemV2Client() if err != nil { t.Fatalf("Unable to create a shared file system client: %v", err) } client.Microversion = "2.7" share, err := CreateShare(t, client) if err != nil { t.Fatalf("Unable to create a share: %v", err) } defer DeleteShare(t, client, share) err = waitForStatus(t, client, share.ID, "available", 120) if err != nil { t.Fatalf("Share status error: %v", err) } err = shares.Unmanage(client, share.ID).ExtractErr() if err != nil { t.Fatalf("Unable to unmanage a share: %v", err) } err = waitForStatus(t, client, share.ID, "deleted", 120) if err != nil { t.Fatalf("Share status error: %v", err) } t.Logf("Share %s was successfuly unmanaged", share.ID) } golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/sharedfilesystems/v2/sharetypes.go000066400000000000000000000026221367513235700340410ustar00rootroot00000000000000package 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) } sharetypes_test.go000066400000000000000000000106511367513235700350220ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/sharedfilesystems/v2package v2 import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "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) } tools.PrintResource(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 { tools.PrintResource(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) } tools.PrintResource(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) } tools.PrintResource(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) } tools.PrintResource(t, shareType) defer DeleteShareType(t, client, shareType) } golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/sharedfilesystems/v2/snapshots.go000066400000000000000000000054561367513235700337040ustar00rootroot00000000000000package v2 import ( "fmt" "strings" "testing" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack/sharedfilesystems/v2/snapshots" ) // CreateSnapshot will create a snapshot from the share ID with a name. An error will // be returned if the snapshot could not be created func CreateSnapshot(t *testing.T, client *gophercloud.ServiceClient, shareID string) (*snapshots.Snapshot, error) { if testing.Short() { t.Skip("Skipping test that requres share creation in short mode.") } createOpts := snapshots.CreateOpts{ ShareID: shareID, Name: "My Test Snapshot", Description: "My Test Description", } snapshot, err := snapshots.Create(client, createOpts).Extract() if err != nil { t.Logf("Failed to create snapshot") return nil, err } err = waitForSnapshotStatus(t, client, snapshot.ID, "available", 600) if err != nil { t.Logf("Failed to get %s snapshot status", snapshot.ID) return snapshot, err } return snapshot, nil } // ListSnapshots lists all snapshots that belong to this tenant's project. // An error will be returned if the snapshots could not be listed.. func ListSnapshots(t *testing.T, client *gophercloud.ServiceClient) ([]snapshots.Snapshot, error) { r, err := snapshots.ListDetail(client, &snapshots.ListOpts{}).AllPages() if err != nil { return nil, err } return snapshots.ExtractSnapshots(r) } // 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 DeleteSnapshot(t *testing.T, client *gophercloud.ServiceClient, snapshot *snapshots.Snapshot) { err := snapshots.Delete(client, snapshot.ID).ExtractErr() if err != nil { t.Errorf("Unable to delete snapshot %s: %v", snapshot.ID, err) } err = waitForSnapshotStatus(t, client, snapshot.ID, "deleted", 600) if err != nil { t.Errorf("Failed to wait for 'deleted' status for %s snapshot: %v", snapshot.ID, err) } else { t.Logf("Deleted snapshot: %s", snapshot.ID) } } func waitForSnapshotStatus(t *testing.T, c *gophercloud.ServiceClient, id, status string, secs int) error { err := gophercloud.WaitFor(secs, func() (bool, error) { current, err := snapshots.Get(c, id).Extract() if err != nil { if _, ok := err.(gophercloud.ErrDefault404); ok { switch status { case "deleted": return true, nil default: return false, err } } return false, err } if current.Status == status { return true, nil } if strings.Contains(current.Status, "error") { return true, fmt.Errorf("An error occurred, wrong status: %s", current.Status) } return false, nil }) if err != nil { mErr := PrintMessages(t, c, id) if mErr != nil { return fmt.Errorf("Snapshot status is '%s' and unable to get manila messages: %s", err, mErr) } } return err } snapshots_test.go000066400000000000000000000060251367513235700346550ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/sharedfilesystems/v2package v2 import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/sharedfilesystems/v2/snapshots" th "github.com/gophercloud/gophercloud/testhelper" ) func TestSnapshotCreate(t *testing.T) { clients.SkipRelease(t, "stable/mitaka") clients.SkipRelease(t, "stable/newton") client, err := clients.NewSharedFileSystemV2Client() if err != nil { t.Fatalf("Unable to create a shared file system 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) snapshot, err := CreateSnapshot(t, client, share.ID) if err != nil { t.Fatalf("Unable to create a snapshot: %v", err) } defer DeleteSnapshot(t, client, snapshot) created, err := snapshots.Get(client, snapshot.ID).Extract() if err != nil { t.Fatalf("Unable to retrieve a snapshot: %v", err) } tools.PrintResource(t, created) } func TestSnapshotUpdate(t *testing.T) { clients.SkipRelease(t, "stable/mitaka") clients.SkipRelease(t, "stable/newton") client, err := clients.NewSharedFileSystemV2Client() if err != nil { t.Fatalf("Unable to create shared file system client: %v", err) } share, err := CreateShare(t, client) if err != nil { t.Fatalf("Unable to create share: %v", err) } defer DeleteShare(t, client, share) snapshot, err := CreateSnapshot(t, client, share.ID) if err != nil { t.Fatalf("Unable to create a snapshot: %v", err) } defer DeleteSnapshot(t, client, snapshot) expectedSnapshot, err := snapshots.Get(client, snapshot.ID).Extract() if err != nil { t.Errorf("Unable to retrieve snapshot: %v", err) } name := "NewName" description := "" options := snapshots.UpdateOpts{ DisplayName: &name, DisplayDescription: &description, } expectedSnapshot.Name = name expectedSnapshot.Description = description _, err = snapshots.Update(client, snapshot.ID, options).Extract() if err != nil { t.Errorf("Unable to update snapshot: %v", err) } updatedSnapshot, err := snapshots.Get(client, snapshot.ID).Extract() if err != nil { t.Errorf("Unable to retrieve snapshot: %v", err) } tools.PrintResource(t, snapshot) th.CheckDeepEquals(t, expectedSnapshot, updatedSnapshot) } func TestSnapshotListDetail(t *testing.T) { clients.SkipRelease(t, "stable/mitaka") clients.SkipRelease(t, "stable/newton") client, err := clients.NewSharedFileSystemV2Client() if err != nil { t.Fatalf("Unable to create a shared file system 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) snapshot, err := CreateSnapshot(t, client, share.ID) if err != nil { t.Fatalf("Unable to create a snapshot: %v", err) } defer DeleteSnapshot(t, client, snapshot) ss, err := ListSnapshots(t, client) if err != nil { t.Fatalf("Unable to list snapshots: %v", err) } for i := range ss { tools.PrintResource(t, &ss[i]) } } golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/workflow/000077500000000000000000000000001367513235700271065ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/workflow/v2/000077500000000000000000000000001367513235700274355ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/workflow/v2/crontrigger.go000066400000000000000000000047021367513235700323140ustar00rootroot00000000000000package 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 } // DeleteCronTrigger deletes a cron trigger. func DeleteCronTrigger(t *testing.T, client *gophercloud.ServiceClient, crontrigger *crontriggers.CronTrigger) { err := crontriggers.Delete(client, crontrigger.ID).ExtractErr() if err != nil { t.Fatalf("Unable to delete cron trigger %s: %v", crontrigger.Name, err) } t.Logf("Deleted crontrigger: %s", crontrigger.Name) } // GetCronTrigger gets a cron trigger. func GetCronTrigger(t *testing.T, client *gophercloud.ServiceClient, id string) (*crontriggers.CronTrigger, error) { crontrigger, err := crontriggers.Get(client, id).Extract() if err != nil { t.Fatalf("Unable to get cron trigger %s: %v", id, err) } t.Logf("Cron trigger %s get", id) return crontrigger, err } // ListCronTriggers lists cron triggers. func ListCronTriggers(t *testing.T, client *gophercloud.ServiceClient, opts crontriggers.ListOptsBuilder) ([]crontriggers.CronTrigger, error) { allPages, err := crontriggers.List(client, opts).AllPages() if err != nil { t.Fatalf("Unable to list cron triggers: %v", err) } crontriggersList, err := crontriggers.ExtractCronTriggers(allPages) if err != nil { t.Fatalf("Unable to extract cron triggers: %v", err) } t.Logf("Cron triggers list found, length: %d", len(crontriggersList)) return crontriggersList, err } golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/workflow/v2/crontriggers_test.go000066400000000000000000000031041367513235700335310ustar00rootroot00000000000000package v2 import ( "testing" "time" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/workflow/v2/crontriggers" th "github.com/gophercloud/gophercloud/testhelper" ) func TestCronTriggersCreateGetDelete(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) trigger, err := CreateCronTrigger(t, client, workflow) th.AssertNoErr(t, err) defer DeleteCronTrigger(t, client, trigger) gettrigger, err := GetCronTrigger(t, client, trigger.ID) th.AssertNoErr(t, err) th.AssertEquals(t, trigger.ID, gettrigger.ID) tools.PrintResource(t, trigger) } func TestCronTriggersList(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) trigger, err := CreateCronTrigger(t, client, workflow) th.AssertNoErr(t, err) defer DeleteCronTrigger(t, client, trigger) list, err := ListCronTriggers(t, client, &crontriggers.ListOpts{ Name: &crontriggers.ListFilter{ Filter: crontriggers.FilterEQ, Value: trigger.Name, }, Pattern: &crontriggers.ListFilter{ Value: "0 0 1 1 *", }, CreatedAt: &crontriggers.ListDateFilter{ Filter: crontriggers.FilterGT, Value: time.Now().AddDate(-1, 0, 0), }, }) th.AssertNoErr(t, err) th.AssertEquals(t, 1, len(list)) tools.PrintResource(t, list) } golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/workflow/v2/execution.go000066400000000000000000000050121367513235700317650ustar00rootroot00000000000000package v2 import ( "fmt" "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) t.Logf("Wait for execution status SUCCESS: %s", executionDescription) th.AssertNoErr(t, tools.WaitFor(func() (bool, error) { latest, err := executions.Get(client, execution.ID).Extract() if err != nil { return false, err } if latest.State == "SUCCESS" { execution = latest return true, nil } if latest.State == "ERROR" { return false, fmt.Errorf("Execution in ERROR state") } return false, nil })) t.Logf("Execution success: %s", executionDescription) return execution, nil } // DeleteExecution deletes an execution. func DeleteExecution(t *testing.T, client *gophercloud.ServiceClient, execution *executions.Execution) { err := executions.Delete(client, execution.ID).ExtractErr() if err != nil { t.Fatalf("Unable to delete executions %s: %v", execution.Description, err) } t.Logf("Deleted executions: %s", execution.Description) } // ListExecutions lists the executions. func ListExecutions(t *testing.T, client *gophercloud.ServiceClient, opts executions.ListOptsBuilder) ([]executions.Execution, error) { allPages, err := executions.List(client, opts).AllPages() if err != nil { t.Fatalf("Unable to list executions: %v", err) } executionsList, err := executions.ExtractExecutions(allPages) if err != nil { t.Fatalf("Unable to extract executions: %v", err) } t.Logf("Executions list find, length: %d", len(executionsList)) return executionsList, err } golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/workflow/v2/executions_test.go000066400000000000000000000025451367513235700332170ustar00rootroot00000000000000package v2 import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/workflow/v2/executions" 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) defer DeleteExecution(t, client, execution) tools.PrintResource(t, execution) } func TestExecutionsList(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) defer DeleteExecution(t, client, execution) list, err := ListExecutions(t, client, &executions.ListOpts{ Description: &executions.ListFilter{ Value: execution.Description, }, CreatedAt: &executions.ListDateFilter{ Filter: executions.FilterGTE, Value: execution.CreatedAt, }, Input: execution.Input, }) th.AssertNoErr(t, err) th.AssertEquals(t, 1, len(list)) tools.PrintResource(t, list) } golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/workflow/v2/workflow.go000066400000000000000000000052311367513235700316370ustar00rootroot00000000000000package 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 tags: - tag1 - tag2 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 } golang-github-gophercloud-gophercloud-0.12.0/acceptance/openstack/workflow/v2/workflows_test.go000066400000000000000000000022731367513235700330640ustar00rootroot00000000000000package v2 import ( "testing" "time" "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: &workflows.ListFilter{ Value: workflow.Name, }, Tags: []string{"tag1"}, CreatedAt: &workflows.ListDateFilter{ Filter: workflows.FilterGT, Value: time.Now().AddDate(-1, 0, 0), }, }) th.AssertNoErr(t, err) th.AssertEquals(t, 1, len(list)) tools.PrintResource(t, list) } golang-github-gophercloud-gophercloud-0.12.0/acceptance/tools/000077500000000000000000000000001367513235700244055ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/acceptance/tools/pkg.go000066400000000000000000000000161367513235700255120ustar00rootroot00000000000000package tools golang-github-gophercloud-gophercloud-0.12.0/acceptance/tools/tools.go000066400000000000000000000045561367513235700261060ustar00rootroot00000000000000package tools import ( "crypto/rand" "encoding/json" "errors" mrand "math/rand" "testing" "time" ) // ErrTimeout is returned if WaitFor/WaitForTimeout take longer than their timeout duration. var ErrTimeout = errors.New("Timed out") // WaitFor uses WaitForTimeout to poll a predicate function once per second to // wait for a certain state to arrive, with a default timeout of 300 seconds. func WaitFor(predicate func() (bool, error)) error { return WaitForTimeout(predicate, 300*time.Second) } // WaitForTimeout polls a predicate function once per second to wait for a // certain state to arrive, or until the given timeout is reached. func WaitForTimeout(predicate func() (bool, error), timeout time.Duration) error { startTime := time.Now() for time.Since(startTime) < timeout { 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.12.0/auth_options.go000066400000000000000000000371461367513235700242350ustar00rootroot00000000000000package 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"` // Passcode is used in TOTP authentication method Passcode string `json:"passcode,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 System bool } // 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 } // ToTokenV3CreateMap allows AuthOptions to satisfy the AuthOptionsBuilder // interface in the v3 tokens package 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"` Passcode *string `json:"passcode,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 totpReq struct { User *userReq `json:"user,omitempty"` } type identityReq struct { Methods []string `json:"methods"` Password *passwordReq `json:"password,omitempty"` Token *tokenReq `json:"token,omitempty"` ApplicationCredential *applicationCredentialReq `json:"application_credential,omitempty"` TOTP *totpReq `json:"totp,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 if opts.Password == "" && opts.Passcode == "" { 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{} } var userRequest *userReq if opts.UserID != "" { // UserID could be used without the domain information userRequest = &userReq{ ID: &opts.UserID, } } if userRequest == nil && opts.Username == "" { // Make sure that Username or UserID are provided return nil, ErrUsernameOrUserID{} } if userRequest == nil && opts.DomainID != "" { userRequest = &userReq{ Name: &opts.Username, Domain: &domainReq{ID: &opts.DomainID}, } } if userRequest == nil && opts.DomainName != "" { userRequest = &userReq{ Name: &opts.Username, Domain: &domainReq{Name: &opts.DomainName}, } } // Make sure that DomainID or DomainName are provided among Username if userRequest == nil { return nil, ErrDomainIDOrDomainName{} } req.Auth.Identity.Methods = []string{"application_credential"} 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. if opts.Password != "" { req.Auth.Identity.Methods = append(req.Auth.Identity.Methods, "password") } // TOTP authentication. if opts.Passcode != "" { req.Auth.Identity.Methods = append(req.Auth.Identity.Methods, "totp") } // 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. if opts.Password != "" { req.Auth.Identity.Password = &passwordReq{ User: userReq{ Name: &opts.Username, Password: &opts.Password, Domain: &domainReq{ID: &opts.DomainID}, }, } } if opts.Passcode != "" { req.Auth.Identity.TOTP = &totpReq{ User: &userReq{ Name: &opts.Username, Passcode: &opts.Passcode, Domain: &domainReq{ID: &opts.DomainID}, }, } } } if opts.DomainName != "" { // Configure the request for Username and Password authentication with a DomainName. if opts.Password != "" { req.Auth.Identity.Password = &passwordReq{ User: userReq{ Name: &opts.Username, Password: &opts.Password, Domain: &domainReq{Name: &opts.DomainName}, }, } } if opts.Passcode != "" { req.Auth.Identity.TOTP = &totpReq{ User: &userReq{ Name: &opts.Username, Passcode: &opts.Passcode, 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. if opts.Password != "" { req.Auth.Identity.Password = &passwordReq{ User: userReq{ ID: &opts.UserID, Password: &opts.Password, }, } } if opts.Passcode != "" { req.Auth.Identity.TOTP = &totpReq{ User: &userReq{ ID: &opts.UserID, Passcode: &opts.Passcode, }, } } } } b, err := BuildRequestBody(req, "") if err != nil { return nil, err } if len(scope) != 0 { b["auth"].(map[string]interface{})["scope"] = scope } return b, nil } // ToTokenV3ScopeMap builds a scope from AuthOptions and satisfies interface in // the v3 tokens package. 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.System { return map[string]interface{}{ "system": map[string]interface{}{ "all": true, }, }, nil } 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 { if opts.Passcode != "" { // cannot reauth using TOTP passcode return false } return opts.AllowReauth } // ToTokenV3HeadersMap allows AuthOptions to satisfy the AuthOptionsBuilder // interface in the v3 tokens package. func (opts *AuthOptions) ToTokenV3HeadersMap(map[string]interface{}) (map[string]string, error) { return nil, nil } golang-github-gophercloud-gophercloud-0.12.0/auth_result.go000066400000000000000000000030751367513235700240520ustar00rootroot00000000000000package gophercloud /* AuthResult is the result from the request that was used to obtain a provider client's Keystone token. It is returned from ProviderClient.GetAuthResult(). The following types satisfy this interface: github.com/gophercloud/gophercloud/openstack/identity/v2/tokens.CreateResult github.com/gophercloud/gophercloud/openstack/identity/v3/tokens.CreateResult Usage example: import ( "github.com/gophercloud/gophercloud" tokens2 "github.com/gophercloud/gophercloud/openstack/identity/v2/tokens" tokens3 "github.com/gophercloud/gophercloud/openstack/identity/v3/tokens" ) func GetAuthenticatedUserID(providerClient *gophercloud.ProviderClient) (string, error) { r := providerClient.GetAuthResult() if r == nil { //ProviderClient did not use openstack.Authenticate(), e.g. because token //was set manually with ProviderClient.SetToken() return "", errors.New("no AuthResult available") } switch r := r.(type) { case tokens2.CreateResult: u, err := r.ExtractUser() if err != nil { return "", err } return u.ID, nil case tokens3.CreateResult: u, err := r.ExtractUser() if err != nil { return "", err } return u.ID, nil default: panic(fmt.Sprintf("got unexpected AuthResult type %t", r)) } } Both implementing types share a lot of methods by name, like ExtractUser() in this example. But those methods cannot be part of the AuthResult interface because the return types are different (in this case, type tokens2.User vs. type tokens3.User). */ type AuthResult interface { ExtractTokenID() (string, error) } golang-github-gophercloud-gophercloud-0.12.0/doc.go000066400000000000000000000077521367513235700222660ustar00rootroot00000000000000/* 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. It is now recommended to use the `clientconfig` package found at https://github.com/gophercloud/utils/tree/master/openstack/clientconfig for all authentication purposes. The below documentation is still relevant. clientconfig simply implements the below and presents it in an easier and more flexible way. 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 can authenticate with a token by doing: opts := gophercloud.AuthOptions{ IdentityEndpoint: "https://openstack.example.com:5000/v2.0", TokenID: "{token_id}", 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, err := 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.12.0/docs/000077500000000000000000000000001367513235700221075ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/docs/FAQ.md000066400000000000000000000066611367513235700230510ustar00rootroot00000000000000# Tips ## Handling Microversions Please see our dedicated document [here](MICROVERSIONS.md). ## 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.12.0/docs/MICROVERSIONS.md000066400000000000000000000073641367513235700245050ustar00rootroot00000000000000# Microversions ## Table of Contents * [Introduction](#introduction) * [Client Configuration](#client-configuration) * [Gophercloud Developer Information](#gophercloud-developer-information) * [Application Developer Information](#application-developer-information) ## Introduction Microversions are an OpenStack API ability which allows developers to add and remove features while still retaining backwards compatibility for all prior versions of the API. More information can be found here: > Note: these links are not an exhaustive reference for microversions. * http://specs.openstack.org/openstack/api-wg/guidelines/microversion_specification.html * https://developer.openstack.org/api-guide/compute/microversions.html * https://github.com/openstack/keystoneauth/blob/master/doc/source/using-sessions.rst ## Client Configuration You can set a specific microversion on a Service Client by doing the following: ```go client, err := openstack.NewComputeV2(providerClient, nil) client.Microversion = "2.52" ``` ## Gophercloud Developer Information Microversions change several aspects about API interaction. ### Existing Fields, New Values This is when an existing field behaves like an "enum" and a new valid value is possible by setting the client's microversion to a specific version. An example of this can be seen with Nova/Compute's Server Group `policy` field and the introduction of the [`soft-affinity`](https://developer.openstack.org/api-ref/compute/?expanded=create-server-group-detail#create-server-group) value. Unless Gophercloud is limiting the valid values that are passed to the Nova/Compute service, no changes are required in Gophercloud. ### New Request Fields This is when a microversion enables a new field to be used in an API request. When implementing this kind of change, it is imperative that the field has the `omitempty` attribute set. If `omitempty` is not set, then the field will be used for _all_ microversions and possibly cause an error from the API service. You may need to use a pointer field in order for this to work. When adding a new field, please make sure to include a GoDoc comment about what microversions the field is valid for. Please see [here](https://github.com/gophercloud/gophercloud/blob/917735ee91e24fe1493e57869c3b42ee89bc95d8/openstack/compute/v2/servers/requests.go#L215-L217) for an example. ### New Response Fields This is when a microversion includes new fields in the API response. The correct way of implementing this in Gophercloud is to add the field to the resource's "result" struct (in the `results.go` file) as a *pointer*. This way, the developer can check for a `nil` value to see if the field was set from a microversioned result. When adding a new field, please make sure to include a GoDoc comment about what microversions the field is valid for. Please see [here](https://github.com/gophercloud/gophercloud/blob/ed4deec00ff1d4d4c8a762af0c6360d4184a4bf4/openstack/compute/v2/servers/results.go#L221-L223) for an example. ### Modified Response Fields This is when the new type of the returned field is incompatible with the original type. When this happens, an entire new result struct must be created with new Extract methods to account for both the original result struct and new result struct. These new structs and methods need to be defined in a new `microversions.go` file. Please see [here](https://github.com/gophercloud/gophercloud/blob/917735ee91e24fe1493e57869c3b42ee89bc95d8/openstack/container/v1/capsules/microversions.go) for an example. ## Application Developer Information Gophercloud does not perform any validation checks on the API request to make sure it is valid for a specific microversion. It is up to you to ensure that the API request is using the correct fields and functions for the microversion. golang-github-gophercloud-gophercloud-0.12.0/docs/MIGRATING.md000066400000000000000000000015261367513235700237560ustar00rootroot00000000000000# 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.12.0/docs/STYLEGUIDE.md000066400000000000000000000075521367513235700241200ustar00rootroot00000000000000 ## 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: - `microversions.go`: contains all the response methods for fields or actions added in a microversion. - `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`). ### Microversions - Please see our dedicated document [here](MICROVERSIONS.md). golang-github-gophercloud-gophercloud-0.12.0/docs/assets/000077500000000000000000000000001367513235700234115ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/docs/assets/openlab.png000066400000000000000000000531001367513235700255360ustar00rootroot00000000000000‰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.12.0/docs/assets/vexxhost.png000066400000000000000000000375121367513235700260170ustar00rootroot00000000000000‰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.12.0/docs/contributor-tutorial/000077500000000000000000000000001367513235700263225ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/docs/contributor-tutorial/.template/000077500000000000000000000000001367513235700302135ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/docs/contributor-tutorial/.template/doc.go000066400000000000000000000003321367513235700313050ustar00rootroot00000000000000/* 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 golang-github-gophercloud-gophercloud-0.12.0/docs/contributor-tutorial/.template/requests.go000066400000000000000000000062431367513235700324220ustar00rootroot00000000000000package 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) { resp, err := client.Get(getURL(client, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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 } resp, err := client.Post(createURL(client), &b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{201}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete deletes a RESOURCE. func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { resp, err := client.Delete(deleteURL(client, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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 } resp, err := client.Patch(updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } golang-github-gophercloud-gophercloud-0.12.0/docs/contributor-tutorial/.template/results.go000066400000000000000000000037551367513235700322550ustar00rootroot00000000000000package 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 } golang-github-gophercloud-gophercloud-0.12.0/docs/contributor-tutorial/.template/testing/000077500000000000000000000000001367513235700316705ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/docs/contributor-tutorial/.template/testing/fixtures.go000066400000000000000000000067331367513235700341010ustar00rootroot00000000000000package 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.go000066400000000000000000000043241367513235700350550ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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) } golang-github-gophercloud-gophercloud-0.12.0/docs/contributor-tutorial/.template/urls.go000066400000000000000000000011321367513235700315240ustar00rootroot00000000000000package 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.12.0/docs/contributor-tutorial/README.md000066400000000000000000000007551367513235700276100ustar00rootroot00000000000000Contributor 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). golang-github-gophercloud-gophercloud-0.12.0/docs/contributor-tutorial/step-01-introduction.md000066400000000000000000000012021367513235700325470ustar00rootroot00000000000000Step 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). golang-github-gophercloud-gophercloud-0.12.0/docs/contributor-tutorial/step-02-issues.md000066400000000000000000000111631367513235700313510ustar00rootroot00000000000000Step 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 to start a discussion about whether or not the feature should be included in Gophercloud. We don't 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. golang-github-gophercloud-gophercloud-0.12.0/docs/contributor-tutorial/step-03-code-hunting.md000066400000000000000000000100441367513235700324200ustar00rootroot00000000000000Step 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. golang-github-gophercloud-gophercloud-0.12.0/docs/contributor-tutorial/step-04-acceptance-testing.md000066400000000000000000000020151367513235700335750ustar00rootroot00000000000000Step 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. golang-github-gophercloud-gophercloud-0.12.0/docs/contributor-tutorial/step-05-pull-requests.md000066400000000000000000000141101367513235700326610ustar00rootroot00000000000000Step 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). golang-github-gophercloud-gophercloud-0.12.0/docs/contributor-tutorial/step-06-code-review.md000066400000000000000000000061341367513235700322550ustar00rootroot00000000000000Step 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). golang-github-gophercloud-gophercloud-0.12.0/docs/contributor-tutorial/step-07-congratulations.md000066400000000000000000000005441367513235700332600ustar00rootroot00000000000000Step 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.12.0/endpoint_search.go000066400000000000000000000060271367513235700246600ustar00rootroot00000000000000package 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.12.0/errors.go000066400000000000000000000361641367513235700230340ustar00rootroot00000000000000package gophercloud import ( "fmt" "net/http" "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 ResponseHeader http.Header } 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() } // GetStatusCode returns the actual status code of the error. func (e ErrUnexpectedResponseCode) GetStatusCode() int { return e.Actual } // StatusCodeError is a convenience interface to easily allow access to the // status code field of the various ErrDefault* types. // // By using this interface, you only have to make a single type cast of // the returned error to err.(StatusCodeError) and then call GetStatusCode() // instead of having a large switch statement checking for each of the // ErrDefault* types. type StatusCodeError interface { Error() string GetStatusCode() int } // 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 } // ErrDefault409 is the default error type returned on a 409 HTTP response code. type ErrDefault409 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 } // Err409er is the interface resource error types implement to override the error message // from a 409 error. type Err409er interface { Error409(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.12.0/go.mod000066400000000000000000000010741367513235700222670ustar00rootroot00000000000000module github.com/gophercloud/gophercloud require ( golang.org/x/crypto v0.0.0-20191202143827-86a70503ff7e golang.org/x/net v0.0.0-20191126235420-ef20fe5d7933 // indirect golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e // indirect golang.org/x/sys v0.0.0-20191128015809-6d18c012aee9 // indirect golang.org/x/text v0.3.2 // indirect golang.org/x/tools v0.0.0-20191203134012-c197fd4bf371 // indirect golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898 // indirect gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect gopkg.in/yaml.v2 v2.2.7 ) golang-github-gophercloud-gophercloud-0.12.0/go.sum000066400000000000000000000050531367513235700223150ustar00rootroot00000000000000golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191202143827-86a70503ff7e h1:egKlR8l7Nu9vHGWbcUV8lqR4987UfUbBd7GbhqGzNYU= golang.org/x/crypto v0.0.0-20191202143827-86a70503ff7e/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191126235420-ef20fe5d7933/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191128015809-6d18c012aee9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191203134012-c197fd4bf371/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.7 h1:VUgggvou5XRW9mHwD/yXxIYSMtY0zoKQf/v226p2nyo= gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= golang-github-gophercloud-gophercloud-0.12.0/internal/000077500000000000000000000000001367513235700227735ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/internal/pkg.go000066400000000000000000000000211367513235700240740ustar00rootroot00000000000000package internal golang-github-gophercloud-gophercloud-0.12.0/internal/testing/000077500000000000000000000000001367513235700244505ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/internal/testing/pkg.go000066400000000000000000000000201367513235700255500ustar00rootroot00000000000000package testing golang-github-gophercloud-gophercloud-0.12.0/internal/testing/util_test.go000066400000000000000000000016131367513235700270140ustar00rootroot00000000000000package 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.12.0/internal/util.go000066400000000000000000000016441367513235700243040ustar00rootroot00000000000000package 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.12.0/openstack/000077500000000000000000000000001367513235700231465ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/auth_env.go000066400000000000000000000104571367513235700253150ustar00rootroot00000000000000package 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 and OS_PROJECT_ID. Of these, OS_USERNAME, OS_PASSWORD, and OS_AUTH_URL must have settings, or an error will result. OS_PROJECT_ID, is optional. OS_TENANT_ID and OS_TENANT_NAME are deprecated forms of OS_PROJECT_ID and OS_PROJECT_NAME and the latter are expected against a v3 auth api. If OS_PROJECT_ID and OS_PROJECT_NAME are set, they will still be referred as "tenant" in Gophercloud. If OS_PROJECT_NAME is set, it requires OS_PROJECT_ID to be set as well to handle projects not on the default domain. 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") passcode := os.Getenv("OS_PASSCODE") 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 userID == "" && username == "" { // Empty username and userID could be ignored, when applicationCredentialID and applicationCredentialSecret are set if applicationCredentialID == "" && applicationCredentialSecret == "" { err := gophercloud.ErrMissingAnyoneOfEnvironmentVariables{ EnvironmentVariables: []string{"OS_USERID", "OS_USERNAME"}, } return nilOptions, err } } if password == "" && passcode == "" && applicationCredentialID == "" && applicationCredentialName == "" { err := gophercloud.ErrMissingEnvironmentVariable{ // silently ignore TOTP passcode warning, since it is not a common auth method EnvironmentVariable: "OS_PASSWORD", } return nilOptions, err } if (applicationCredentialID != "" || applicationCredentialName != "") && applicationCredentialSecret == "" { err := gophercloud.ErrMissingEnvironmentVariable{ EnvironmentVariable: "OS_APPLICATION_CREDENTIAL_SECRET", } return nilOptions, err } if domainID == "" && domainName == "" && tenantID == "" && tenantName != "" { err := gophercloud.ErrMissingEnvironmentVariable{ EnvironmentVariable: "OS_PROJECT_ID", } return nilOptions, err } if applicationCredentialID == "" && applicationCredentialName != "" && applicationCredentialSecret != "" { if userID == "" && username == "" { return nilOptions, gophercloud.ErrMissingAnyoneOfEnvironmentVariables{ EnvironmentVariables: []string{"OS_USERID", "OS_USERNAME"}, } } if username != "" && domainID == "" && domainName == "" { return nilOptions, gophercloud.ErrMissingAnyoneOfEnvironmentVariables{ EnvironmentVariables: []string{"OS_DOMAIN_ID", "OS_DOMAIN_NAME"}, } } } ao := gophercloud.AuthOptions{ IdentityEndpoint: authURL, UserID: userID, Username: username, Password: password, Passcode: passcode, TenantID: tenantID, TenantName: tenantName, DomainID: domainID, DomainName: domainName, ApplicationCredentialID: applicationCredentialID, ApplicationCredentialName: applicationCredentialName, ApplicationCredentialSecret: applicationCredentialSecret, } return ao, nil } golang-github-gophercloud-gophercloud-0.12.0/openstack/baremetal/000077500000000000000000000000001367513235700251025ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/baremetal/apiversions/000077500000000000000000000000001367513235700274445ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/baremetal/apiversions/doc.go000066400000000000000000000005171367513235700305430ustar00rootroot00000000000000/* Package apiversions provides information about the versions supported by a specific Ironic API. Example to list versions allVersions, err := apiversions.List(client.ServiceClient()).AllPages() Example to get a specific version actual, err := apiversions.Get(client.ServiceClient(), "v1").Extract() */ package apiversions golang-github-gophercloud-gophercloud-0.12.0/openstack/baremetal/apiversions/requests.go000066400000000000000000000011021367513235700316400ustar00rootroot00000000000000package apiversions import ( "github.com/gophercloud/gophercloud" ) // List lists all the API versions available to end users. func List(client *gophercloud.ServiceClient) (r ListResult) { resp, err := client.Get(listURL(client), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get will get a specific API version, specified by major ID. func Get(client *gophercloud.ServiceClient, v string) (r GetResult) { resp, err := client.Get(getURL(client, v), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } golang-github-gophercloud-gophercloud-0.12.0/openstack/baremetal/apiversions/results.go000066400000000000000000000026531367513235700315020ustar00rootroot00000000000000package apiversions import ( "github.com/gophercloud/gophercloud" ) // APIVersions represents the result from getting a list of all versions available type APIVersions struct { DefaultVersion APIVersion `json:"default_version"` Versions []APIVersion `json:"versions"` } // APIVersion represents an API version for Ironic 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"` // Version is the maximum microversion supported. Version string `json:"version"` } // GetResult represents the result of a get operation. type GetResult struct { gophercloud.Result } // ListResult represents the result of a list operation. type ListResult struct { gophercloud.Result } // Extract is a function that accepts a get result and extracts an API version resource. func (r GetResult) Extract() (*APIVersion, error) { var s struct { Version APIVersion `json:"version"` } err := r.ExtractInto(&s) if err != nil { return nil, err } return &s.Version, nil } // Extract is a function that accepts a list result and extracts an APIVersions resource func (r ListResult) Extract() (*APIVersions, error) { var version APIVersions err := r.ExtractInto(&version) if err != nil { return nil, err } return &version, nil } golang-github-gophercloud-gophercloud-0.12.0/openstack/baremetal/apiversions/testing/000077500000000000000000000000001367513235700311215ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/baremetal/apiversions/testing/fixtures.go000066400000000000000000000043101367513235700333170ustar00rootroot00000000000000package testing import ( "fmt" "net/http" "testing" "github.com/gophercloud/gophercloud/openstack/baremetal/apiversions" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) const IronicAPIAllVersionResponse = ` { "default_version": { "status": "CURRENT", "min_version": "1.1", "version": "1.56", "id": "v1", "links": [ { "href": "http://localhost:6385/v1/", "rel": "self" } ] }, "versions": [ { "status": "CURRENT", "min_version": "1.1", "version": "1.56", "id": "v1", "links": [ { "href": "http://localhost:6385/v1/", "rel": "self" } ] } ], "name": "OpenStack Ironic API", "description": "Ironic is an OpenStack project which aims to provision baremetal machines." } ` const IronicAPIVersionResponse = ` { "media_types": [ { "base": "application/json", "type": "application/vnd.openstack.ironic.v1+json" } ], "version": { "status": "CURRENT", "min_version": "1.1", "version": "1.56", "id": "v1", "links": [ { "href": "http://localhost:6385/v1/", "rel": "self" } ] }, "id": "v1" } ` var IronicAPIVersion1Result = apiversions.APIVersion{ ID: "v1", Status: "CURRENT", MinVersion: "1.1", Version: "1.56", } var IronicAllAPIVersionResults = apiversions.APIVersions{ DefaultVersion: IronicAPIVersion1Result, Versions: []apiversions.APIVersion{ IronicAPIVersion1Result, }, } 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, IronicAPIAllVersionResponse) }) } 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, IronicAPIVersionResponse) }) } requests_test.go000066400000000000000000000013431367513235700343040ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/baremetal/apiversions/testingpackage testing import ( "testing" "github.com/gophercloud/gophercloud/openstack/baremetal/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) actual, err := apiversions.List(client.ServiceClient()).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, IronicAllAPIVersionResults, *actual) } func TestGetAPIVersion(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() MockGetResponse(t) actual, err := apiversions.Get(client.ServiceClient(), "v1").Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, IronicAPIVersion1Result, *actual) } golang-github-gophercloud-gophercloud-0.12.0/openstack/baremetal/apiversions/urls.go000066400000000000000000000003701367513235700307600ustar00rootroot00000000000000package apiversions import ( "github.com/gophercloud/gophercloud" ) func getURL(c *gophercloud.ServiceClient, version string) string { return c.ServiceURL(version) } func listURL(c *gophercloud.ServiceClient) string { return c.ServiceURL() } golang-github-gophercloud-gophercloud-0.12.0/openstack/baremetal/httpbasic/000077500000000000000000000000001367513235700270635ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/baremetal/httpbasic/doc.go000066400000000000000000000006741367513235700301660ustar00rootroot00000000000000/* Package httpbasic provides support for http_basic bare metal endpoints. Example of obtaining and using a client: client, err := httpbasic.NewBareMetalHTTPBasic(httpbasic.Endpoints{ IronicEndpoing: "http://localhost:6385/v1/", IronicUser: "myUser", IronicUserPassword: "myPassword", }) if err != nil { panic(err) } client.Microversion = "1.50" nodes.ListDetail(client, nodes.listOpts{}) */ package httpbasic golang-github-gophercloud-gophercloud-0.12.0/openstack/baremetal/httpbasic/requests.go000066400000000000000000000023601367513235700312660ustar00rootroot00000000000000package httpbasic import ( "encoding/base64" "fmt" "github.com/gophercloud/gophercloud" ) // EndpointOpts specifies a "http_basic" Ironic Endpoint type EndpointOpts struct { IronicEndpoint string IronicUser string IronicUserPassword string } func initClientOpts(client *gophercloud.ProviderClient, eo EndpointOpts) (*gophercloud.ServiceClient, error) { sc := new(gophercloud.ServiceClient) if eo.IronicEndpoint == "" { return nil, fmt.Errorf("IronicEndpoint is required") } if eo.IronicUser == "" || eo.IronicUserPassword == "" { return nil, fmt.Errorf("User and Password are required") } token := []byte(eo.IronicUser + ":" + eo.IronicUserPassword) encodedToken := base64.StdEncoding.EncodeToString(token) sc.MoreHeaders = map[string]string{"Authorization": "Basic " + encodedToken} sc.Endpoint = gophercloud.NormalizeURL(eo.IronicEndpoint) sc.ProviderClient = client return sc, nil } // NewBareMetalHTTPBasic creates a ServiceClient that may be used to access a // "http_basic" bare metal service. func NewBareMetalHTTPBasic(eo EndpointOpts) (*gophercloud.ServiceClient, error) { sc, err := initClientOpts(&gophercloud.ProviderClient{}, eo) if err != nil { return nil, err } sc.Type = "baremetal" return sc, nil } golang-github-gophercloud-gophercloud-0.12.0/openstack/baremetal/httpbasic/testing/000077500000000000000000000000001367513235700305405ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/baremetal/httpbasic/testing/requests_test.go000066400000000000000000000020251367513235700340000ustar00rootroot00000000000000package testing import ( "encoding/base64" "testing" "github.com/gophercloud/gophercloud/openstack/baremetal/httpbasic" th "github.com/gophercloud/gophercloud/testhelper" ) func TestHttpBasic(t *testing.T) { httpClient, err := httpbasic.NewBareMetalHTTPBasic(httpbasic.EndpointOpts{ IronicEndpoint: "http://ironic:6385/v1", IronicUser: "myUser", IronicUserPassword: "myPasswd", }) th.AssertNoErr(t, err) encToken := base64.StdEncoding.EncodeToString([]byte("myUser:myPasswd")) headerValue := "Basic " + encToken th.AssertEquals(t, headerValue, httpClient.MoreHeaders["Authorization"]) errTest1, err := httpbasic.NewBareMetalHTTPBasic(httpbasic.EndpointOpts{ IronicEndpoint: "http://ironic:6385/v1", }) _ = errTest1 th.AssertEquals(t, "User and Password are required", err.Error()) errTest2, err := httpbasic.NewBareMetalHTTPBasic(httpbasic.EndpointOpts{ IronicUser: "myUser", IronicUserPassword: "myPasswd", }) _ = errTest2 th.AssertEquals(t, "IronicEndpoint is required", err.Error()) } golang-github-gophercloud-gophercloud-0.12.0/openstack/baremetal/noauth/000077500000000000000000000000001367513235700264005ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/baremetal/noauth/doc.go000066400000000000000000000005401367513235700274730ustar00rootroot00000000000000/* Package noauth provides support for noauth bare metal endpoints. Example of obtaining and using a client: client, err := noauth.NewBareMetalNoAuth(noauth.EndpointOpts{ IronicEndpoint: "http://localhost:6385/v1/", }) if err != nil { panic(err) } client.Microversion = "1.50" nodes.ListDetail(client, nodes.ListOpts{}) */ package noauth golang-github-gophercloud-gophercloud-0.12.0/openstack/baremetal/noauth/requests.go000066400000000000000000000020261367513235700306020ustar00rootroot00000000000000package noauth import ( "fmt" "github.com/gophercloud/gophercloud" ) // EndpointOpts specifies a "noauth" Ironic Endpoint. type EndpointOpts struct { // IronicEndpoint [required] is currently only used with "noauth" Ironic. // An Ironic endpoint with "auth_strategy=noauth" is necessary, for example: // http://ironic.example.com:6385/v1. IronicEndpoint string } func initClientOpts(client *gophercloud.ProviderClient, eo EndpointOpts) (*gophercloud.ServiceClient, error) { sc := new(gophercloud.ServiceClient) if eo.IronicEndpoint == "" { return nil, fmt.Errorf("IronicEndpoint is required") } sc.Endpoint = gophercloud.NormalizeURL(eo.IronicEndpoint) sc.ProviderClient = client return sc, nil } // NewBareMetalNoAuth creates a ServiceClient that may be used to access a // "noauth" bare metal service. func NewBareMetalNoAuth(eo EndpointOpts) (*gophercloud.ServiceClient, error) { sc, err := initClientOpts(&gophercloud.ProviderClient{}, eo) if err != nil { return nil, err } sc.Type = "baremetal" return sc, nil } golang-github-gophercloud-gophercloud-0.12.0/openstack/baremetal/noauth/testing/000077500000000000000000000000001367513235700300555ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/baremetal/noauth/testing/requests_test.go000066400000000000000000000005721367513235700333220ustar00rootroot00000000000000package testing import ( "testing" "github.com/gophercloud/gophercloud/openstack/baremetal/noauth" th "github.com/gophercloud/gophercloud/testhelper" ) func TestNoAuth(t *testing.T) { noauthClient, err := noauth.NewBareMetalNoAuth(noauth.EndpointOpts{ IronicEndpoint: "http://ironic:6385/v1", }) th.AssertNoErr(t, err) th.AssertEquals(t, "", noauthClient.TokenID) } golang-github-gophercloud-gophercloud-0.12.0/openstack/baremetal/v1/000077500000000000000000000000001367513235700254305ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/baremetal/v1/allocations/000077500000000000000000000000001367513235700277405ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/baremetal/v1/allocations/requests.go000066400000000000000000000101701367513235700321410ustar00rootroot00000000000000package allocations import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // CreateOptsBuilder allows extensions to add additional parameters to the // Create request. type CreateOptsBuilder interface { ToAllocationCreateMap() (map[string]interface{}, error) } // CreateOpts specifies allocation creation parameters type CreateOpts struct { // The requested resource class for the allocation. ResourceClass string `json:"resource_class" required:"true"` // The list of nodes (names or UUIDs) that should be considered for this allocation. If not provided, all available nodes will be considered. CandidateNodes []string `json:"candidate_nodes,omitempty"` // The unique name of the Allocation. Name string `json:"name,omitempty"` // The list of requested traits for the allocation. Traits []string `json:"traits,omitempty"` // The UUID for the resource. UUID string `json:"uuid,omitempty"` // A set of one or more arbitrary metadata key and value pairs. Extra map[string]string `json:"extra,omitempty"` } // ToAllocationCreateMap assembles a request body based on the contents of a CreateOpts. func (opts CreateOpts) ToAllocationCreateMap() (map[string]interface{}, error) { body, err := gophercloud.BuildRequestBody(opts, "") if err != nil { return nil, err } return body, nil } // Create requests a node to be created func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { reqBody, err := opts.ToAllocationCreateMap() if err != nil { r.Err = err return } resp, err := client.Post(createURL(client), reqBody, &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } type AllocationState string var ( Allocating AllocationState = "allocating" Active = "active" Error = "error" ) // ListOptsBuilder allows extensions to add additional parameters to the List request. type ListOptsBuilder interface { ToAllocationListQuery() (string, error) } // ListOpts allows the filtering and sorting of paginated collections through the API. type ListOpts struct { // Filter the list of allocations by the node UUID or name. Node string `q:"node"` // Filter the list of returned nodes, and only return the ones with the specified resource class. ResourceClass string `q:"resource_class"` // Filter the list of allocations by the allocation state, one of active, allocating or error. State AllocationState `q:"state"` // One or more fields to be returned in the response. Fields []string `q:"fields"` // Requests a page size of items. Limit int `q:"limit"` // The ID of the last-seen item Marker string `q:"marker"` // Sorts the response by the requested sort direction. // Valid value is asc (ascending) or desc (descending). Default is asc. SortDir string `q:"sort_dir"` // Sorts the response by the this attribute value. Default is id. SortKey string `q:"sort_key"` } // ToAllocationListQuery formats a ListOpts into a query string. func (opts ListOpts) ToAllocationListQuery() (string, error) { q, err := gophercloud.BuildQueryString(opts) return q.String(), err } // List makes a request against the API to list allocations accessible to you. func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { url := listURL(client) if opts != nil { query, err := opts.ToAllocationListQuery() if err != nil { return pagination.Pager{Err: err} } url += query } return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { return AllocationPage{pagination.LinkedPageBase{PageResult: r}} }) } // Get requests the details of an allocation by ID. func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { resp, err := client.Get(getURL(client, id), &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete requests the deletion of an allocation func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { resp, err := client.Delete(deleteURL(client, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } golang-github-gophercloud-gophercloud-0.12.0/openstack/baremetal/v1/allocations/results.go000066400000000000000000000062471367513235700320010ustar00rootroot00000000000000package allocations import ( "time" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) type Allocation struct { // The UUID for the resource. UUID string `json:"uuid"` // A list of UUIDs of the nodes that are candidates for this allocation. CandidateNodes []string `json:"candidate_nodes"` // The error message for the allocation if it is in the error state, null otherwise. LastError string `json:"last_error"` // The unique name of the allocation. Name string `json:"name"` // The UUID of the node assigned to the allocation. Will be null if a node is not yet assigned. NodeUUID string `json:"node_uuid"` // The current state of the allocation. One of: allocation, active, error State string `json:"state"` // The resource class requested for the allocation. ResourceClass string `json:"resource_class"` // The list of the traits requested for the allocation. Traits []string `json:"traits"` // A set of one or more arbitrary metadata key and value pairs. Extra map[string]string `json:"extra"` // The UTC date and time when the resource was created, ISO 8601 format. CreatedAt time.Time `json:"created_at"` // The UTC date and time when the resource was updated, ISO 8601 format. May be “nullâ€. UpdatedAt time.Time `json:"updated_at"` // A list of relative links. Includes the self and bookmark links. Links []interface{} `json:"links"` } type allocationResult struct { gophercloud.Result } func (r allocationResult) Extract() (*Allocation, error) { var s Allocation err := r.ExtractInto(&s) return &s, err } func (r allocationResult) ExtractInto(v interface{}) error { return r.Result.ExtractIntoStructPtr(v, "") } func ExtractAllocationsInto(r pagination.Page, v interface{}) error { return r.(AllocationPage).Result.ExtractIntoSlicePtr(v, "allocations") } // AllocationPage abstracts the raw results of making a List() request against // the API. type AllocationPage struct { pagination.LinkedPageBase } // IsEmpty returns true if a page contains no Allocation results. func (r AllocationPage) IsEmpty() (bool, error) { s, err := ExtractAllocations(r) return len(s) == 0, err } // NextPageURL uses the response's embedded link reference to navigate to the // next page of results. func (r AllocationPage) NextPageURL() (string, error) { var s struct { Links []gophercloud.Link `json:"allocations_links"` } err := r.ExtractInto(&s) if err != nil { return "", err } return gophercloud.ExtractNextURL(s.Links) } // ExtractAllocations interprets the results of a single page from a List() call, // producing a slice of Allocation entities. func ExtractAllocations(r pagination.Page) ([]Allocation, error) { var s []Allocation err := ExtractAllocationsInto(r, &s) return s, err } // GetResult is the response from a Get operation. Call its Extract // method to interpret it as a Allocation. type GetResult struct { allocationResult } // CreateResult is the response from a Create operation. type CreateResult struct { allocationResult } // 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 } golang-github-gophercloud-gophercloud-0.12.0/openstack/baremetal/v1/allocations/testing/000077500000000000000000000000001367513235700314155ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/baremetal/v1/allocations/testing/fixtures.go000066400000000000000000000123561367513235700336240ustar00rootroot00000000000000package testing import ( "fmt" "net/http" "testing" "time" "github.com/gophercloud/gophercloud/openstack/baremetal/v1/allocations" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) const AllocationListBody = ` { "allocations": [ { "candidate_nodes": [], "created_at": "2019-02-20T09:43:58+00:00", "extra": {}, "last_error": null, "links": [ { "href": "http://127.0.0.1:6385/v1/allocations/5344a3e2-978a-444e-990a-cbf47c62ef88", "rel": "self" }, { "href": "http://127.0.0.1:6385/allocations/5344a3e2-978a-444e-990a-cbf47c62ef88", "rel": "bookmark" } ], "name": "allocation-1", "node_uuid": "6d85703a-565d-469a-96ce-30b6de53079d", "resource_class": "bm-large", "state": "active", "traits": [], "updated_at": "2019-02-20T09:43:58+00:00", "uuid": "5344a3e2-978a-444e-990a-cbf47c62ef88" }, { "candidate_nodes": [], "created_at": "2019-02-20T09:43:58+00:00", "extra": {}, "last_error": "Failed to process allocation eff80f47-75f0-4d41-b1aa-cf07c201adac: no available nodes match the resource class bm-large.", "links": [ { "href": "http://127.0.0.1:6385/v1/allocations/eff80f47-75f0-4d41-b1aa-cf07c201adac", "rel": "self" }, { "href": "http://127.0.0.1:6385/allocations/eff80f47-75f0-4d41-b1aa-cf07c201adac", "rel": "bookmark" } ], "name": "allocation-2", "node_uuid": null, "resource_class": "bm-large", "state": "error", "traits": [ "CUSTOM_GOLD" ], "updated_at": "2019-02-20T09:43:58+00:00", "uuid": "eff80f47-75f0-4d41-b1aa-cf07c201adac" } ] } ` const SingleAllocationBody = ` { "candidate_nodes": ["344a3e2-978a-444e-990a-cbf47c62ef88"], "created_at": "2019-02-20T09:43:58+00:00", "extra": {}, "last_error": null, "links": [ { "href": "http://127.0.0.1:6385/v1/allocations/5344a3e2-978a-444e-990a-cbf47c62ef88", "rel": "self" }, { "href": "http://127.0.0.1:6385/allocations/5344a3e2-978a-444e-990a-cbf47c62ef88", "rel": "bookmark" } ], "name": "allocation-1", "node_uuid": null, "resource_class": "baremetal", "state": "allocating", "traits": ["foo"], "updated_at": null, "uuid": "5344a3e2-978a-444e-990a-cbf47c62ef88" }` var ( createdAt, _ = time.Parse(time.RFC3339, "2019-02-20T09:43:58+00:00") Allocation1 = allocations.Allocation{ UUID: "5344a3e2-978a-444e-990a-cbf47c62ef88", CandidateNodes: []string{"344a3e2-978a-444e-990a-cbf47c62ef88"}, Name: "allocation-1", State: "allocating", ResourceClass: "baremetal", Traits: []string{"foo"}, Extra: map[string]string{}, CreatedAt: createdAt, Links: []interface{}{map[string]interface{}{"href": "http://127.0.0.1:6385/v1/allocations/5344a3e2-978a-444e-990a-cbf47c62ef88", "rel": "self"}, map[string]interface{}{"href": "http://127.0.0.1:6385/allocations/5344a3e2-978a-444e-990a-cbf47c62ef88", "rel": "bookmark"}}, } ) // HandleAllocationListSuccessfully sets up the test server to respond to a allocation List request. func HandleAllocationListSuccessfully(t *testing.T) { th.Mux.HandleFunc("/allocations", 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, AllocationListBody) case "eff80f47-75f0-4d41-b1aa-cf07c201adac": fmt.Fprintf(w, `{ "allocations": [] }`) default: t.Fatalf("/allocations invoked with unexpected marker=[%s]", marker) } }) } // HandleAllocationCreationSuccessfully sets up the test server to respond to a allocation creation request // with a given response. func HandleAllocationCreationSuccessfully(t *testing.T, response string) { th.Mux.HandleFunc("/allocations", 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, `{ "name": "allocation-1", "resource_class": "baremetal", "candidate_nodes": ["344a3e2-978a-444e-990a-cbf47c62ef88"], "traits": ["foo"] }`) w.WriteHeader(http.StatusAccepted) w.Header().Add("Content-Type", "application/json") fmt.Fprintf(w, response) }) } // HandleAllocationDeletionSuccessfully sets up the test server to respond to a allocation deletion request. func HandleAllocationDeletionSuccessfully(t *testing.T) { th.Mux.HandleFunc("/allocations/344a3e2-978a-444e-990a-cbf47c62ef88", 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 HandleAllocationGetSuccessfully(t *testing.T) { th.Mux.HandleFunc("/allocations/344a3e2-978a-444e-990a-cbf47c62ef88", 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, SingleAllocationBody) }) } requests_test.go000066400000000000000000000040241367513235700345770ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/baremetal/v1/allocations/testingpackage testing import ( "testing" "github.com/gophercloud/gophercloud/openstack/baremetal/v1/allocations" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) func TestListAllocations(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleAllocationListSuccessfully(t) pages := 0 err := allocations.List(client.ServiceClient(), allocations.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { pages++ actual, err := allocations.ExtractAllocations(page) if err != nil { return false, err } if len(actual) != 2 { t.Fatalf("Expected 2 allocations, got %d", len(actual)) } th.AssertEquals(t, "5344a3e2-978a-444e-990a-cbf47c62ef88", actual[0].UUID) th.AssertEquals(t, "eff80f47-75f0-4d41-b1aa-cf07c201adac", actual[1].UUID) return true, nil }) th.AssertNoErr(t, err) if pages != 1 { t.Errorf("Expected 1 page, saw %d", pages) } } func TestCreateAllocation(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleAllocationCreationSuccessfully(t, SingleAllocationBody) actual, err := allocations.Create(client.ServiceClient(), allocations.CreateOpts{ Name: "allocation-1", ResourceClass: "baremetal", CandidateNodes: []string{"344a3e2-978a-444e-990a-cbf47c62ef88"}, Traits: []string{"foo"}, }).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, Allocation1, *actual) } func TestDeleteAllocation(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleAllocationDeletionSuccessfully(t) res := allocations.Delete(client.ServiceClient(), "344a3e2-978a-444e-990a-cbf47c62ef88") th.AssertNoErr(t, res.Err) } func TestGetAllocation(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleAllocationGetSuccessfully(t) c := client.ServiceClient() actual, err := allocations.Get(c, "344a3e2-978a-444e-990a-cbf47c62ef88").Extract() if err != nil { t.Fatalf("Unexpected Get error: %v", err) } th.CheckDeepEquals(t, Allocation1, *actual) } golang-github-gophercloud-gophercloud-0.12.0/openstack/baremetal/v1/allocations/urls.go000066400000000000000000000011051367513235700312510ustar00rootroot00000000000000package allocations import "github.com/gophercloud/gophercloud" func createURL(client *gophercloud.ServiceClient) string { return client.ServiceURL("allocations") } func listURL(client *gophercloud.ServiceClient) string { return createURL(client) } func resourceURL(client *gophercloud.ServiceClient, id string) string { return client.ServiceURL("allocations", id) } func deleteURL(client *gophercloud.ServiceClient, id string) string { return resourceURL(client, id) } func getURL(client *gophercloud.ServiceClient, id string) string { return resourceURL(client, id) } golang-github-gophercloud-gophercloud-0.12.0/openstack/baremetal/v1/drivers/000077500000000000000000000000001367513235700271065ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/baremetal/v1/drivers/doc.go000066400000000000000000000020351367513235700302020ustar00rootroot00000000000000/* Package drivers contains the functionality for Listing drivers, driver details, driver properties and driver logical disk properties API reference: https://developer.openstack.org/api-ref/baremetal/#drivers-drivers Example to List Drivers drivers.ListDrivers(client.ServiceClient(), drivers.ListDriversOpts{}).EachPage(func(page pagination.Page) (bool, error) { driversList, err := drivers.ExtractDrivers(page) if err != nil { return false, err } for _, n := range driversList { // Do something } return true, nil }) Example to Get single Driver Details showDriverDetails, err := drivers.GetDriverDetails(client, "ipmi").Extract() if err != nil { panic(err) } Example to Get single Driver Properties showDriverProperties, err := drivers.GetDriverProperties(client, "ipmi").Extract() if err != nil { panic(err) } Example to Get single Driver Logical Disk Properties showDriverDiskProperties, err := drivers.GetDriverDiskProperties(client, "ipmi").Extract() if err != nil { panic(err) } */ package drivers golang-github-gophercloud-gophercloud-0.12.0/openstack/baremetal/v1/drivers/requests.go000066400000000000000000000050201367513235700313050ustar00rootroot00000000000000package drivers import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // ListDriversOptsBuilder allows extensions to add additional parameters to the // ListDrivers request. type ListDriversOptsBuilder interface { ToListDriversOptsQuery() (string, error) } // ListDriversOpts defines query options that can be passed to ListDrivers type ListDriversOpts struct { // Provide detailed information about the drivers Detail bool `q:"detail"` // Filter the list by the type of the driver Type string `q:"type"` } // ToListDriversOptsQuery formats a ListOpts into a query string func (opts ListDriversOpts) ToListDriversOptsQuery() (string, error) { q, err := gophercloud.BuildQueryString(opts) return q.String(), err } // ListDrivers makes a request against the API to list all drivers func ListDrivers(client *gophercloud.ServiceClient, opts ListDriversOptsBuilder) pagination.Pager { url := driversURL(client) if opts != nil { query, err := opts.ToListDriversOptsQuery() if err != nil { return pagination.Pager{Err: err} } url += query } return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { return DriverPage{pagination.LinkedPageBase{PageResult: r}} }) } // GetDriverDetails Shows details for a driver func GetDriverDetails(client *gophercloud.ServiceClient, driverName string) (r GetDriverResult) { resp, err := client.Get(driverDetailsURL(client, driverName), &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // GetDriverProperties Shows the required and optional parameters that // driverName expects to be supplied in the driver_info field for every // Node it manages func GetDriverProperties(client *gophercloud.ServiceClient, driverName string) (r GetPropertiesResult) { resp, err := client.Get(driverPropertiesURL(client, driverName), &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // GetDriverDiskProperties Show the required and optional parameters that // driverName expects to be supplied in the node’s raid_config field, if a // RAID configuration change is requested. func GetDriverDiskProperties(client *gophercloud.ServiceClient, driverName string) (r GetDiskPropertiesResult) { resp, err := client.Get(driverDiskPropertiesURL(client, driverName), &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } golang-github-gophercloud-gophercloud-0.12.0/openstack/baremetal/v1/drivers/results.go000066400000000000000000000155551367513235700311510ustar00rootroot00000000000000package drivers import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) type driverResult struct { gophercloud.Result } // Extract interprets any driverResult as a Driver, if possible. func (r driverResult) Extract() (*Driver, error) { var s Driver err := r.ExtractInto(&s) return &s, err } func (r driverResult) ExtractInto(v interface{}) error { return r.Result.ExtractIntoStructPtr(v, "") } func ExtractDriversInto(r pagination.Page, v interface{}) error { return r.(DriverPage).Result.ExtractIntoSlicePtr(v, "drivers") } // Driver represents a driver in the OpenStack Bare Metal API. type Driver struct { // Name and Identifier of the driver Name string `json:"name"` // A list of active hosts that support this driver Hosts []string `json:"hosts"` // Type of this driver (“classic†or “dynamicâ€) Type string `json:"type"` // The default bios interface used for a node with a dynamic driver, // if no bios interface is specified for the node. DefaultBiosInterface string `json:"default_bios_interface"` // The default boot interface used for a node with a dynamic driver, // if no boot interface is specified for the node. DefaultBootInterface string `json:"default_boot_interface"` // The default console interface used for a node with a dynamic driver, // if no console interface is specified for the node. DefaultConsoleInterface string `json:"default_console_interface"` // The default deploy interface used for a node with a dynamic driver, // if no deploy interface is specified for the node. DefaultDeployInterface string `json:"default_deploy_interface"` // The default inspection interface used for a node with a dynamic driver, // if no inspection interface is specified for the node. DefaultInspectInterface string `json:"default_inspect_interface"` // The default management interface used for a node with a dynamic driver, // if no management interface is specified for the node. DefaultManagementInterface string `json:"default_management_interface"` // The default network interface used for a node with a dynamic driver, // if no network interface is specified for the node. DefaultNetworkInterface string `json:"default_network_interface"` // The default power interface used for a node with a dynamic driver, // if no power interface is specified for the node. DefaultPowerInterface string `json:"default_power_interface"` // The default RAID interface used for a node with a dynamic driver, // if no RAID interface is specified for the node. DefaultRaidInterface string `json:"default_raid_interface"` // The default rescue interface used for a node with a dynamic driver, // if no rescue interface is specified for the node. DefaultRescueInterface string `json:"default_rescue_interface"` // The default storage interface used for a node with a dynamic driver, // if no storage interface is specified for the node. DefaultStorageInterface string `json:"default_storage_interface"` // The default vendor interface used for a node with a dynamic driver, // if no vendor interface is specified for the node. DefaultVendorInterface string `json:"default_vendor_interface"` // The enabled bios interfaces for this driver. EnabledBiosInterfaces []string `json:"enabled_bios_interfaces"` // The enabled boot interfaces for this driver. EnabledBootInterfaces []string `json:"enabled_boot_interfaces"` // The enabled console interfaces for this driver. EnabledConsoleInterface []string `json:"enabled_console_interfaces"` // The enabled deploy interfaces for this driver. EnabledDeployInterfaces []string `json:"enabled_deploy_interfaces"` // The enabled inspection interfaces for this driver. EnabledInspectInterfaces []string `json:"enabled_inspect_interfaces"` // The enabled management interfaces for this driver. EnabledManagementInterfaces []string `json:"enabled_management_interfaces"` // The enabled network interfaces for this driver. EnabledNetworkInterfaces []string `json:"enabled_network_interfaces"` // The enabled power interfaces for this driver. EnabledPowerInterfaces []string `json:"enabled_power_interfaces"` // The enabled rescue interfaces for this driver. EnabledRescueInterfaces []string `json:"enabled_rescue_interfaces"` // The enabled RAID interfaces for this driver. EnabledRaidInterfaces []string `json:"enabled_raid_interfaces"` // The enabled storage interfaces for this driver. EnabledStorageInterfaces []string `json:"enabled_storage_interfaces"` // The enabled vendor interfaces for this driver. EnabledVendorInterfaces []string `json:"enabled_vendor_interfaces"` //A list of relative links. Includes the self and bookmark links. Links []interface{} `json:"links"` // A list of links to driver properties. Properties []interface{} `json:"properties"` } // DriverPage abstracts the raw results of making a ListDrivers() request // against the API. type DriverPage struct { pagination.LinkedPageBase } // IsEmpty returns true if a page contains no Driver results. func (r DriverPage) IsEmpty() (bool, error) { s, err := ExtractDrivers(r) return len(s) == 0, err } // NextPageURL uses the response's embedded link reference to navigate to the // next page of results. func (r DriverPage) NextPageURL() (string, error) { var s struct { Links []gophercloud.Link `json:"drivers_links"` } err := r.ExtractInto(&s) if err != nil { return "", err } return gophercloud.ExtractNextURL(s.Links) } // ExtractDrivers interprets the results of a single page from ListDrivers() // call, producing a slice of Driver entities. func ExtractDrivers(r pagination.Page) ([]Driver, error) { var s []Driver err := ExtractDriversInto(r, &s) return s, err } // GetDriverResult is the response from a Get operation. // Call its Extract method to interpret it as a Driver. type GetDriverResult struct { driverResult } // DriverProperties represents driver properties in the OpenStack Bare Metal API. type DriverProperties map[string]interface{} // Extract interprets any GetPropertiesResult as DriverProperties, if possible. func (r GetPropertiesResult) Extract() (*DriverProperties, error) { var s DriverProperties err := r.ExtractInto(&s) return &s, err } // GetPropertiesResult is the response from a GetDriverProperties operation. // Call its Extract method to interpret it as DriverProperties. type GetPropertiesResult struct { gophercloud.Result } // DiskProperties represents driver disk properties in the OpenStack Bare Metal API. type DiskProperties map[string]interface{} // Extract interprets any GetDiskPropertiesResult as DiskProperties, if possible. func (r GetDiskPropertiesResult) Extract() (*DiskProperties, error) { var s DiskProperties err := r.ExtractInto(&s) return &s, err } // GetDiskPropertiesResult is the response from a GetDriverDiskProperties operation. // Call its Extract method to interpret it as DiskProperties. type GetDiskPropertiesResult struct { gophercloud.Result } golang-github-gophercloud-gophercloud-0.12.0/openstack/baremetal/v1/drivers/testing/000077500000000000000000000000001367513235700305635ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/baremetal/v1/drivers/testing/doc.go000066400000000000000000000000771367513235700316630ustar00rootroot00000000000000// Package testing contains drivers unit tests package testing golang-github-gophercloud-gophercloud-0.12.0/openstack/baremetal/v1/drivers/testing/fixtures.go000066400000000000000000000426541367513235700327760ustar00rootroot00000000000000package testing import ( "fmt" "net/http" "testing" "github.com/gophercloud/gophercloud/openstack/baremetal/v1/drivers" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) // ListDriversBody contains the canned body of a drivers.ListDrivers response, without details. const ListDriversBody = ` { "drivers": [ { "hosts": [ "897ab1dad809" ], "links": [ { "href": "http://127.0.0.1:6385/v1/drivers/agent_ipmitool", "rel": "self" }, { "href": "http://127.0.0.1:6385/drivers/agent_ipmitool", "rel": "bookmark" } ], "name": "agent_ipmitool", "properties": [ { "href": "http://127.0.0.1:6385/v1/drivers/agent_ipmitool/properties", "rel": "self" }, { "href": "http://127.0.0.1:6385/drivers/agent_ipmitool/properties", "rel": "bookmark" } ], "type": "classic" }, { "hosts": [ "897ab1dad809" ], "links": [ { "href": "http://127.0.0.1:6385/v1/drivers/fake", "rel": "self" }, { "href": "http://127.0.0.1:6385/drivers/fake", "rel": "bookmark" } ], "name": "fake", "properties": [ { "href": "http://127.0.0.1:6385/v1/drivers/fake/properties", "rel": "self" }, { "href": "http://127.0.0.1:6385/drivers/fake/properties", "rel": "bookmark" } ], "type": "classic" }, { "hosts": [ "897ab1dad809" ], "links": [ { "href": "http://127.0.0.1:6385/v1/drivers/ipmi", "rel": "self" }, { "href": "http://127.0.0.1:6385/drivers/ipmi", "rel": "bookmark" } ], "name": "ipmi", "properties": [ { "href": "http://127.0.0.1:6385/v1/drivers/ipmi/properties", "rel": "self" }, { "href": "http://127.0.0.1:6385/drivers/ipmi/properties", "rel": "bookmark" } ], "type": "dynamic" } ] } ` const SingleDriverDetails = ` { "default_bios_interface": "no-bios", "default_boot_interface": "pxe", "default_console_interface": "no-console", "default_deploy_interface": "iscsi", "default_inspect_interface": "no-inspect", "default_management_interface": "ipmitool", "default_network_interface": "flat", "default_power_interface": "ipmitool", "default_raid_interface": "no-raid", "default_rescue_interface": "no-rescue", "default_storage_interface": "noop", "default_vendor_interface": "no-vendor", "enabled_bios_interfaces": [ "no-bios" ], "enabled_boot_interfaces": [ "pxe" ], "enabled_console_interfaces": [ "no-console" ], "enabled_deploy_interfaces": [ "iscsi", "direct" ], "enabled_inspect_interfaces": [ "no-inspect" ], "enabled_management_interfaces": [ "ipmitool" ], "enabled_network_interfaces": [ "flat", "noop" ], "enabled_power_interfaces": [ "ipmitool" ], "enabled_raid_interfaces": [ "no-raid", "agent" ], "enabled_rescue_interfaces": [ "no-rescue" ], "enabled_storage_interfaces": [ "noop" ], "enabled_vendor_interfaces": [ "no-vendor" ], "hosts": [ "897ab1dad809" ], "links": [ { "href": "http://127.0.0.1:6385/v1/drivers/ipmi", "rel": "self" }, { "href": "http://127.0.0.1:6385/drivers/ipmi", "rel": "bookmark" } ], "name": "ipmi", "properties": [ { "href": "http://127.0.0.1:6385/v1/drivers/ipmi/properties", "rel": "self" }, { "href": "http://127.0.0.1:6385/drivers/ipmi/properties", "rel": "bookmark" } ], "type": "dynamic" } ` const SingleDriverProperties = ` { "deploy_forces_oob_reboot": "Whether Ironic should force a reboot of the Node via the out-of-band channel after deployment is complete. Provides compatibility with older deploy ramdisks. Defaults to False. Optional.", "deploy_kernel": "UUID (from Glance) of the deployment kernel. Required.", "deploy_ramdisk": "UUID (from Glance) of the ramdisk that is mounted at boot time. Required.", "image_http_proxy": "URL of a proxy server for HTTP connections. Optional.", "image_https_proxy": "URL of a proxy server for HTTPS connections. Optional.", "image_no_proxy": "A comma-separated list of host names, IP addresses and domain names (with optional :port) that will be excluded from proxying. To denote a domain name, use a dot to prefix the domain name. This value will be ignored if ` + "``image_http_proxy`` and ``image_https_proxy``" + ` are not specified. Optional.", "ipmi_address": "IP address or hostname of the node. Required.", "ipmi_bridging": "bridging_type; default is \"no\". One of \"single\", \"dual\", \"no\". Optional.", "ipmi_disable_boot_timeout": "By default ironic will send a raw IPMI command to disable the 60 second timeout for booting. Setting this option to False will NOT send that command; default value is True. Optional.", "ipmi_force_boot_device": "Whether Ironic should specify the boot device to the BMC each time the server is turned on, eg. because the BMC is not capable of remembering the selected boot device across power cycles; default value is False. Optional.", "ipmi_local_address": "local IPMB address for bridged requests. Used only if ipmi_bridging is set to \"single\" or \"dual\". Optional.", "ipmi_password": "password. Optional.", "ipmi_port": "remote IPMI RMCP port. Optional.", "ipmi_priv_level": "privilege level; default is ADMINISTRATOR. One of ADMINISTRATOR, CALLBACK, OPERATOR, USER. Optional.", "ipmi_protocol_version": "the version of the IPMI protocol; default is \"2.0\". One of \"1.5\", \"2.0\". Optional.", "ipmi_target_address": "destination address for bridged request. Required only if ipmi_bridging is set to \"single\" or \"dual\".", "ipmi_target_channel": "destination channel for bridged request. Required only if ipmi_bridging is set to \"single\" or \"dual\".", "ipmi_terminal_port": "node's UDP port to connect to. Only required for console access.", "ipmi_transit_address": "transit address for bridged request. Required only if ipmi_bridging is set to \"dual\".", "ipmi_transit_channel": "transit channel for bridged request. Required only if ipmi_bridging is set to \"dual\".", "ipmi_username": "username; default is NULL user. Optional." } ` const SingleDriverDiskProperties = ` { "controller": "Controller to use for this logical disk. If not specified, the driver will choose a suitable RAID controller on the bare metal node. Optional.", "disk_type": "The type of disk preferred. Valid values are 'hdd' and 'ssd'. If this is not specified, disk type will not be a selection criterion for choosing backing physical disks. Optional.", "interface_type": "The interface type of disk. Valid values are 'sata', 'scsi' and 'sas'. If this is not specified, interface type will not be a selection criterion for choosing backing physical disks. Optional.", "is_root_volume": "Specifies whether this disk is a root volume. By default, this is False. Optional.", "number_of_physical_disks": "Number of physical disks to use for this logical disk. By default, the driver uses the minimum number of disks required for that RAID level. Optional.", "physical_disks": "The physical disks to use for this logical disk. If not specified, the driver will choose suitable physical disks to use. Optional.", "raid_level": "RAID level for the logical disk. Valid values are 'JBOD', '0', '1', '2', '5', '6', '1+0', '5+0' and '6+0'. Required.", "share_physical_disks": "Specifies whether other logical disks can share physical disks with this logical disk. By default, this is False. Optional.", "size_gb": "Size in GiB (Integer) for the logical disk. Use 'MAX' as size_gb if this logical disk is supposed to use the rest of the space available. Required.", "volume_name": "Name of the volume to be created. If this is not specified, it will be auto-generated. Optional." } ` var ( DriverAgentIpmitool = drivers.Driver{ Name: "agent_ipmitool", Type: "classic", Hosts: []string{"897ab1dad809"}, Links: []interface{}{ map[string]interface{}{ "href": "http://127.0.0.1:6385/v1/drivers/agent_ipmitool", "rel": "self", }, map[string]interface{}{ "href": "http://127.0.0.1:6385/drivers/agent_ipmitool", "rel": "bookmark", }, }, Properties: []interface{}{ map[string]interface{}{ "href": "http://127.0.0.1:6385/v1/drivers/agent_ipmitool/properties", "rel": "self", }, map[string]interface{}{ "href": "http://127.0.0.1:6385/drivers/agent_ipmitool/properties", "rel": "bookmark", }, }, } DriverFake = drivers.Driver{ Name: "fake", Type: "classic", Hosts: []string{"897ab1dad809"}, Links: []interface{}{ map[string]interface{}{ "href": "http://127.0.0.1:6385/v1/drivers/fake", "rel": "self", }, map[string]interface{}{ "href": "http://127.0.0.1:6385/drivers/fake", "rel": "bookmark", }, }, Properties: []interface{}{ map[string]interface{}{ "href": "http://127.0.0.1:6385/v1/drivers/fake/properties", "rel": "self", }, map[string]interface{}{ "href": "http://127.0.0.1:6385/drivers/fake/properties", "rel": "bookmark", }, }, } DriverIpmi = drivers.Driver{ Name: "ipmi", Type: "dynamic", Hosts: []string{"897ab1dad809"}, DefaultBiosInterface: "no-bios", DefaultBootInterface: "pxe", DefaultConsoleInterface: "no-console", DefaultDeployInterface: "iscsi", DefaultInspectInterface: "no-inspect", DefaultManagementInterface: "ipmitool", DefaultNetworkInterface: "flat", DefaultPowerInterface: "ipmitool", DefaultRaidInterface: "no-raid", DefaultRescueInterface: "no-rescue", DefaultStorageInterface: "noop", DefaultVendorInterface: "no-vendor", EnabledBiosInterfaces: []string{"no-bios"}, EnabledBootInterfaces: []string{"pxe"}, EnabledConsoleInterface: []string{"no-console"}, EnabledDeployInterfaces: []string{"iscsi", "direct"}, EnabledInspectInterfaces: []string{"no-inspect"}, EnabledManagementInterfaces: []string{"ipmitool"}, EnabledNetworkInterfaces: []string{"flat", "noop"}, EnabledPowerInterfaces: []string{"ipmitool"}, EnabledRescueInterfaces: []string{"no-rescue"}, EnabledRaidInterfaces: []string{"no-raid", "agent"}, EnabledStorageInterfaces: []string{"noop"}, EnabledVendorInterfaces: []string{"no-vendor"}, Links: []interface{}{ map[string]interface{}{ "href": "http://127.0.0.1:6385/v1/drivers/ipmi", "rel": "self", }, map[string]interface{}{ "href": "http://127.0.0.1:6385/drivers/ipmi", "rel": "bookmark", }, }, Properties: []interface{}{ map[string]interface{}{ "href": "http://127.0.0.1:6385/v1/drivers/ipmi/properties", "rel": "self", }, map[string]interface{}{ "href": "http://127.0.0.1:6385/drivers/ipmi/properties", "rel": "bookmark", }, }, } DriverIpmiToolProperties = drivers.DriverProperties{ "deploy_forces_oob_reboot": "Whether Ironic should force a reboot of the Node via the out-of-band channel after deployment is complete. Provides compatibility with older deploy ramdisks. Defaults to False. Optional.", "deploy_kernel": "UUID (from Glance) of the deployment kernel. Required.", "deploy_ramdisk": "UUID (from Glance) of the ramdisk that is mounted at boot time. Required.", "image_http_proxy": "URL of a proxy server for HTTP connections. Optional.", "image_https_proxy": "URL of a proxy server for HTTPS connections. Optional.", "image_no_proxy": "A comma-separated list of host names, IP addresses and domain names (with optional :port) that will be excluded from proxying. To denote a domain name, use a dot to prefix the domain name. This value will be ignored if ``image_http_proxy`` and ``image_https_proxy`` are not specified. Optional.", "ipmi_address": "IP address or hostname of the node. Required.", "ipmi_bridging": "bridging_type; default is \"no\". One of \"single\", \"dual\", \"no\". Optional.", "ipmi_disable_boot_timeout": "By default ironic will send a raw IPMI command to disable the 60 second timeout for booting. Setting this option to False will NOT send that command; default value is True. Optional.", "ipmi_force_boot_device": "Whether Ironic should specify the boot device to the BMC each time the server is turned on, eg. because the BMC is not capable of remembering the selected boot device across power cycles; default value is False. Optional.", "ipmi_local_address": "local IPMB address for bridged requests. Used only if ipmi_bridging is set to \"single\" or \"dual\". Optional.", "ipmi_password": "password. Optional.", "ipmi_port": "remote IPMI RMCP port. Optional.", "ipmi_priv_level": "privilege level; default is ADMINISTRATOR. One of ADMINISTRATOR, CALLBACK, OPERATOR, USER. Optional.", "ipmi_protocol_version": "the version of the IPMI protocol; default is \"2.0\". One of \"1.5\", \"2.0\". Optional.", "ipmi_target_address": "destination address for bridged request. Required only if ipmi_bridging is set to \"single\" or \"dual\".", "ipmi_target_channel": "destination channel for bridged request. Required only if ipmi_bridging is set to \"single\" or \"dual\".", "ipmi_terminal_port": "node's UDP port to connect to. Only required for console access.", "ipmi_transit_address": "transit address for bridged request. Required only if ipmi_bridging is set to \"dual\".", "ipmi_transit_channel": "transit channel for bridged request. Required only if ipmi_bridging is set to \"dual\".", "ipmi_username": "username; default is NULL user. Optional.", } DriverIpmiToolDisk = drivers.DiskProperties{ "controller": "Controller to use for this logical disk. If not specified, the driver will choose a suitable RAID controller on the bare metal node. Optional.", "disk_type": "The type of disk preferred. Valid values are 'hdd' and 'ssd'. If this is not specified, disk type will not be a selection criterion for choosing backing physical disks. Optional.", "interface_type": "The interface type of disk. Valid values are 'sata', 'scsi' and 'sas'. If this is not specified, interface type will not be a selection criterion for choosing backing physical disks. Optional.", "is_root_volume": "Specifies whether this disk is a root volume. By default, this is False. Optional.", "number_of_physical_disks": "Number of physical disks to use for this logical disk. By default, the driver uses the minimum number of disks required for that RAID level. Optional.", "physical_disks": "The physical disks to use for this logical disk. If not specified, the driver will choose suitable physical disks to use. Optional.", "raid_level": "RAID level for the logical disk. Valid values are 'JBOD', '0', '1', '2', '5', '6', '1+0', '5+0' and '6+0'. Required.", "share_physical_disks": "Specifies whether other logical disks can share physical disks with this logical disk. By default, this is False. Optional.", "size_gb": "Size in GiB (Integer) for the logical disk. Use 'MAX' as size_gb if this logical disk is supposed to use the rest of the space available. Required.", "volume_name": "Name of the volume to be created. If this is not specified, it will be auto-generated. Optional.", } ) // HandleListDriversSuccessfully sets up the test server to respond to a drivers ListDrivers request. func HandleListDriversSuccessfully(t *testing.T) { th.Mux.HandleFunc("/drivers", 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() fmt.Fprintf(w, ListDriversBody) }) } // HandleGetDriverDetailsSuccessfully sets up the test server to respond to a drivers GetDriverDetails request. func HandleGetDriverDetailsSuccessfully(t *testing.T) { th.Mux.HandleFunc("/drivers/ipmi", 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, SingleDriverDetails) }) } // HandleGetDriverPropertiesSuccessfully sets up the test server to respond to a drivers GetDriverProperties request. func HandleGetDriverPropertiesSuccessfully(t *testing.T) { th.Mux.HandleFunc("/drivers/agent_ipmitool/properties", 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, SingleDriverProperties) }) } // HandleGetDriverDiskPropertiesSuccessfully sets up the test server to respond to a drivers GetDriverDiskProperties request. func HandleGetDriverDiskPropertiesSuccessfully(t *testing.T) { th.Mux.HandleFunc("/drivers/agent_ipmitool/raid/logical_disk_properties", 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, SingleDriverDiskProperties) }) } golang-github-gophercloud-gophercloud-0.12.0/openstack/baremetal/v1/drivers/testing/requests_test.go000066400000000000000000000037471367513235700340370ustar00rootroot00000000000000package testing import ( "testing" "github.com/gophercloud/gophercloud/openstack/baremetal/v1/drivers" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) func TestListDrivers(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleListDriversSuccessfully(t) pages := 0 err := drivers.ListDrivers(client.ServiceClient(), drivers.ListDriversOpts{}).EachPage(func(page pagination.Page) (bool, error) { pages++ actual, err := drivers.ExtractDrivers(page) if err != nil { return false, err } if len(actual) != 3 { t.Fatalf("Expected 3 drivers, got %d", len(actual)) } th.CheckDeepEquals(t, DriverAgentIpmitool, actual[0]) th.CheckDeepEquals(t, DriverFake, actual[1]) th.AssertEquals(t, "ipmi", actual[2].Name) return true, nil }) th.AssertNoErr(t, err) if pages != 1 { t.Errorf("Expected 1 page, saw %d", pages) } } func TestGetDriverDetails(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleGetDriverDetailsSuccessfully(t) c := client.ServiceClient() actual, err := drivers.GetDriverDetails(c, "ipmi").Extract() if err != nil { t.Fatalf("Unexpected Get error: %v", err) } th.CheckDeepEquals(t, DriverIpmi, *actual) } func TestGetDriverProperties(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleGetDriverPropertiesSuccessfully(t) c := client.ServiceClient() actual, err := drivers.GetDriverProperties(c, "agent_ipmitool").Extract() if err != nil { t.Fatalf("Unexpected Get error: %v", err) } th.CheckDeepEquals(t, DriverIpmiToolProperties, *actual) } func TestGetDriverDiskProperties(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleGetDriverDiskPropertiesSuccessfully(t) c := client.ServiceClient() actual, err := drivers.GetDriverDiskProperties(c, "agent_ipmitool").Extract() if err != nil { t.Fatalf("Unexpected Get error: %v", err) } th.CheckDeepEquals(t, DriverIpmiToolDisk, *actual) } golang-github-gophercloud-gophercloud-0.12.0/openstack/baremetal/v1/drivers/urls.go000066400000000000000000000011671367513235700304270ustar00rootroot00000000000000package drivers import "github.com/gophercloud/gophercloud" func driversURL(client *gophercloud.ServiceClient) string { return client.ServiceURL("drivers") } func driverDetailsURL(client *gophercloud.ServiceClient, driverName string) string { return client.ServiceURL("drivers", driverName) } func driverPropertiesURL(client *gophercloud.ServiceClient, driverName string) string { return client.ServiceURL("drivers", driverName, "properties") } func driverDiskPropertiesURL(client *gophercloud.ServiceClient, driverName string) string { return client.ServiceURL("drivers", driverName, "raid", "logical_disk_properties") } golang-github-gophercloud-gophercloud-0.12.0/openstack/baremetal/v1/nodes/000077500000000000000000000000001367513235700265405ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/baremetal/v1/nodes/doc.go000066400000000000000000000054771367513235700276510ustar00rootroot00000000000000/* Package nodes provides information and interaction with the nodes API resource in the OpenStack Bare Metal service. Example to List Nodes with Detail nodes.ListDetail(client, nodes.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { nodeList, err := nodes.ExtractNodes(page) if err != nil { return false, err } for _, n := range nodeList { // Do something } return true, nil }) Example to List Nodes listOpts := nodes.ListOpts{ ProvisionState: nodes.Deploying, Fields: []string{"name"}, } nodes.List(client, listOpts).EachPage(func(page pagination.Page) (bool, error) { nodeList, err := nodes.ExtractNodes(page) if err != nil { return false, err } for _, n := range nodeList { // Do something } return true, nil }) Example to Create Node createOpts := nodes.CreateOpts Driver: "ipmi", BootInterface: "pxe", Name: "coconuts", DriverInfo: map[string]interface{}{ "ipmi_port": "6230", "ipmi_username": "admin", "deploy_kernel": "http://172.22.0.1/images/tinyipa-stable-rocky.vmlinuz", "ipmi_address": "192.168.122.1", "deploy_ramdisk": "http://172.22.0.1/images/tinyipa-stable-rocky.gz", "ipmi_password": "admin", }, } createNode, err := nodes.Create(client, createOpts).Extract() if err != nil { panic(err) } Example to Get Node showNode, err := nodes.Get(client, "c9afd385-5d89-4ecb-9e1c-68194da6b474").Extract() if err != nil { panic(err) } Example to Update Node updateOpts := nodes.UpdateOpts{ nodes.UpdateOperation{ Op: ReplaceOp, Path: "/maintenance", Value: "true", }, } updateNode, err := nodes.Update(client, "c9afd385-5d89-4ecb-9e1c-68194da6b474", updateOpts).Extract() if err != nil { panic(err) } Example to Delete Node err = nodes.Delete(client, "c9afd385-5d89-4ecb-9e1c-68194da6b474").ExtractErr() if err != nil { panic(err) } Example to Validate Node validation, err := nodes.Validate(client, "a62b8495-52e2-407b-b3cb-62775d04c2b8").Extract() if err != nil { panic(err) } Example to inject non-masking interrupts err := nodes.InjectNMI(client, "a62b8495-52e2-407b-b3cb-62775d04c2b8").ExtractErr() if err != nil { panic(err) } Example to get array of supported boot devices for a node bootDevices, err := nodes.GetSupportedBootDevices(client, "a62b8495-52e2-407b-b3cb-62775d04c2b8").Extract() if err != nil { panic(err) } Example to set boot device for a node bootOpts := nodes.BootDeviceOpts{ BootDevice: "pxe", Persistent: false, } err := nodes.SetBootDevice(client, "a62b8495-52e2-407b-b3cb-62775d04c2b8", bootOpts).ExtractErr() if err != nil { panic(err) } Example to get boot device for a node bootDevice, err := nodes.GetBootDevice(client, "a62b8495-52e2-407b-b3cb-62775d04c2b8").Extract() if err != nil { panic(err) } */ package nodes golang-github-gophercloud-gophercloud-0.12.0/openstack/baremetal/v1/nodes/requests.go000066400000000000000000000506711367513235700307530ustar00rootroot00000000000000package nodes 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 { ToNodeListQuery() (string, error) ToNodeListDetailQuery() (string, error) } // Provision state reports the current provision state of the node, these are only used in filtering type ProvisionState string const ( Enroll ProvisionState = "enroll" Verifying ProvisionState = "verifying" Manageable ProvisionState = "manageable" Available ProvisionState = "available" Active ProvisionState = "active" DeployWait ProvisionState = "wait call-back" Deploying ProvisionState = "deploying" DeployFail ProvisionState = "deploy failed" DeployDone ProvisionState = "deploy complete" Deleting ProvisionState = "deleting" Deleted ProvisionState = "deleted" Cleaning ProvisionState = "cleaning" CleanWait ProvisionState = "clean wait" CleanFail ProvisionState = "clean failed" Error ProvisionState = "error" Rebuild ProvisionState = "rebuild" Inspecting ProvisionState = "inspecting" InspectFail ProvisionState = "inspect failed" InspectWait ProvisionState = "inspect wait" Adopting ProvisionState = "adopting" AdoptFail ProvisionState = "adopt failed" Rescue ProvisionState = "rescue" RescueFail ProvisionState = "rescue failed" Rescuing ProvisionState = "rescuing" UnrescueFail ProvisionState = "unrescue failed" ) // TargetProvisionState is used when setting the provision state for a node. type TargetProvisionState string const ( TargetActive TargetProvisionState = "active" TargetDeleted TargetProvisionState = "deleted" TargetManage TargetProvisionState = "manage" TargetProvide TargetProvisionState = "provide" TargetInspect TargetProvisionState = "inspect" TargetAbort TargetProvisionState = "abort" TargetClean TargetProvisionState = "clean" TargetAdopt TargetProvisionState = "adopt" TargetRescue TargetProvisionState = "rescue" TargetUnrescue TargetProvisionState = "unrescue" ) // 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 node attributes you want to see returned. Marker and Limit are used // for pagination. type ListOpts struct { // Filter the list by specific instance UUID InstanceUUID string `q:"instance_uuid"` // Filter the list by chassis UUID ChassisUUID string `q:"chassis_uuid"` // Filter the list by maintenance set to True or False Maintenance bool `q:"maintenance"` // Nodes which are, or are not, associated with an instance_uuid. Associated bool `q:"associated"` // Only return those with the specified provision_state. ProvisionState ProvisionState `q:"provision_state"` // Filter the list with the specified driver. Driver string `q:"driver"` // Filter the list with the specified resource class. ResourceClass string `q:"resource_class"` // Filter the list with the specified conductor_group. ConductorGroup string `q:"conductor_group"` // Filter the list with the specified fault. Fault string `q:"fault"` // One or more fields to be returned in the response. Fields []string `q:"fields"` // Requests a page size of items. Limit int `q:"limit"` // The ID of the last-seen item. Marker string `q:"marker"` // Sorts the response by the requested sort direction. SortDir string `q:"sort_dir"` // Sorts the response by the this attribute value. SortKey string `q:"sort_key"` // A string or UUID of the tenant who owns the baremetal node. Owner string `q:"owner"` } // ToNodeListQuery formats a ListOpts into a query string. func (opts ListOpts) ToNodeListQuery() (string, error) { q, err := gophercloud.BuildQueryString(opts) return q.String(), err } // List makes a request against the API to list nodes accessible to you. 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}} }) } // ToNodeListDetailQuery formats a ListOpts into a query string for the list details API. func (opts ListOpts) ToNodeListDetailQuery() (string, error) { // Detail endpoint can't filter by Fields if len(opts.Fields) > 0 { return "", fmt.Errorf("fields is not a valid option when getting a detailed listing of nodes") } q, err := gophercloud.BuildQueryString(opts) return q.String(), err } // Return a list of bare metal Nodes with complete details. Some filtering is possible by passing in flags in ListOpts, // but you cannot limit by the fields returned. func ListDetail(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { // This URL is deprecated. In the future, we should compare the microversion and if >= 1.43, hit the listURL // with ListOpts{Detail: true,} url := listDetailURL(client) if opts != nil { query, err := opts.ToNodeListDetailQuery() 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}} }) } // Get requests details on a single node, by ID. func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { resp, err := client.Get(getURL(client, id), &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // CreateOptsBuilder allows extensions to add additional parameters to the // Create request. type CreateOptsBuilder interface { ToNodeCreateMap() (map[string]interface{}, error) } // CreateOpts specifies node creation parameters. type CreateOpts struct { // The boot interface for a Node, e.g. “pxeâ€. BootInterface string `json:"boot_interface,omitempty"` // The conductor group for a node. Case-insensitive string up to 255 characters, containing a-z, 0-9, _, -, and .. ConductorGroup string `json:"conductor_group,omitempty"` // The console interface for a node, e.g. “no-consoleâ€. ConsoleInterface string `json:"console_interface,omitempty"` // The deploy interface for a node, e.g. “iscsiâ€. DeployInterface string `json:"deploy_interface,omitempty"` // All the metadata required by the driver to manage this Node. List of fields varies between drivers, and can // be retrieved from the /v1/drivers//properties resource. DriverInfo map[string]interface{} `json:"driver_info,omitempty"` // name of the driver used to manage this Node. Driver string `json:"driver,omitempty"` // A set of one or more arbitrary metadata key and value pairs. Extra map[string]interface{} `json:"extra,omitempty"` // The interface used for node inspection, e.g. “no-inspectâ€. InspectInterface string `json:"inspect_interface,omitempty"` // Interface for out-of-band node management, e.g. “ipmitoolâ€. ManagementInterface string `json:"management_interface,omitempty"` // Human-readable identifier for the Node resource. May be undefined. Certain words are reserved. Name string `json:"name,omitempty"` // Which Network Interface provider to use when plumbing the network connections for this Node. NetworkInterface string `json:"network_interface,omitempty"` // Interface used for performing power actions on the node, e.g. “ipmitoolâ€. PowerInterface string `json:"power_interface,omitempty"` // Physical characteristics of this Node. Populated during inspection, if performed. Can be edited via the REST // API at any time. Properties map[string]interface{} `json:"properties,omitempty"` // Interface used for configuring RAID on this node, e.g. “no-raidâ€. RAIDInterface string `json:"raid_interface,omitempty"` // The interface used for node rescue, e.g. “no-rescueâ€. RescueInterface string `json:"rescue_interface,omitempty"` // A string which can be used by external schedulers to identify this Node as a unit of a specific type // of resource. ResourceClass string `json:"resource_class,omitempty"` // Interface used for attaching and detaching volumes on this node, e.g. “cinderâ€. StorageInterface string `json:"storage_interface,omitempty"` // The UUID for the resource. UUID string `json:"uuid,omitempty"` // Interface for vendor-specific functionality on this node, e.g. “no-vendorâ€. VendorInterface string `json:"vendor_interface,omitempty"` // A string or UUID of the tenant who owns the baremetal node. Owner string `json:"owner,omitempty"` } // ToNodeCreateMap assembles a request body based on the contents of a CreateOpts. func (opts CreateOpts) ToNodeCreateMap() (map[string]interface{}, error) { body, err := gophercloud.BuildRequestBody(opts, "") if err != nil { return nil, err } return body, nil } // Create requests a node to be created func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { reqBody, err := opts.ToNodeCreateMap() if err != nil { r.Err = err return } resp, err := client.Post(createURL(client), reqBody, &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } type Patch interface { ToNodeUpdateMap() (map[string]interface{}, error) } // UpdateOpts is a slice of Patches used to update a node type UpdateOpts []Patch type UpdateOp string const ( ReplaceOp UpdateOp = "replace" AddOp UpdateOp = "add" RemoveOp UpdateOp = "remove" ) type UpdateOperation struct { Op UpdateOp `json:"op" required:"true"` Path string `json:"path" required:"true"` Value interface{} `json:"value,omitempty"` } func (opts UpdateOperation) ToNodeUpdateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "") } // Update requests that a node be updated func Update(client *gophercloud.ServiceClient, id string, opts UpdateOpts) (r UpdateResult) { body := make([]map[string]interface{}, len(opts)) for i, patch := range opts { result, err := patch.ToNodeUpdateMap() if err != nil { r.Err = err return } body[i] = result } resp, err := client.Patch(updateURL(client, id), body, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete requests that a node be removed func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { resp, err := client.Delete(deleteURL(client, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Request that Ironic validate whether the Node’s driver has enough information to manage the Node. This polls each // interface on the driver, and returns the status of that interface. func Validate(client *gophercloud.ServiceClient, id string) (r ValidateResult) { resp, err := client.Get(validateURL(client, id), &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Inject NMI (Non-Masking Interrupts) for the given Node. This feature can be used for hardware diagnostics, and // actual support depends on a driver. func InjectNMI(client *gophercloud.ServiceClient, id string) (r InjectNMIResult) { resp, err := client.Put(injectNMIURL(client, id), map[string]string{}, nil, &gophercloud.RequestOpts{ OkCodes: []int{204}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } type BootDeviceOpts struct { BootDevice string `json:"boot_device"` // e.g., 'pxe', 'disk', etc. Persistent bool `json:"persistent"` // Whether this is one-time or not } // BootDeviceOptsBuilder allows extensions to add additional parameters to the // SetBootDevice request. type BootDeviceOptsBuilder interface { ToBootDeviceMap() (map[string]interface{}, error) } // ToBootDeviceSetMap assembles a request body based on the contents of a BootDeviceOpts. func (opts BootDeviceOpts) ToBootDeviceMap() (map[string]interface{}, error) { body, err := gophercloud.BuildRequestBody(opts, "") if err != nil { return nil, err } return body, nil } // Set the boot device for the given Node, and set it persistently or for one-time boot. The exact behaviour // of this depends on the hardware driver. func SetBootDevice(client *gophercloud.ServiceClient, id string, bootDevice BootDeviceOptsBuilder) (r SetBootDeviceResult) { reqBody, err := bootDevice.ToBootDeviceMap() if err != nil { r.Err = err return } resp, err := client.Put(bootDeviceURL(client, id), reqBody, nil, &gophercloud.RequestOpts{ OkCodes: []int{204}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get the current boot device for the given Node. func GetBootDevice(client *gophercloud.ServiceClient, id string) (r BootDeviceResult) { resp, err := client.Get(bootDeviceURL(client, id), &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Retrieve the acceptable set of supported boot devices for a specific Node. func GetSupportedBootDevices(client *gophercloud.ServiceClient, id string) (r SupportedBootDeviceResult) { resp, err := client.Get(supportedBootDeviceURL(client, id), &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // A cleaning step has required keys ‘interface’ and ‘step’, and optional key ‘args’. If specified, // the value for ‘args’ is a keyword variable argument dictionary that is passed to the cleaning step // method. type CleanStep struct { Interface string `json:"interface" required:"true"` Step string `json:"step" required:"true"` Args map[string]interface{} `json:"args,omitempty"` } // ProvisionStateOptsBuilder allows extensions to add additional parameters to the // ChangeProvisionState request. type ProvisionStateOptsBuilder interface { ToProvisionStateMap() (map[string]interface{}, error) } // Starting with Ironic API version 1.56, a configdrive may be a JSON object with structured data. // Prior to this version, it must be a base64-encoded, gzipped ISO9660 image. type ConfigDrive struct { MetaData map[string]interface{} `json:"meta_data,omitempty"` NetworkData map[string]interface{} `json:"network_data,omitempty"` UserData interface{} `json:"user_data,omitempty"` } // ProvisionStateOpts for a request to change a node's provision state. A config drive should be base64-encoded // gzipped ISO9660 image. type ProvisionStateOpts struct { Target TargetProvisionState `json:"target" required:"true"` ConfigDrive interface{} `json:"configdrive,omitempty"` CleanSteps []CleanStep `json:"clean_steps,omitempty"` RescuePassword string `json:"rescue_password,omitempty"` } // ToProvisionStateMap assembles a request body based on the contents of a CreateOpts. func (opts ProvisionStateOpts) ToProvisionStateMap() (map[string]interface{}, error) { body, err := gophercloud.BuildRequestBody(opts, "") if err != nil { return nil, err } return body, nil } // Request a change to the Node’s provision state. Acceptable target states depend on the Node’s current provision // state. More detailed documentation of the Ironic State Machine is available in the developer docs. func ChangeProvisionState(client *gophercloud.ServiceClient, id string, opts ProvisionStateOptsBuilder) (r ChangeStateResult) { reqBody, err := opts.ToProvisionStateMap() if err != nil { r.Err = err return } resp, err := client.Put(provisionStateURL(client, id), reqBody, nil, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } type TargetPowerState string // TargetPowerState is used when changing the power state of a node. const ( PowerOn TargetPowerState = "power on" PowerOff TargetPowerState = "power off" Rebooting TargetPowerState = "rebooting" SoftPowerOff TargetPowerState = "soft power off" SoftRebooting TargetPowerState = "soft rebooting" ) // PowerStateOptsBuilder allows extensions to add additional parameters to the ChangePowerState request. type PowerStateOptsBuilder interface { ToPowerStateMap() (map[string]interface{}, error) } // PowerStateOpts for a request to change a node's power state. type PowerStateOpts struct { Target TargetPowerState `json:"target" required:"true"` Timeout int `json:"timeout,omitempty"` } // ToPowerStateMap assembles a request body based on the contents of a PowerStateOpts. func (opts PowerStateOpts) ToPowerStateMap() (map[string]interface{}, error) { body, err := gophercloud.BuildRequestBody(opts, "") if err != nil { return nil, err } return body, nil } // Request to change a Node's power state. func ChangePowerState(client *gophercloud.ServiceClient, id string, opts PowerStateOptsBuilder) (r ChangePowerStateResult) { reqBody, err := opts.ToPowerStateMap() if err != nil { r.Err = err return } resp, err := client.Put(powerStateURL(client, id), reqBody, nil, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // This is the desired RAID configuration on the bare metal node. type RAIDConfigOpts struct { LogicalDisks []LogicalDisk `json:"logical_disks"` } // RAIDConfigOptsBuilder allows extensions to modify a set RAID config request. type RAIDConfigOptsBuilder interface { ToRAIDConfigMap() (map[string]interface{}, error) } // RAIDLevel type is used to specify the RAID level for a logical disk. type RAIDLevel string const ( RAID0 RAIDLevel = "0" RAID1 RAIDLevel = "1" RAID2 RAIDLevel = "2" RAID5 RAIDLevel = "5" RAID6 RAIDLevel = "6" RAID10 RAIDLevel = "1+0" RAID50 RAIDLevel = "5+0" RAID60 RAIDLevel = "6+0" ) // DiskType is used to specify the disk type for a logical disk, e.g. hdd or ssd. type DiskType string const ( HDD DiskType = "hdd" SSD DiskType = "ssd" ) // InterfaceType is used to specify the interface for a logical disk. type InterfaceType string const ( SATA InterfaceType = "sata" SCSI InterfaceType = "scsi" SAS InterfaceType = "sas" ) type LogicalDisk struct { // Size (Integer) of the logical disk to be created in GiB. If unspecified, "MAX" will be used. SizeGB *int `json:"size_gb"` // RAID level for the logical disk. RAIDLevel RAIDLevel `json:"raid_level" required:"true"` // Name of the volume. Should be unique within the Node. If not specified, volume name will be auto-generated. VolumeName string `json:"volume_name,omitempty"` // Set to true if this is the root volume. At most one logical disk can have this set to true. IsRootVolume *bool `json:"is_root_volume,omitempty"` // Set to true if this logical disk can share physical disks with other logical disks. SharePhysicalDisks *bool `json:"share_physical_disks,omitempty"` // If this is not specified, disk type will not be a criterion to find backing physical disks DiskType DiskType `json:"disk_type,omitempty"` // If this is not specified, interface type will not be a criterion to find backing physical disks. InterfaceType InterfaceType `json:"interface_type,omitempty"` // Integer, number of disks to use for the logical disk. Defaults to minimum number of disks required // for the particular RAID level. NumberOfPhysicalDisks int `json:"number_of_physical_disks,omitempty"` // The name of the controller as read by the RAID interface. Controller string `json:"controller,omitempty"` // A list of physical disks to use as read by the RAID interface. PhysicalDisks []interface{} `json:"physical_disks,omitempty"` } func (opts RAIDConfigOpts) ToRAIDConfigMap() (map[string]interface{}, error) { body, err := gophercloud.BuildRequestBody(opts, "") if err != nil { return nil, err } if body["logical_disks"] != nil { for _, v := range body["logical_disks"].([]interface{}) { if logicalDisk, ok := v.(map[string]interface{}); ok { if logicalDisk["size_gb"] == nil { logicalDisk["size_gb"] = "MAX" } } } } return body, nil } // Request to change a Node's RAID config. func SetRAIDConfig(client *gophercloud.ServiceClient, id string, raidConfigOptsBuilder RAIDConfigOptsBuilder) (r ChangeStateResult) { reqBody, err := raidConfigOptsBuilder.ToRAIDConfigMap() if err != nil { r.Err = err return } resp, err := client.Put(raidConfigURL(client, id), reqBody, nil, &gophercloud.RequestOpts{ OkCodes: []int{204}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } golang-github-gophercloud-gophercloud-0.12.0/openstack/baremetal/v1/nodes/results.go000066400000000000000000000272301367513235700305740ustar00rootroot00000000000000package nodes import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) type nodeResult struct { gophercloud.Result } // Extract interprets any nodeResult as a Node, if possible. func (r nodeResult) Extract() (*Node, error) { var s Node err := r.ExtractInto(&s) return &s, err } // Extract interprets a BootDeviceResult as BootDeviceOpts, if possible. func (r BootDeviceResult) Extract() (*BootDeviceOpts, error) { var s BootDeviceOpts err := r.ExtractInto(&s) return &s, err } // Extract interprets a SupportedBootDeviceResult as an array of supported boot devices, if possible. func (r SupportedBootDeviceResult) Extract() ([]string, error) { var s struct { Devices []string `json:"supported_boot_devices"` } err := r.ExtractInto(&s) return s.Devices, err } // Extract interprets a ValidateResult as NodeValidation, if possible. func (r ValidateResult) Extract() (*NodeValidation, error) { var s NodeValidation err := r.ExtractInto(&s) return &s, err } func (r nodeResult) ExtractInto(v interface{}) error { return r.Result.ExtractIntoStructPtr(v, "") } func ExtractNodesInto(r pagination.Page, v interface{}) error { return r.(NodePage).Result.ExtractIntoSlicePtr(v, "nodes") } // Node represents a node in the OpenStack Bare Metal API. type Node struct { // UUID for the resource. UUID string `json:"uuid"` // Identifier for the Node resource. May be undefined. Certain words are reserved. Name string `json:"name"` // Current power state of this Node. Usually, “power on†or “power offâ€, but may be “None†// if Ironic is unable to determine the power state (eg, due to hardware failure). PowerState string `json:"power_state"` // A power state transition has been requested, this field represents the requested (ie, “targetâ€) // state either “power onâ€, “power offâ€, “rebootingâ€, “soft power off†or “soft rebootingâ€. TargetPowerState string `json:"target_power_state"` // Current provisioning state of this Node. ProvisionState string `json:"provision_state"` // A provisioning action has been requested, this field represents the requested (ie, “targetâ€) state. Note // that a Node may go through several states during its transition to this target state. For instance, when // requesting an instance be deployed to an AVAILABLE Node, the Node may go through the following state // change progression: AVAILABLE -> DEPLOYING -> DEPLOYWAIT -> DEPLOYING -> ACTIVE TargetProvisionState string `json:"target_provision_state"` // Whether or not this Node is currently in “maintenance modeâ€. Setting a Node into maintenance mode removes it // from the available resource pool and halts some internal automation. This can happen manually (eg, via an API // request) or automatically when Ironic detects a hardware fault that prevents communication with the machine. Maintenance bool `json:"maintenance"` // Description of the reason why this Node was placed into maintenance mode MaintenanceReason string `json:"maintenance_reason"` // Fault indicates the active fault detected by ironic, typically the Node is in “maintenance modeâ€. None means no // fault has been detected by ironic. “power failure†indicates ironic failed to retrieve power state from this // node. There are other possible types, e.g., “clean failure†and “rescue abort failureâ€. Fault string `json:"fault"` // Error from the most recent (last) transaction that started but failed to finish. LastError string `json:"last_error"` // Name of an Ironic Conductor host which is holding a lock on this node, if a lock is held. Usually “nullâ€, // but this field can be useful for debugging. Reservation string `json:"reservation"` // Name of the driver. Driver string `json:"driver"` // The metadata required by the driver to manage this Node. List of fields varies between drivers, and can be // retrieved from the /v1/drivers//properties resource. DriverInfo map[string]interface{} `json:"driver_info"` // Metadata set and stored by the Node’s driver. This field is read-only. DriverInternalInfo map[string]interface{} `json:"driver_internal_info"` // Characteristics of this Node. Populated by ironic-inspector during inspection. May be edited via the REST // API at any time. Properties map[string]interface{} `json:"properties"` // Used to customize the deployed image. May include root partition size, a base 64 encoded config drive, and other // metadata. Note that this field is erased automatically when the instance is deleted (this is done by requesting // the Node provision state be changed to DELETED). InstanceInfo map[string]interface{} `json:"instance_info"` // ID of the Nova instance associated with this Node. InstanceUUID string `json:"instance_uuid"` // ID of the chassis associated with this Node. May be empty or None. ChassisUUID string `json:"chassis_uuid"` // Set of one or more arbitrary metadata key and value pairs. Extra map[string]interface{} `json:"extra"` // Whether console access is enabled or disabled on this node. ConsoleEnabled bool `json:"console_enabled"` // The current RAID configuration of the node. Introduced with the cleaning feature. RAIDConfig map[string]interface{} `json:"raid_config"` // The requested RAID configuration of the node, which will be applied when the Node next transitions // through the CLEANING state. Introduced with the cleaning feature. TargetRAIDConfig map[string]interface{} `json:"target_raid_config"` // Current clean step. Introduced with the cleaning feature. CleanStep map[string]interface{} `json:"clean_step"` // Current deploy step. DeployStep map[string]interface{} `json:"deploy_step"` // String which can be used by external schedulers to identify this Node as a unit of a specific type of resource. // For more details, see: https://docs.openstack.org/ironic/latest/install/configure-nova-flavors.html ResourceClass string `json:"resource_class"` // Boot interface for a Node, e.g. “pxeâ€. BootInterface string `json:"boot_interface"` // Console interface for a node, e.g. “no-consoleâ€. ConsoleInterface string `json:"console_interface"` // Deploy interface for a node, e.g. “iscsiâ€. DeployInterface string `json:"deploy_interface"` // Interface used for node inspection, e.g. “no-inspectâ€. InspectInterface string `json:"inspect_interface"` // For out-of-band node management, e.g. “ipmitoolâ€. ManagementInterface string `json:"management_interface"` // Network Interface provider to use when plumbing the network connections for this Node. NetworkInterface string `json:"network_interface"` // used for performing power actions on the node, e.g. “ipmitoolâ€. PowerInterface string `json:"power_interface"` // Used for configuring RAID on this node, e.g. “no-raidâ€. RAIDInterface string `json:"raid_interface"` // Interface used for node rescue, e.g. “no-rescueâ€. RescueInterface string `json:"rescue_interface"` // Used for attaching and detaching volumes on this node, e.g. “cinderâ€. StorageInterface string `json:"storage_interface"` // Array of traits for this node. Traits []string `json:"traits"` // For vendor-specific functionality on this node, e.g. “no-vendorâ€. VendorInterface string `json:"vendor_interface"` // Conductor group for a node. Case-insensitive string up to 255 characters, containing a-z, 0-9, _, -, and .. ConductorGroup string `json:"conductor_group"` // The node is protected from undeploying, rebuilding and deletion. Protected bool `json:"protected"` // Reason the node is marked as protected. ProtectedReason string `json:"protected_reason"` // A string or UUID of the tenant who owns the baremetal node. Owner string `json:"owner"` } // NodePage 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 ExtractNodes call. type NodePage struct { pagination.LinkedPageBase } // IsEmpty returns true if a page contains no Node results. func (r NodePage) IsEmpty() (bool, error) { s, err := ExtractNodes(r) return len(s) == 0, err } // NextPageURL uses the response's embedded link reference to navigate to the // next page of results. func (r NodePage) NextPageURL() (string, error) { var s struct { Links []gophercloud.Link `json:"nodes_links"` } err := r.ExtractInto(&s) if err != nil { return "", err } return gophercloud.ExtractNextURL(s.Links) } // ExtractNodes interprets the results of a single page from a List() call, // producing a slice of Node entities. func ExtractNodes(r pagination.Page) ([]Node, error) { var s []Node err := ExtractNodesInto(r, &s) return s, err } // GetResult is the response from a Get operation. Call its Extract // method to interpret it as a Node. type GetResult struct { nodeResult } // CreateResult is the response from a Create operation. type CreateResult struct { nodeResult } // UpdateResult is the response from an Update operation. Call its Extract // method to interpret it as a Node. type UpdateResult struct { nodeResult } // 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 } // ValidateResult is the response from a Validate operation. Call its Extract // method to interpret it as a NodeValidation struct. type ValidateResult struct { gophercloud.Result } // InjectNMIResult is the response from an InjectNMI operation. Call its ExtractErr // method to determine if the call succeeded or failed. type InjectNMIResult struct { gophercloud.ErrResult } // BootDeviceResult is the response from a GetBootDevice operation. Call its Extract // method to interpret it as a BootDeviceOpts struct. type BootDeviceResult struct { gophercloud.Result } // BootDeviceResult is the response from a GetBootDevice operation. Call its Extract // method to interpret it as a BootDeviceOpts struct. type SetBootDeviceResult struct { gophercloud.ErrResult } // SupportedBootDeviceResult is the response from a GetSupportedBootDevices operation. Call its Extract // method to interpret it as an array of supported boot device values. type SupportedBootDeviceResult struct { gophercloud.Result } // ChangePowerStateResult is the response from a ChangePowerState operation. Call its ExtractErr // method to determine if the call succeeded or failed. type ChangePowerStateResult struct { gophercloud.ErrResult } // Each element in the response will contain a “result†variable, which will have a value of “true†or “falseâ€, and // also potentially a reason. A value of nil indicates that the Node’s driver does not support that interface. type DriverValidation struct { Result bool `json:"result"` Reason string `json:"reason"` } // Ironic validates whether the Node’s driver has enough information to manage the Node. This polls each interface on // the driver, and returns the status of that interface as an DriverValidation struct. type NodeValidation struct { Boot DriverValidation `json:"boot"` Console DriverValidation `json:"console"` Deploy DriverValidation `json:"deploy"` Inspect DriverValidation `json:"inspect"` Management DriverValidation `json:"management"` Network DriverValidation `json:"network"` Power DriverValidation `json:"power"` RAID DriverValidation `json:"raid"` Rescue DriverValidation `json:"rescue"` Storage DriverValidation `json:"storage"` } // ChangeStateResult is the response from any state change operation. Call its ExtractErr // method to determine if the call succeeded or failed. type ChangeStateResult struct { gophercloud.ErrResult } golang-github-gophercloud-gophercloud-0.12.0/openstack/baremetal/v1/nodes/testing/000077500000000000000000000000001367513235700302155ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/baremetal/v1/nodes/testing/doc.go000066400000000000000000000000441367513235700313070ustar00rootroot00000000000000// nodes unit tests package testing golang-github-gophercloud-gophercloud-0.12.0/openstack/baremetal/v1/nodes/testing/fixtures.go000066400000000000000000000761331367513235700324270ustar00rootroot00000000000000package testing import ( "fmt" "net/http" "testing" "github.com/gophercloud/gophercloud/openstack/baremetal/v1/nodes" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) // NodeListBody contains the canned body of a nodes.List response, without detail. const NodeListBody = ` { "nodes": [ { "instance_uuid": null, "links": [ { "href": "http://ironic.example.com:6385/v1/nodes/d2630783-6ec8-4836-b556-ab427c4b581e", "rel": "self" }, { "href": "http://ironic.example.com:6385/nodes/d2630783-6ec8-4836-b556-ab427c4b581e", "rel": "bookmark" } ], "maintenance": false, "name": "foo", "power_state": null, "provision_state": "enroll" }, { "instance_uuid": null, "links": [ { "href": "http://ironic.example.com:6385/v1/nodes/08c84581-58f5-4ea2-a0c6-dd2e5d2b3662", "rel": "self" }, { "href": "http://ironic.example.com:6385/nodes/08c84581-58f5-4ea2-a0c6-dd2e5d2b3662", "rel": "bookmark" } ], "maintenance": false, "name": "bar", "power_state": null, "provision_state": "enroll" }, { "instance_uuid": null, "links": [ { "href": "http://ironic.example.com:6385/v1/nodes/c9afd385-5d89-4ecb-9e1c-68194da6b474", "rel": "self" }, { "href": "http://ironic.example.com:6385/nodes/c9afd385-5d89-4ecb-9e1c-68194da6b474", "rel": "bookmark" } ], "maintenance": false, "name": "baz", "power_state": null, "provision_state": "enroll" } ] } ` // NodeListDetailBody contains the canned body of a nodes.ListDetail response. const NodeListDetailBody = ` { "nodes": [ { "bios_interface": "no-bios", "boot_interface": "pxe", "chassis_uuid": null, "clean_step": {}, "conductor_group": "", "console_enabled": false, "console_interface": "no-console", "created_at": "2019-01-31T19:59:28+00:00", "deploy_interface": "iscsi", "deploy_step": {}, "driver": "ipmi", "driver_info": { "ipmi_port": "6230", "ipmi_username": "admin", "deploy_kernel": "http://172.22.0.1/images/tinyipa-stable-rocky.vmlinuz", "ipmi_address": "192.168.122.1", "deploy_ramdisk": "http://172.22.0.1/images/tinyipa-stable-rocky.gz", "ipmi_password": "admin" }, "driver_internal_info": {}, "extra": {}, "fault": null, "inspect_interface": "no-inspect", "inspection_finished_at": null, "inspection_started_at": null, "instance_info": {}, "instance_uuid": null, "last_error": null, "links": [ { "href": "http://ironic.example.com:6385/v1/nodes/d2630783-6ec8-4836-b556-ab427c4b581e", "rel": "self" }, { "href": "http://ironic.example.com:6385/nodes/d2630783-6ec8-4836-b556-ab427c4b581e", "rel": "bookmark" } ], "maintenance": false, "maintenance_reason": null, "management_interface": "ipmitool", "name": "foo", "network_interface": "flat", "portgroups": [ { "href": "http://ironic.example.com:6385/v1/nodes/d2630783-6ec8-4836-b556-ab427c4b581e/portgroups", "rel": "self" }, { "href": "http://ironic.example.com:6385/nodes/d2630783-6ec8-4836-b556-ab427c4b581e/portgroups", "rel": "bookmark" } ], "ports": [ { "href": "http://ironic.example.com:6385/v1/nodes/d2630783-6ec8-4836-b556-ab427c4b581e/ports", "rel": "self" }, { "href": "http://ironic.example.com:6385/nodes/d2630783-6ec8-4836-b556-ab427c4b581e/ports", "rel": "bookmark" } ], "power_interface": "ipmitool", "power_state": null, "properties": {}, "provision_state": "enroll", "provision_updated_at": null, "raid_config": {}, "raid_interface": "no-raid", "rescue_interface": "no-rescue", "reservation": null, "resource_class": null, "states": [ { "href": "http://ironic.example.com:6385/v1/nodes/d2630783-6ec8-4836-b556-ab427c4b581e/states", "rel": "self" }, { "href": "http://ironic.example.com:6385/nodes/d2630783-6ec8-4836-b556-ab427c4b581e/states", "rel": "bookmark" } ], "storage_interface": "noop", "target_power_state": null, "target_provision_state": null, "target_raid_config": {}, "traits": [], "updated_at": null, "uuid": "d2630783-6ec8-4836-b556-ab427c4b581e", "vendor_interface": "ipmitool", "volume": [ { "href": "http://ironic.example.com:6385/v1/nodes/d2630783-6ec8-4836-b556-ab427c4b581e/volume", "rel": "self" }, { "href": "http://ironic.example.com:6385/nodes/d2630783-6ec8-4836-b556-ab427c4b581e/volume", "rel": "bookmark" } ] }, { "bios_interface": "no-bios", "boot_interface": "pxe", "chassis_uuid": null, "clean_step": {}, "conductor_group": "", "console_enabled": false, "console_interface": "no-console", "created_at": "2019-01-31T19:59:29+00:00", "deploy_interface": "iscsi", "deploy_step": {}, "driver": "ipmi", "driver_info": {}, "driver_internal_info": {}, "extra": {}, "fault": null, "inspect_interface": "no-inspect", "inspection_finished_at": null, "inspection_started_at": null, "instance_info": {}, "instance_uuid": null, "last_error": null, "links": [ { "href": "http://ironic.example.com:6385/v1/nodes/08c84581-58f5-4ea2-a0c6-dd2e5d2b3662", "rel": "self" }, { "href": "http://ironic.example.com:6385/nodes/08c84581-58f5-4ea2-a0c6-dd2e5d2b3662", "rel": "bookmark" } ], "maintenance": false, "maintenance_reason": null, "management_interface": "ipmitool", "name": "bar", "network_interface": "flat", "portgroups": [ { "href": "http://ironic.example.com:6385/v1/nodes/08c84581-58f5-4ea2-a0c6-dd2e5d2b3662/portgroups", "rel": "self" }, { "href": "http://ironic.example.com:6385/nodes/08c84581-58f5-4ea2-a0c6-dd2e5d2b3662/portgroups", "rel": "bookmark" } ], "ports": [ { "href": "http://ironic.example.com:6385/v1/nodes/08c84581-58f5-4ea2-a0c6-dd2e5d2b3662/ports", "rel": "self" }, { "href": "http://ironic.example.com:6385/nodes/08c84581-58f5-4ea2-a0c6-dd2e5d2b3662/ports", "rel": "bookmark" } ], "power_interface": "ipmitool", "power_state": null, "properties": {}, "provision_state": "enroll", "provision_updated_at": null, "raid_config": {}, "raid_interface": "no-raid", "rescue_interface": "no-rescue", "reservation": null, "resource_class": null, "states": [ { "href": "http://ironic.example.com:6385/v1/nodes/08c84581-58f5-4ea2-a0c6-dd2e5d2b3662/states", "rel": "self" }, { "href": "http://ironic.example.com:6385/nodes/08c84581-58f5-4ea2-a0c6-dd2e5d2b3662/states", "rel": "bookmark" } ], "storage_interface": "noop", "target_power_state": null, "target_provision_state": null, "target_raid_config": {}, "traits": [], "updated_at": null, "uuid": "08c84581-58f5-4ea2-a0c6-dd2e5d2b3662", "vendor_interface": "ipmitool", "volume": [ { "href": "http://ironic.example.com:6385/v1/nodes/08c84581-58f5-4ea2-a0c6-dd2e5d2b3662/volume", "rel": "self" }, { "href": "http://ironic.example.com:6385/nodes/08c84581-58f5-4ea2-a0c6-dd2e5d2b3662/volume", "rel": "bookmark" } ] }, { "bios_interface": "no-bios", "boot_interface": "pxe", "chassis_uuid": null, "clean_step": {}, "conductor_group": "", "console_enabled": false, "console_interface": "no-console", "created_at": "2019-01-31T19:59:30+00:00", "deploy_interface": "iscsi", "deploy_step": {}, "driver": "ipmi", "driver_info": {}, "driver_internal_info": {}, "extra": {}, "fault": null, "inspect_interface": "no-inspect", "inspection_finished_at": null, "inspection_started_at": null, "instance_info": {}, "instance_uuid": null, "last_error": null, "links": [ { "href": "http://ironic.example.com:6385/v1/nodes/c9afd385-5d89-4ecb-9e1c-68194da6b474", "rel": "self" }, { "href": "http://ironic.example.com:6385/nodes/c9afd385-5d89-4ecb-9e1c-68194da6b474", "rel": "bookmark" } ], "maintenance": false, "maintenance_reason": null, "management_interface": "ipmitool", "name": "baz", "network_interface": "flat", "portgroups": [ { "href": "http://ironic.example.com:6385/v1/nodes/c9afd385-5d89-4ecb-9e1c-68194da6b474/portgroups", "rel": "self" }, { "href": "http://ironic.example.com:6385/nodes/c9afd385-5d89-4ecb-9e1c-68194da6b474/portgroups", "rel": "bookmark" } ], "ports": [ { "href": "http://ironic.example.com:6385/v1/nodes/c9afd385-5d89-4ecb-9e1c-68194da6b474/ports", "rel": "self" }, { "href": "http://ironic.example.com:6385/nodes/c9afd385-5d89-4ecb-9e1c-68194da6b474/ports", "rel": "bookmark" } ], "power_interface": "ipmitool", "power_state": null, "properties": {}, "provision_state": "enroll", "provision_updated_at": null, "raid_config": {}, "raid_interface": "no-raid", "rescue_interface": "no-rescue", "reservation": null, "resource_class": null, "states": [ { "href": "http://ironic.example.com:6385/v1/nodes/c9afd385-5d89-4ecb-9e1c-68194da6b474/states", "rel": "self" }, { "href": "http://ironic.example.com:6385/nodes/c9afd385-5d89-4ecb-9e1c-68194da6b474/states", "rel": "bookmark" } ], "storage_interface": "noop", "target_power_state": null, "target_provision_state": null, "target_raid_config": {}, "traits": [], "updated_at": null, "uuid": "c9afd385-5d89-4ecb-9e1c-68194da6b474", "vendor_interface": "ipmitool", "volume": [ { "href": "http://ironic.example.com:6385/v1/nodes/c9afd385-5d89-4ecb-9e1c-68194da6b474/volume", "rel": "self" }, { "href": "http://ironic.example.com:6385/nodes/c9afd385-5d89-4ecb-9e1c-68194da6b474/volume", "rel": "bookmark" } ] } ] } ` // SingleNodeBody is the canned body of a Get request on an existing node. const SingleNodeBody = ` { "bios_interface": "no-bios", "boot_interface": "pxe", "chassis_uuid": null, "clean_step": {}, "conductor_group": "", "console_enabled": false, "console_interface": "no-console", "created_at": "2019-01-31T19:59:28+00:00", "deploy_interface": "iscsi", "deploy_step": {}, "driver": "ipmi", "driver_info": { "ipmi_port": "6230", "ipmi_username": "admin", "deploy_kernel": "http://172.22.0.1/images/tinyipa-stable-rocky.vmlinuz", "ipmi_address": "192.168.122.1", "deploy_ramdisk": "http://172.22.0.1/images/tinyipa-stable-rocky.gz", "ipmi_password": "admin" }, "driver_internal_info": {}, "extra": {}, "fault": null, "inspect_interface": "no-inspect", "inspection_finished_at": null, "inspection_started_at": null, "instance_info": {}, "instance_uuid": null, "last_error": null, "links": [ { "href": "http://ironic.example.com:6385/v1/nodes/d2630783-6ec8-4836-b556-ab427c4b581e", "rel": "self" }, { "href": "http://ironic.example.com:6385/nodes/d2630783-6ec8-4836-b556-ab427c4b581e", "rel": "bookmark" } ], "maintenance": false, "maintenance_reason": null, "management_interface": "ipmitool", "name": "foo", "network_interface": "flat", "portgroups": [ { "href": "http://ironic.example.com:6385/v1/nodes/d2630783-6ec8-4836-b556-ab427c4b581e/portgroups", "rel": "self" }, { "href": "http://ironic.example.com:6385/nodes/d2630783-6ec8-4836-b556-ab427c4b581e/portgroups", "rel": "bookmark" } ], "ports": [ { "href": "http://ironic.example.com:6385/v1/nodes/d2630783-6ec8-4836-b556-ab427c4b581e/ports", "rel": "self" }, { "href": "http://ironic.example.com:6385/nodes/d2630783-6ec8-4836-b556-ab427c4b581e/ports", "rel": "bookmark" } ], "power_interface": "ipmitool", "power_state": null, "properties": {}, "provision_state": "enroll", "provision_updated_at": null, "raid_config": {}, "raid_interface": "no-raid", "rescue_interface": "no-rescue", "reservation": null, "resource_class": null, "states": [ { "href": "http://ironic.example.com:6385/v1/nodes/d2630783-6ec8-4836-b556-ab427c4b581e/states", "rel": "self" }, { "href": "http://ironic.example.com:6385/nodes/d2630783-6ec8-4836-b556-ab427c4b581e/states", "rel": "bookmark" } ], "storage_interface": "noop", "target_power_state": null, "target_provision_state": null, "target_raid_config": {}, "traits": [], "updated_at": null, "uuid": "d2630783-6ec8-4836-b556-ab427c4b581e", "vendor_interface": "ipmitool", "volume": [ { "href": "http://ironic.example.com:6385/v1/nodes/d2630783-6ec8-4836-b556-ab427c4b581e/volume", "rel": "self" }, { "href": "http://ironic.example.com:6385/nodes/d2630783-6ec8-4836-b556-ab427c4b581e/volume", "rel": "bookmark" } ] } ` const NodeValidationBody = ` { "bios": { "reason": "Driver ipmi does not support bios (disabled or not implemented).", "result": false }, "boot": { "reason": "Cannot validate image information for node a62b8495-52e2-407b-b3cb-62775d04c2b8 because one or more parameters are missing from its instance_info and insufficent information is present to boot from a remote volume. Missing are: ['ramdisk', 'kernel', 'image_source']", "result": false }, "console": { "reason": "Driver ipmi does not support console (disabled or not implemented).", "result": false }, "deploy": { "reason": "Cannot validate image information for node a62b8495-52e2-407b-b3cb-62775d04c2b8 because one or more parameters are missing from its instance_info and insufficent information is present to boot from a remote volume. Missing are: ['ramdisk', 'kernel', 'image_source']", "result": false }, "inspect": { "reason": "Driver ipmi does not support inspect (disabled or not implemented).", "result": false }, "management": { "result": true }, "network": { "result": true }, "power": { "result": true }, "raid": { "reason": "Driver ipmi does not support raid (disabled or not implemented).", "result": false }, "rescue": { "reason": "Driver ipmi does not support rescue (disabled or not implemented).", "result": false }, "storage": { "result": true } } ` const NodeBootDeviceBody = ` { "boot_device":"pxe", "persistent":false } ` const NodeSupportedBootDeviceBody = ` { "supported_boot_devices": [ "pxe", "disk" ] } ` const NodeProvisionStateActiveBody = ` { "target": "active", "configdrive": "http://127.0.0.1/images/test-node-config-drive.iso.gz" } ` const NodeProvisionStateCleanBody = ` { "target": "clean", "clean_steps": [ { "interface": "deploy", "step": "upgrade_firmware", "args": { "force": "True" } } ] } ` const NodeProvisionStateConfigDriveBody = ` { "target": "active", "configdrive": { "user_data": { "ignition": { "version": "2.2.0" }, "systemd": { "units": [ { "enabled": true, "name": "example.service" } ] } } } } ` var ( NodeFoo = nodes.Node{ UUID: "d2630783-6ec8-4836-b556-ab427c4b581e", Name: "foo", PowerState: "", TargetPowerState: "", ProvisionState: "enroll", TargetProvisionState: "", Maintenance: false, MaintenanceReason: "", Fault: "", LastError: "", Reservation: "", Driver: "ipmi", DriverInfo: map[string]interface{}{ "ipmi_port": "6230", "ipmi_username": "admin", "deploy_kernel": "http://172.22.0.1/images/tinyipa-stable-rocky.vmlinuz", "ipmi_address": "192.168.122.1", "deploy_ramdisk": "http://172.22.0.1/images/tinyipa-stable-rocky.gz", "ipmi_password": "admin", }, DriverInternalInfo: map[string]interface{}{}, Properties: map[string]interface{}{}, InstanceInfo: map[string]interface{}{}, InstanceUUID: "", ChassisUUID: "", Extra: map[string]interface{}{}, ConsoleEnabled: false, RAIDConfig: map[string]interface{}{}, TargetRAIDConfig: map[string]interface{}{}, CleanStep: map[string]interface{}{}, DeployStep: map[string]interface{}{}, ResourceClass: "", BootInterface: "pxe", ConsoleInterface: "no-console", DeployInterface: "iscsi", InspectInterface: "no-inspect", ManagementInterface: "ipmitool", NetworkInterface: "flat", PowerInterface: "ipmitool", RAIDInterface: "no-raid", RescueInterface: "no-rescue", StorageInterface: "noop", Traits: []string{}, VendorInterface: "ipmitool", ConductorGroup: "", Protected: false, ProtectedReason: "", } NodeFooValidation = nodes.NodeValidation{ Boot: nodes.DriverValidation{ Result: false, Reason: "Cannot validate image information for node a62b8495-52e2-407b-b3cb-62775d04c2b8 because one or more parameters are missing from its instance_info and insufficent information is present to boot from a remote volume. Missing are: ['ramdisk', 'kernel', 'image_source']", }, Console: nodes.DriverValidation{ Result: false, Reason: "Driver ipmi does not support console (disabled or not implemented).", }, Deploy: nodes.DriverValidation{ Result: false, Reason: "Cannot validate image information for node a62b8495-52e2-407b-b3cb-62775d04c2b8 because one or more parameters are missing from its instance_info and insufficent information is present to boot from a remote volume. Missing are: ['ramdisk', 'kernel', 'image_source']", }, Inspect: nodes.DriverValidation{ Result: false, Reason: "Driver ipmi does not support inspect (disabled or not implemented).", }, Management: nodes.DriverValidation{ Result: true, }, Network: nodes.DriverValidation{ Result: true, }, Power: nodes.DriverValidation{ Result: true, }, RAID: nodes.DriverValidation{ Result: false, Reason: "Driver ipmi does not support raid (disabled or not implemented).", }, Rescue: nodes.DriverValidation{ Result: false, Reason: "Driver ipmi does not support rescue (disabled or not implemented).", }, Storage: nodes.DriverValidation{ Result: true, }, } NodeBootDevice = nodes.BootDeviceOpts{ BootDevice: "pxe", Persistent: false, } NodeSupportedBootDevice = []string{ "pxe", "disk", } NodeBar = nodes.Node{ UUID: "08c84581-58f5-4ea2-a0c6-dd2e5d2b3662", Name: "bar", PowerState: "", TargetPowerState: "", ProvisionState: "enroll", TargetProvisionState: "", Maintenance: false, MaintenanceReason: "", Fault: "", LastError: "", Reservation: "", Driver: "ipmi", DriverInfo: map[string]interface{}{}, DriverInternalInfo: map[string]interface{}{}, Properties: map[string]interface{}{}, InstanceInfo: map[string]interface{}{}, InstanceUUID: "", ChassisUUID: "", Extra: map[string]interface{}{}, ConsoleEnabled: false, RAIDConfig: map[string]interface{}{}, TargetRAIDConfig: map[string]interface{}{}, CleanStep: map[string]interface{}{}, DeployStep: map[string]interface{}{}, ResourceClass: "", BootInterface: "pxe", ConsoleInterface: "no-console", DeployInterface: "iscsi", InspectInterface: "no-inspect", ManagementInterface: "ipmitool", NetworkInterface: "flat", PowerInterface: "ipmitool", RAIDInterface: "no-raid", RescueInterface: "no-rescue", StorageInterface: "noop", Traits: []string{}, VendorInterface: "ipmitool", ConductorGroup: "", Protected: false, ProtectedReason: "", } NodeBaz = nodes.Node{ UUID: "c9afd385-5d89-4ecb-9e1c-68194da6b474", Name: "baz", PowerState: "", TargetPowerState: "", ProvisionState: "enroll", TargetProvisionState: "", Maintenance: false, MaintenanceReason: "", Fault: "", LastError: "", Reservation: "", Driver: "ipmi", DriverInfo: map[string]interface{}{}, DriverInternalInfo: map[string]interface{}{}, Properties: map[string]interface{}{}, InstanceInfo: map[string]interface{}{}, InstanceUUID: "", ChassisUUID: "", Extra: map[string]interface{}{}, ConsoleEnabled: false, RAIDConfig: map[string]interface{}{}, TargetRAIDConfig: map[string]interface{}{}, CleanStep: map[string]interface{}{}, DeployStep: map[string]interface{}{}, ResourceClass: "", BootInterface: "pxe", ConsoleInterface: "no-console", DeployInterface: "iscsi", InspectInterface: "no-inspect", ManagementInterface: "ipmitool", NetworkInterface: "flat", PowerInterface: "ipmitool", RAIDInterface: "no-raid", RescueInterface: "no-rescue", StorageInterface: "noop", Traits: []string{}, VendorInterface: "ipmitool", ConductorGroup: "", Protected: false, ProtectedReason: "", } ConfigDriveMap = nodes.ConfigDrive{ UserData: map[string]interface{}{ "ignition": map[string]string{ "version": "2.2.0", }, "systemd": map[string]interface{}{ "units": []map[string]interface{}{{ "name": "example.service", "enabled": true, }, }, }, }, } ) // HandleNodeListSuccessfully sets up the test server to respond to a server List request. func HandleNodeListSuccessfully(t *testing.T) { th.Mux.HandleFunc("/nodes", 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, NodeListBody) case "9e5476bd-a4ec-4653-93d6-72c93aa682ba": fmt.Fprintf(w, `{ "servers": [] }`) default: t.Fatalf("/nodes invoked with unexpected marker=[%s]", marker) } }) } // HandleNodeListSuccessfully sets up the test server to respond to a server List request. func HandleNodeListDetailSuccessfully(t *testing.T) { th.Mux.HandleFunc("/nodes/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() fmt.Fprintf(w, NodeListDetailBody) }) } // HandleServerCreationSuccessfully sets up the test server to respond to a server creation request // with a given response. func HandleNodeCreationSuccessfully(t *testing.T, response string) { th.Mux.HandleFunc("/nodes", 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, `{ "boot_interface": "pxe", "driver": "ipmi", "driver_info": { "deploy_kernel": "http://172.22.0.1/images/tinyipa-stable-rocky.vmlinuz", "deploy_ramdisk": "http://172.22.0.1/images/tinyipa-stable-rocky.gz", "ipmi_address": "192.168.122.1", "ipmi_password": "admin", "ipmi_port": "6230", "ipmi_username": "admin" }, "name": "foo" }`) w.WriteHeader(http.StatusAccepted) w.Header().Add("Content-Type", "application/json") fmt.Fprintf(w, response) }) } // HandleNodeDeletionSuccessfully sets up the test server to respond to a server deletion request. func HandleNodeDeletionSuccessfully(t *testing.T) { th.Mux.HandleFunc("/nodes/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) }) } func HandleNodeGetSuccessfully(t *testing.T) { th.Mux.HandleFunc("/nodes/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, SingleNodeBody) }) } func HandleNodeUpdateSuccessfully(t *testing.T, response string) { th.Mux.HandleFunc("/nodes/1234asdf", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "PATCH") 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, `[{"op": "replace", "path": "/properties", "value": {"root_gb": 25}}]`) fmt.Fprintf(w, response) }) } func HandleNodeValidateSuccessfully(t *testing.T) { th.Mux.HandleFunc("/nodes/1234asdf/validate", 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, NodeValidationBody) }) } // HandleInjectNMISuccessfully sets up the test server to respond to a node InjectNMI request func HandleInjectNMISuccessfully(t *testing.T) { th.Mux.HandleFunc("/nodes/1234asdf/management/inject_nmi", 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, "{}") w.WriteHeader(http.StatusNoContent) }) } // HandleSetBootDeviceSuccessfully sets up the test server to respond to a set boot device request for a node func HandleSetBootDeviceSuccessfully(t *testing.T) { th.Mux.HandleFunc("/nodes/1234asdf/management/boot_device", 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, NodeBootDeviceBody) w.WriteHeader(http.StatusNoContent) }) } // HandleGetBootDeviceSuccessfully sets up the test server to respond to a get boot device request for a node func HandleGetBootDeviceSuccessfully(t *testing.T) { th.Mux.HandleFunc("/nodes/1234asdf/management/boot_device", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.WriteHeader(http.StatusOK) fmt.Fprintf(w, NodeBootDeviceBody) }) } // HandleGetBootDeviceSuccessfully sets up the test server to respond to a get boot device request for a node func HandleGetSupportedBootDeviceSuccessfully(t *testing.T) { th.Mux.HandleFunc("/nodes/1234asdf/management/boot_device/supported", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.WriteHeader(http.StatusOK) fmt.Fprintf(w, NodeSupportedBootDeviceBody) }) } func HandleNodeChangeProvisionStateActive(t *testing.T) { th.Mux.HandleFunc("/nodes/1234asdf/states/provision", 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, NodeProvisionStateActiveBody) w.WriteHeader(http.StatusAccepted) }) } func HandleNodeChangeProvisionStateClean(t *testing.T) { th.Mux.HandleFunc("/nodes/1234asdf/states/provision", 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, NodeProvisionStateCleanBody) w.WriteHeader(http.StatusAccepted) }) } func HandleNodeChangeProvisionStateCleanWithConflict(t *testing.T) { th.Mux.HandleFunc("/nodes/1234asdf/states/provision", 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, NodeProvisionStateCleanBody) w.WriteHeader(http.StatusConflict) }) } func HandleNodeChangeProvisionStateConfigDrive(t *testing.T) { th.Mux.HandleFunc("/nodes/1234asdf/states/provision", 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, NodeProvisionStateConfigDriveBody) w.WriteHeader(http.StatusAccepted) }) } // HandleChangePowerStateSuccessfully sets up the test server to respond to a change power state request for a node func HandleChangePowerStateSuccessfully(t *testing.T) { th.Mux.HandleFunc("/nodes/1234asdf/states/power", 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, `{ "target": "power on", "timeout": 100 }`) w.WriteHeader(http.StatusAccepted) }) } // HandleChangePowerStateWithConflict sets up the test server to respond to a change power state request for a node with a 409 error func HandleChangePowerStateWithConflict(t *testing.T) { th.Mux.HandleFunc("/nodes/1234asdf/states/power", 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, `{ "target": "power on", "timeout": 100 }`) w.WriteHeader(http.StatusConflict) }) } func HandleSetRAIDConfig(t *testing.T) { th.Mux.HandleFunc("/nodes/1234asdf/states/raid", 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, ` { "logical_disks" : [ { "size_gb" : 100, "is_root_volume" : true, "raid_level" : "1" } ] } `) w.WriteHeader(http.StatusNoContent) }) } func HandleSetRAIDConfigMaxSize(t *testing.T) { th.Mux.HandleFunc("/nodes/1234asdf/states/raid", 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, ` { "logical_disks" : [ { "size_gb" : "MAX", "is_root_volume" : true, "raid_level" : "1" } ] } `) w.WriteHeader(http.StatusNoContent) }) } golang-github-gophercloud-gophercloud-0.12.0/openstack/baremetal/v1/nodes/testing/requests_test.go000066400000000000000000000276261367513235700334730ustar00rootroot00000000000000package testing import ( "testing" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack/baremetal/v1/nodes" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) func TestListDetailNodes(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleNodeListDetailSuccessfully(t) pages := 0 err := nodes.ListDetail(client.ServiceClient(), nodes.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { pages++ actual, err := nodes.ExtractNodes(page) if err != nil { return false, err } if len(actual) != 3 { t.Fatalf("Expected 3 nodes, got %d", len(actual)) } th.CheckDeepEquals(t, NodeFoo, actual[0]) th.CheckDeepEquals(t, NodeBar, actual[1]) th.CheckDeepEquals(t, NodeBaz, actual[2]) return true, nil }) th.AssertNoErr(t, err) if pages != 1 { t.Errorf("Expected 1 page, saw %d", pages) } } func TestListNodes(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleNodeListSuccessfully(t) pages := 0 err := nodes.List(client.ServiceClient(), nodes.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { pages++ actual, err := nodes.ExtractNodes(page) if err != nil { return false, err } if len(actual) != 3 { t.Fatalf("Expected 3 nodes, got %d", len(actual)) } th.AssertEquals(t, "foo", actual[0].Name) th.AssertEquals(t, "bar", actual[1].Name) th.AssertEquals(t, "baz", actual[2].Name) return true, nil }) th.AssertNoErr(t, err) if pages != 1 { t.Errorf("Expected 1 page, saw %d", pages) } } func TestListOpts(t *testing.T) { // Detail cannot take Fields opts := nodes.ListOpts{ Fields: []string{"name", "uuid"}, } _, err := opts.ToNodeListDetailQuery() th.AssertEquals(t, err.Error(), "fields is not a valid option when getting a detailed listing of nodes") // Regular ListOpts can query, err := opts.ToNodeListQuery() th.AssertEquals(t, query, "?fields=name&fields=uuid") th.AssertNoErr(t, err) } func TestCreateNode(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleNodeCreationSuccessfully(t, SingleNodeBody) actual, err := nodes.Create(client.ServiceClient(), nodes.CreateOpts{ Name: "foo", Driver: "ipmi", BootInterface: "pxe", DriverInfo: map[string]interface{}{ "ipmi_port": "6230", "ipmi_username": "admin", "deploy_kernel": "http://172.22.0.1/images/tinyipa-stable-rocky.vmlinuz", "ipmi_address": "192.168.122.1", "deploy_ramdisk": "http://172.22.0.1/images/tinyipa-stable-rocky.gz", "ipmi_password": "admin", }, }).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, NodeFoo, *actual) } func TestDeleteNode(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleNodeDeletionSuccessfully(t) res := nodes.Delete(client.ServiceClient(), "asdfasdfasdf") th.AssertNoErr(t, res.Err) } func TestGetNode(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleNodeGetSuccessfully(t) c := client.ServiceClient() actual, err := nodes.Get(c, "1234asdf").Extract() if err != nil { t.Fatalf("Unexpected Get error: %v", err) } th.CheckDeepEquals(t, NodeFoo, *actual) } func TestUpdateNode(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleNodeUpdateSuccessfully(t, SingleNodeBody) c := client.ServiceClient() actual, err := nodes.Update(c, "1234asdf", nodes.UpdateOpts{ nodes.UpdateOperation{ Op: nodes.ReplaceOp, Path: "/properties", Value: map[string]interface{}{ "root_gb": 25, }, }, }).Extract() if err != nil { t.Fatalf("Unexpected Update error: %v", err) } th.CheckDeepEquals(t, NodeFoo, *actual) } func TestUpdateRequiredOp(t *testing.T) { c := client.ServiceClient() _, err := nodes.Update(c, "1234asdf", nodes.UpdateOpts{ nodes.UpdateOperation{ Path: "/driver", Value: "new-driver", }, }).Extract() if _, ok := err.(gophercloud.ErrMissingInput); !ok { t.Fatal("ErrMissingInput was expected to occur") } } func TestUpdateRequiredPath(t *testing.T) { c := client.ServiceClient() _, err := nodes.Update(c, "1234asdf", nodes.UpdateOpts{ nodes.UpdateOperation{ Op: nodes.ReplaceOp, Value: "new-driver", }, }).Extract() if _, ok := err.(gophercloud.ErrMissingInput); !ok { t.Fatal("ErrMissingInput was expected to occur") } } func TestValidateNode(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleNodeValidateSuccessfully(t) c := client.ServiceClient() actual, err := nodes.Validate(c, "1234asdf").Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, NodeFooValidation, *actual) } func TestInjectNMI(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleInjectNMISuccessfully(t) c := client.ServiceClient() err := nodes.InjectNMI(c, "1234asdf").ExtractErr() th.AssertNoErr(t, err) } func TestSetBootDevice(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleSetBootDeviceSuccessfully(t) c := client.ServiceClient() err := nodes.SetBootDevice(c, "1234asdf", nodes.BootDeviceOpts{ BootDevice: "pxe", Persistent: false, }).ExtractErr() th.AssertNoErr(t, err) } func TestGetBootDevice(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleGetBootDeviceSuccessfully(t) c := client.ServiceClient() bootDevice, err := nodes.GetBootDevice(c, "1234asdf").Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, NodeBootDevice, *bootDevice) } func TestGetSupportedBootDevices(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleGetSupportedBootDeviceSuccessfully(t) c := client.ServiceClient() bootDevices, err := nodes.GetSupportedBootDevices(c, "1234asdf").Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, NodeSupportedBootDevice, bootDevices) } func TestNodeChangeProvisionStateActive(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleNodeChangeProvisionStateActive(t) c := client.ServiceClient() err := nodes.ChangeProvisionState(c, "1234asdf", nodes.ProvisionStateOpts{ Target: nodes.TargetActive, ConfigDrive: "http://127.0.0.1/images/test-node-config-drive.iso.gz", }).ExtractErr() th.AssertNoErr(t, err) } func TestHandleNodeChangeProvisionStateConfigDrive(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleNodeChangeProvisionStateConfigDrive(t) c := client.ServiceClient() err := nodes.ChangeProvisionState(c, "1234asdf", nodes.ProvisionStateOpts{ Target: nodes.TargetActive, ConfigDrive: ConfigDriveMap, }).ExtractErr() th.AssertNoErr(t, err) } func TestNodeChangeProvisionStateClean(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleNodeChangeProvisionStateClean(t) c := client.ServiceClient() err := nodes.ChangeProvisionState(c, "1234asdf", nodes.ProvisionStateOpts{ Target: nodes.TargetClean, CleanSteps: []nodes.CleanStep{ { Interface: "deploy", Step: "upgrade_firmware", Args: map[string]interface{}{ "force": "True", }, }, }, }).ExtractErr() th.AssertNoErr(t, err) } func TestNodeChangeProvisionStateCleanWithConflict(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleNodeChangeProvisionStateCleanWithConflict(t) c := client.ServiceClient() err := nodes.ChangeProvisionState(c, "1234asdf", nodes.ProvisionStateOpts{ Target: nodes.TargetClean, CleanSteps: []nodes.CleanStep{ { Interface: "deploy", Step: "upgrade_firmware", Args: map[string]interface{}{ "force": "True", }, }, }, }).ExtractErr() if _, ok := err.(gophercloud.ErrDefault409); !ok { t.Fatal("ErrDefault409 was expected to occur") } } func TestCleanStepRequiresInterface(t *testing.T) { c := client.ServiceClient() err := nodes.ChangeProvisionState(c, "1234asdf", nodes.ProvisionStateOpts{ Target: nodes.TargetClean, CleanSteps: []nodes.CleanStep{ { Step: "upgrade_firmware", Args: map[string]interface{}{ "force": "True", }, }, }, }).ExtractErr() if _, ok := err.(gophercloud.ErrMissingInput); !ok { t.Fatal("ErrMissingInput was expected to occur") } } func TestCleanStepRequiresStep(t *testing.T) { c := client.ServiceClient() err := nodes.ChangeProvisionState(c, "1234asdf", nodes.ProvisionStateOpts{ Target: nodes.TargetClean, CleanSteps: []nodes.CleanStep{ { Interface: "deploy", Args: map[string]interface{}{ "force": "True", }, }, }, }).ExtractErr() if _, ok := err.(gophercloud.ErrMissingInput); !ok { t.Fatal("ErrMissingInput was expected to occur") } } func TestChangePowerState(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleChangePowerStateSuccessfully(t) opts := nodes.PowerStateOpts{ Target: nodes.PowerOn, Timeout: 100, } c := client.ServiceClient() err := nodes.ChangePowerState(c, "1234asdf", opts).ExtractErr() th.AssertNoErr(t, err) } func TestChangePowerStateWithConflict(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleChangePowerStateWithConflict(t) opts := nodes.PowerStateOpts{ Target: nodes.PowerOn, Timeout: 100, } c := client.ServiceClient() err := nodes.ChangePowerState(c, "1234asdf", opts).ExtractErr() if _, ok := err.(gophercloud.ErrDefault409); !ok { t.Fatal("ErrDefault409 was expected to occur") } } func TestSetRAIDConfig(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleSetRAIDConfig(t) sizeGB := 100 isRootVolume := true config := nodes.RAIDConfigOpts{ LogicalDisks: []nodes.LogicalDisk{ { SizeGB: &sizeGB, IsRootVolume: &isRootVolume, RAIDLevel: nodes.RAID1, }, }, } c := client.ServiceClient() err := nodes.SetRAIDConfig(c, "1234asdf", config).ExtractErr() th.AssertNoErr(t, err) } // Without specifying a size, we need to send a string: "MAX" func TestSetRAIDConfigMaxSize(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleSetRAIDConfigMaxSize(t) isRootVolume := true config := nodes.RAIDConfigOpts{ LogicalDisks: []nodes.LogicalDisk{ { IsRootVolume: &isRootVolume, RAIDLevel: nodes.RAID1, }, }, } c := client.ServiceClient() err := nodes.SetRAIDConfig(c, "1234asdf", config).ExtractErr() th.AssertNoErr(t, err) } func TestToRAIDConfigMap(t *testing.T) { cases := []struct { name string opts nodes.RAIDConfigOpts expected map[string]interface{} }{ { name: "LogicalDisks is empty", opts: nodes.RAIDConfigOpts{}, expected: map[string]interface{}{ "logical_disks": nil, }, }, { name: "LogicalDisks is nil", opts: nodes.RAIDConfigOpts{ LogicalDisks: nil, }, expected: map[string]interface{}{ "logical_disks": nil, }, }, { name: "PhysicalDisks is []string", opts: nodes.RAIDConfigOpts{ LogicalDisks: []nodes.LogicalDisk{ { RAIDLevel: "0", VolumeName: "root", PhysicalDisks: []interface{}{"6I:1:5", "6I:1:6", "6I:1:7"}, }, }, }, expected: map[string]interface{}{ "logical_disks": []map[string]interface{}{ { "raid_level": "0", "size_gb": "MAX", "volume_name": "root", "physical_disks": []interface{}{"6I:1:5", "6I:1:6", "6I:1:7"}, }, }, }, }, { name: "PhysicalDisks is []map[string]string", opts: nodes.RAIDConfigOpts{ LogicalDisks: []nodes.LogicalDisk{ { RAIDLevel: "0", VolumeName: "root", Controller: "software", PhysicalDisks: []interface{}{ map[string]string{ "size": "> 100", }, map[string]string{ "size": "> 100", }, }, }, }, }, expected: map[string]interface{}{ "logical_disks": []map[string]interface{}{ { "raid_level": "0", "size_gb": "MAX", "volume_name": "root", "controller": "software", "physical_disks": []interface{}{ map[string]interface{}{ "size": "> 100", }, map[string]interface{}{ "size": "> 100", }, }, }, }, }, }, } for _, c := range cases { t.Run(c.name, func(t *testing.T) { got, _ := c.opts.ToRAIDConfigMap() th.CheckDeepEquals(t, c.expected, got) }) } } golang-github-gophercloud-gophercloud-0.12.0/openstack/baremetal/v1/nodes/urls.go000066400000000000000000000033631367513235700300610ustar00rootroot00000000000000package nodes import "github.com/gophercloud/gophercloud" func createURL(client *gophercloud.ServiceClient) string { return client.ServiceURL("nodes") } func listURL(client *gophercloud.ServiceClient) string { return createURL(client) } func listDetailURL(client *gophercloud.ServiceClient) string { return client.ServiceURL("nodes", "detail") } func deleteURL(client *gophercloud.ServiceClient, id string) string { return client.ServiceURL("nodes", 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 validateURL(client *gophercloud.ServiceClient, id string) string { return client.ServiceURL("nodes", id, "validate") } func injectNMIURL(client *gophercloud.ServiceClient, id string) string { return client.ServiceURL("nodes", id, "management", "inject_nmi") } func bootDeviceURL(client *gophercloud.ServiceClient, id string) string { return client.ServiceURL("nodes", id, "management", "boot_device") } func supportedBootDeviceURL(client *gophercloud.ServiceClient, id string) string { return client.ServiceURL("nodes", id, "management", "boot_device", "supported") } func statesResourceURL(client *gophercloud.ServiceClient, id string, state string) string { return client.ServiceURL("nodes", id, "states", state) } func powerStateURL(client *gophercloud.ServiceClient, id string) string { return statesResourceURL(client, id, "power") } func provisionStateURL(client *gophercloud.ServiceClient, id string) string { return statesResourceURL(client, id, "provision") } func raidConfigURL(client *gophercloud.ServiceClient, id string) string { return statesResourceURL(client, id, "raid") } golang-github-gophercloud-gophercloud-0.12.0/openstack/baremetal/v1/ports/000077500000000000000000000000001367513235700265775ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/baremetal/v1/ports/doc.go000066400000000000000000000033161367513235700276760ustar00rootroot00000000000000/* Package ports contains the functionality to Listing, Searching, Creating, Updating, and Deleting of bare metal Port resources API reference: https://developer.openstack.org/api-ref/baremetal/#ports-ports Example to List Ports with Detail ports.ListDetail(client, nil).EachPage(func(page pagination.Page) (bool, error) { portList, err := ports.ExtractPorts(page) if err != nil { return false, err } for _, n := range portList { // Do something } return true, nil }) Example to List Ports listOpts := ports.ListOpts{ Limit: 10, } ports.List(client, listOpts).EachPage(func(page pagination.Page) (bool, error) { portList, err := ports.ExtractPorts(page) if err != nil { return false, err } for _, n := range portList { // Do something } return true, nil }) Example to Create a Port createOpts := ports.CreateOpts{ NodeUUID: "e8920409-e07e-41bb-8cc1-72acb103e2dd", Address: "00:1B:63:84:45:E6", PhysicalNetwork: "my-network", } createPort, err := ports.Create(client, createOpts).Extract() if err != nil { panic(err) } Example to Get a Port showPort, err := ports.Get(client, "c9afd385-5d89-4ecb-9e1c-68194da6b474").Extract() if err != nil { panic(err) } Example to Update a Port updateOpts := ports.UpdateOpts{ ports.UpdateOperation{ Op: ReplaceOp, Path: "/address", Value: "22:22:22:22:22:22", }, } updatePort, err := ports.Update(client, "c9afd385-5d89-4ecb-9e1c-68194da6b474", updateOpts).Extract() if err != nil { panic(err) } Example to Delete a Port err = ports.Delete(client, "c9afd385-5d89-4ecb-9e1c-68194da6b474").ExtractErr() if err != nil { panic(err) } */ package ports golang-github-gophercloud-gophercloud-0.12.0/openstack/baremetal/v1/ports/requests.go000066400000000000000000000153231367513235700310050ustar00rootroot00000000000000package ports 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 { ToPortListQuery() (string, error) ToPortListDetailQuery() (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 node attributes you want to see returned. Marker and Limit are used // for pagination. type ListOpts struct { // Filter the list by the name or uuid of the Node Node string `q:"node"` // Filter the list by the Node uuid NodeUUID string `q:"node_uuid"` // Filter the list with the specified Portgroup (name or UUID) PortGroup string `q:"portgroup"` // Filter the list with the specified physical hardware address, typically MAC Address string `q:"address"` // One or more fields to be returned in the response. Fields []string `q:"fields"` // Requests a page size of items. Limit int `q:"limit"` // The ID of the last-seen item Marker string `q:"marker"` // Sorts the response by the requested sort direction. // Valid value is asc (ascending) or desc (descending). Default is asc. SortDir string `q:"sort_dir"` // Sorts the response by the this attribute value. Default is id. SortKey string `q:"sort_key"` } // ToPortListQuery formats a ListOpts into a query string. func (opts ListOpts) ToPortListQuery() (string, error) { q, err := gophercloud.BuildQueryString(opts) return q.String(), err } // List makes a request against the API to list ports accessible to you. func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { url := listURL(client) if opts != nil { query, err := opts.ToPortListQuery() if err != nil { return pagination.Pager{Err: err} } url += query } return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { return PortPage{pagination.LinkedPageBase{PageResult: r}} }) } // ToPortListDetailQuery formats a ListOpts into a query string for the list details API. func (opts ListOpts) ToPortListDetailQuery() (string, error) { // Detail endpoint can't filter by Fields if len(opts.Fields) > 0 { return "", fmt.Errorf("fields is not a valid option when getting a detailed listing of ports") } q, err := gophercloud.BuildQueryString(opts) return q.String(), err } // ListDetail - Return a list ports with complete details. // Some filtering is possible by passing in flags in "ListOpts", // but you cannot limit by the fields returned. func ListDetail(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { url := listDetailURL(client) if opts != nil { query, err := opts.ToPortListDetailQuery() if err != nil { return pagination.Pager{Err: err} } url += query } return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { return PortPage{pagination.LinkedPageBase{PageResult: r}} }) } // Get - requests the details off a port, by ID. func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { resp, err := client.Get(getURL(client, id), &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // CreateOptsBuilder allows extensions to add additional parameters to the // Create request. type CreateOptsBuilder interface { ToPortCreateMap() (map[string]interface{}, error) } // CreateOpts specifies port creation parameters. type CreateOpts struct { // UUID of the Node this resource belongs to. NodeUUID string `json:"node_uuid,omitempty"` // Physical hardware address of this network Port, // typically the hardware MAC address. Address string `json:"address,omitempty"` // UUID of the Portgroup this resource belongs to. PortGroupUUID string `json:"portgroup_uuid,omitempty"` // The Port binding profile. If specified, must contain switch_id (only a MAC // address or an OpenFlow based datapath_id of the switch are accepted in this // field) and port_id (identifier of the physical port on the switch to which // node’s port is connected to) fields. switch_info is an optional string // field to be used to store any vendor-specific information. LocalLinkConnection map[string]interface{} `json:"local_link_connection,omitempty"` // Indicates whether PXE is enabled or disabled on the Port. PXEEnabled *bool `json:"pxe_enabled,omitempty"` // The name of the physical network to which a port is connected. May be empty. PhysicalNetwork string `json:"physical_network,omitempty"` // A set of one or more arbitrary metadata key and value pairs. Extra map[string]interface{} `json:"extra,omitempty"` // Indicates whether the Port is a Smart NIC port. IsSmartNIC *bool `json:"is_smartnic,omitempty"` } // ToPortCreateMap assembles a request body based on the contents of a CreateOpts. func (opts CreateOpts) ToPortCreateMap() (map[string]interface{}, error) { body, err := gophercloud.BuildRequestBody(opts, "") if err != nil { return nil, err } return body, nil } // Create - requests the creation of a port func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { reqBody, err := opts.ToPortCreateMap() if err != nil { r.Err = err return } resp, err := client.Post(createURL(client), reqBody, &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // TODO Update type Patch interface { ToPortUpdateMap() map[string]interface{} } // UpdateOpts is a slice of Patches used to update a port type UpdateOpts []Patch type UpdateOp string const ( ReplaceOp UpdateOp = "replace" AddOp UpdateOp = "add" RemoveOp UpdateOp = "remove" ) type UpdateOperation struct { Op UpdateOp `json:"op" required:"true"` Path string `json:"path" required:"true"` Value interface{} `json:"value,omitempty"` } func (opts UpdateOperation) ToPortUpdateMap() map[string]interface{} { return map[string]interface{}{ "op": opts.Op, "path": opts.Path, "value": opts.Value, } } // Update - requests the update of a port func Update(client *gophercloud.ServiceClient, id string, opts UpdateOpts) (r UpdateResult) { body := make([]map[string]interface{}, len(opts)) for i, patch := range opts { body[i] = patch.ToPortUpdateMap() } resp, err := client.Patch(updateURL(client, id), body, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete - requests the deletion of a port func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { resp, err := client.Delete(deleteURL(client, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } golang-github-gophercloud-gophercloud-0.12.0/openstack/baremetal/v1/ports/results.go000066400000000000000000000073051367513235700306340ustar00rootroot00000000000000package ports import ( "time" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) type portResult struct { gophercloud.Result } func (r portResult) Extract() (*Port, error) { var s Port err := r.ExtractInto(&s) return &s, err } func (r portResult) ExtractInto(v interface{}) error { return r.Result.ExtractIntoStructPtr(v, "") } func ExtractPortsInto(r pagination.Page, v interface{}) error { return r.(PortPage).Result.ExtractIntoSlicePtr(v, "ports") } // Port represents a port in the OpenStack Bare Metal API. type Port struct { // UUID for the resource. UUID string `json:"uuid"` // Physical hardware address of this network Port, // typically the hardware MAC address. Address string `json:"address"` // UUID of the Node this resource belongs to. NodeUUID string `json:"node_uuid"` // UUID of the Portgroup this resource belongs to. PortGroupUUID string `json:"portgroup_uuid"` // The Port binding profile. If specified, must contain switch_id (only a MAC // address or an OpenFlow based datapath_id of the switch are accepted in this // field) and port_id (identifier of the physical port on the switch to which // node’s port is connected to) fields. switch_info is an optional string // field to be used to store any vendor-specific information. LocalLinkConnection map[string]interface{} `json:"local_link_connection"` // Indicates whether PXE is enabled or disabled on the Port. PXEEnabled bool `json:"pxe_enabled"` // The name of the physical network to which a port is connected. // May be empty. PhysicalNetwork string `json:"physical_network"` // Internal metadata set and stored by the Port. This field is read-only. InternalInfo map[string]interface{} `json:"internal_info"` // A set of one or more arbitrary metadata key and value pairs. Extra map[string]interface{} `json:"extra"` // The UTC date and time when the resource was created, ISO 8601 format. CreatedAt time.Time `json:"created_at"` // The UTC date and time when the resource was updated, ISO 8601 format. // May be “nullâ€. UpdatedAt time.Time `json:"updated_at"` // A list of relative links. Includes the self and bookmark links. Links []interface{} `json:"links"` // Indicates whether the Port is a Smart NIC port. IsSmartNIC bool `json:"is_smartnic"` } // PortPage abstracts the raw results of making a List() request against // the API. type PortPage struct { pagination.LinkedPageBase } // IsEmpty returns true if a page contains no Port results. func (r PortPage) IsEmpty() (bool, error) { s, err := ExtractPorts(r) return len(s) == 0, err } // NextPageURL uses the response's embedded link reference to navigate to the // next page of results. 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) } // ExtractPorts interprets the results of a single page from a List() call, // producing a slice of Port entities. func ExtractPorts(r pagination.Page) ([]Port, error) { var s []Port err := ExtractPortsInto(r, &s) return s, err } // GetResult is the response from a Get operation. Call its Extract // method to interpret it as a Port. type GetResult struct { portResult } // CreateResult is the response from a Create operation. type CreateResult struct { portResult } // UpdateResult is the response from an Update operation. Call its Extract // method to interpret it as a Port. type UpdateResult struct { portResult } // 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 } golang-github-gophercloud-gophercloud-0.12.0/openstack/baremetal/v1/ports/testing/000077500000000000000000000000001367513235700302545ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/baremetal/v1/ports/testing/doc.go000066400000000000000000000000441367513235700313460ustar00rootroot00000000000000// ports unit tests package testing golang-github-gophercloud-gophercloud-0.12.0/openstack/baremetal/v1/ports/testing/fixtures.go000066400000000000000000000211571367513235700324620ustar00rootroot00000000000000package testing import ( "fmt" "net/http" "testing" "time" "github.com/gophercloud/gophercloud/openstack/baremetal/v1/ports" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) // PortListBody contains the canned body of a ports.List response, without detail. const PortListBody = ` { "ports": [ { "uuid": "3abe3f36-9708-4e9f-b07e-0f898061d3a7", "links": [ { "href": "http://192.168.0.8/baremetal/v1/ports/3abe3f36-9708-4e9f-b07e-0f898061d3a7", "rel": "self" }, { "href": "http://192.168.0.8/baremetal/ports/3abe3f36-9708-4e9f-b07e-0f898061d3a7", "rel": "bookmark" } ], "address": "52:54:00:0a:af:d1" }, { "uuid": "f2845e11-dbd4-4728-a8c0-30d19f48924a", "links": [ { "href": "http://192.168.0.8/baremetal/v1/ports/f2845e11-dbd4-4728-a8c0-30d19f48924a", "rel": "self" }, { "href": "http://192.168.0.8/baremetal/ports/f2845e11-dbd4-4728-a8c0-30d19f48924a", "rel": "bookmark" } ], "address": "52:54:00:4d:87:e6" } ] } ` // PortListDetailBody contains the canned body of a port.ListDetail response. const PortListDetailBody = ` { "ports": [ { "local_link_connection": {}, "node_uuid": "ddd06a60-b91e-4ab4-a6e7-56c0b25b6086", "uuid": "3abe3f36-9708-4e9f-b07e-0f898061d3a7", "links": [ { "href": "http://192.168.0.8/baremetal/v1/ports/3abe3f36-9708-4e9f-b07e-0f898061d3a7", "rel": "self" }, { "href": "http://192.168.0.8/baremetal/ports/3abe3f36-9708-4e9f-b07e-0f898061d3a7", "rel": "bookmark" } ], "extra": {}, "pxe_enabled": true, "portgroup_uuid": null, "updated_at": "2019-02-15T09:55:19+00:00", "physical_network": null, "address": "52:54:00:0a:af:d1", "internal_info": { }, "created_at": "2019-02-15T09:52:23+00:00" }, { "local_link_connection": {}, "node_uuid": "ddd06a60-b91e-4ab4-a6e7-56c0b25b6086", "uuid": "f2845e11-dbd4-4728-a8c0-30d19f48924a", "links": [ { "href": "http://192.168.0.8/baremetal/v1/ports/f2845e11-dbd4-4728-a8c0-30d19f48924a", "rel": "self" }, { "href": "http://192.168.0.8/baremetal/ports/f2845e11-dbd4-4728-a8c0-30d19f48924a", "rel": "bookmark" } ], "extra": {}, "pxe_enabled": true, "portgroup_uuid": null, "updated_at": "2019-02-15T09:55:19+00:00", "physical_network": null, "address": "52:54:00:4d:87:e6", "internal_info": {}, "created_at": "2019-02-15T09:52:24+00:00" } ] } ` // SinglePortBody is the canned body of a Get request on an existing port. const SinglePortBody = ` { "local_link_connection": { }, "node_uuid": "ddd06a60-b91e-4ab4-a6e7-56c0b25b6086", "uuid": "f2845e11-dbd4-4728-a8c0-30d19f48924a", "links": [ { "href": "http://192.168.0.8/baremetal/v1/ports/f2845e11-dbd4-4728-a8c0-30d19f48924a", "rel": "self" }, { "href": "http://192.168.0.8/baremetal/ports/f2845e11-dbd4-4728-a8c0-30d19f48924a", "rel": "bookmark" } ], "extra": { }, "pxe_enabled": true, "portgroup_uuid": null, "updated_at": "2019-02-15T09:55:19+00:00", "physical_network": null, "address": "52:54:00:4d:87:e6", "internal_info": { }, "created_at": "2019-02-15T09:52:24+00:00" } ` var ( fooCreated, _ = time.Parse(time.RFC3339, "2019-02-15T09:52:24+00:00") fooUpdated, _ = time.Parse(time.RFC3339, "2019-02-15T09:55:19+00:00") BarCreated, _ = time.Parse(time.RFC3339, "2019-02-15T09:52:23+00:00") BarUpdated, _ = time.Parse(time.RFC3339, "2019-02-15T09:55:19+00:00") PortFoo = ports.Port{ UUID: "f2845e11-dbd4-4728-a8c0-30d19f48924a", NodeUUID: "ddd06a60-b91e-4ab4-a6e7-56c0b25b6086", Address: "52:54:00:4d:87:e6", PXEEnabled: true, LocalLinkConnection: map[string]interface{}{}, InternalInfo: map[string]interface{}{}, Extra: map[string]interface{}{}, CreatedAt: fooCreated, UpdatedAt: fooUpdated, Links: []interface{}{map[string]interface{}{"href": "http://192.168.0.8/baremetal/v1/ports/f2845e11-dbd4-4728-a8c0-30d19f48924a", "rel": "self"}, map[string]interface{}{"href": "http://192.168.0.8/baremetal/ports/f2845e11-dbd4-4728-a8c0-30d19f48924a", "rel": "bookmark"}}, } PortBar = ports.Port{ UUID: "3abe3f36-9708-4e9f-b07e-0f898061d3a7", NodeUUID: "ddd06a60-b91e-4ab4-a6e7-56c0b25b6086", Address: "52:54:00:0a:af:d1", PXEEnabled: true, LocalLinkConnection: map[string]interface{}{}, InternalInfo: map[string]interface{}{}, Extra: map[string]interface{}{}, CreatedAt: BarCreated, UpdatedAt: BarUpdated, Links: []interface{}{map[string]interface{}{"href": "http://192.168.0.8/baremetal/v1/ports/3abe3f36-9708-4e9f-b07e-0f898061d3a7", "rel": "self"}, map[string]interface{}{"rel": "bookmark", "href": "http://192.168.0.8/baremetal/ports/3abe3f36-9708-4e9f-b07e-0f898061d3a7"}}, } ) // HandlePortListSuccessfully sets up the test server to respond to a port List request. func HandlePortListSuccessfully(t *testing.T) { th.Mux.HandleFunc("/ports", 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, PortListBody) case "f2845e11-dbd4-4728-a8c0-30d19f48924a": fmt.Fprintf(w, `{ "ports": [] }`) default: t.Fatalf("/ports invoked with unexpected marker=[%s]", marker) } }) } // HandlePortListSuccessfully sets up the test server to respond to a port List request. func HandlePortListDetailSuccessfully(t *testing.T) { th.Mux.HandleFunc("/ports/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() fmt.Fprintf(w, PortListDetailBody) }) } // HandleSPortCreationSuccessfully sets up the test server to respond to a port creation request // with a given response. func HandlePortCreationSuccessfully(t *testing.T, response string) { th.Mux.HandleFunc("/ports", 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, `{ "node_uuid": "ddd06a60-b91e-4ab4-a6e7-56c0b25b6086", "address": "52:54:00:4d:87:e6", "pxe_enabled": true }`) w.WriteHeader(http.StatusAccepted) w.Header().Add("Content-Type", "application/json") fmt.Fprintf(w, response) }) } // HandlePortDeletionSuccessfully sets up the test server to respond to a port deletion request. func HandlePortDeletionSuccessfully(t *testing.T) { th.Mux.HandleFunc("/ports/3abe3f36-9708-4e9f-b07e-0f898061d3a7", 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 HandlePortGetSuccessfully(t *testing.T) { th.Mux.HandleFunc("/ports/f2845e11-dbd4-4728-a8c0-30d19f48924a", 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, SinglePortBody) }) } func HandlePortUpdateSuccessfully(t *testing.T, response string) { th.Mux.HandleFunc("/ports/f2845e11-dbd4-4728-a8c0-30d19f48924a", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "PATCH") 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, `[{"op": "replace", "path": "/address", "value": "22:22:22:22:22:22"}]`) fmt.Fprintf(w, response) }) } golang-github-gophercloud-gophercloud-0.12.0/openstack/baremetal/v1/ports/testing/requests_test.go000066400000000000000000000065731367513235700335300ustar00rootroot00000000000000package testing import ( "testing" "github.com/gophercloud/gophercloud/openstack/baremetal/v1/ports" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) func TestListDetailPorts(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandlePortListDetailSuccessfully(t) pages := 0 err := ports.ListDetail(client.ServiceClient(), ports.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { pages++ actual, err := ports.ExtractPorts(page) if err != nil { return false, err } if len(actual) != 2 { t.Fatalf("Expected 2 ports, got %d", len(actual)) } th.CheckDeepEquals(t, PortBar, actual[0]) th.CheckDeepEquals(t, PortFoo, actual[1]) return true, nil }) th.AssertNoErr(t, err) if pages != 1 { t.Errorf("Expected 1 page, saw %d", pages) } } func TestListPorts(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandlePortListSuccessfully(t) pages := 0 err := ports.List(client.ServiceClient(), ports.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { pages++ actual, err := ports.ExtractPorts(page) if err != nil { return false, err } if len(actual) != 2 { t.Fatalf("Expected 2 ports, got %d", len(actual)) } th.AssertEquals(t, "3abe3f36-9708-4e9f-b07e-0f898061d3a7", actual[0].UUID) th.AssertEquals(t, "f2845e11-dbd4-4728-a8c0-30d19f48924a", actual[1].UUID) return true, nil }) th.AssertNoErr(t, err) if pages != 1 { t.Errorf("Expected 1 page, saw %d", pages) } } func TestListOpts(t *testing.T) { // Detail cannot take Fields opts := ports.ListOpts{ Fields: []string{"uuid", "address"}, } _, err := opts.ToPortListDetailQuery() th.AssertEquals(t, err.Error(), "fields is not a valid option when getting a detailed listing of ports") // Regular ListOpts can query, err := opts.ToPortListQuery() th.AssertEquals(t, query, "?fields=uuid&fields=address") th.AssertNoErr(t, err) } func TestCreatePort(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandlePortCreationSuccessfully(t, SinglePortBody) iTrue := true actual, err := ports.Create(client.ServiceClient(), ports.CreateOpts{ NodeUUID: "ddd06a60-b91e-4ab4-a6e7-56c0b25b6086", Address: "52:54:00:4d:87:e6", PXEEnabled: &iTrue, }).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, PortFoo, *actual) } func TestDeletePort(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandlePortDeletionSuccessfully(t) res := ports.Delete(client.ServiceClient(), "3abe3f36-9708-4e9f-b07e-0f898061d3a7") th.AssertNoErr(t, res.Err) } func TestGetPort(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandlePortGetSuccessfully(t) c := client.ServiceClient() actual, err := ports.Get(c, "f2845e11-dbd4-4728-a8c0-30d19f48924a").Extract() if err != nil { t.Fatalf("Unexpected Get error: %v", err) } th.CheckDeepEquals(t, PortFoo, *actual) } func TestUpdatePort(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandlePortUpdateSuccessfully(t, SinglePortBody) c := client.ServiceClient() actual, err := ports.Update(c, "f2845e11-dbd4-4728-a8c0-30d19f48924a", ports.UpdateOpts{ ports.UpdateOperation{ Op: ports.ReplaceOp, Path: "/address", Value: "22:22:22:22:22:22", }, }).Extract() if err != nil { t.Fatalf("Unexpected Update error: %v", err) } th.CheckDeepEquals(t, PortFoo, *actual) } golang-github-gophercloud-gophercloud-0.12.0/openstack/baremetal/v1/ports/urls.go000066400000000000000000000014131367513235700301120ustar00rootroot00000000000000package ports import "github.com/gophercloud/gophercloud" func createURL(client *gophercloud.ServiceClient) string { return client.ServiceURL("ports") } func listURL(client *gophercloud.ServiceClient) string { return createURL(client) } func listDetailURL(client *gophercloud.ServiceClient) string { return client.ServiceURL("ports", "detail") } func resourceURL(client *gophercloud.ServiceClient, id string) string { return client.ServiceURL("ports", id) } func deleteURL(client *gophercloud.ServiceClient, id string) string { return resourceURL(client, id) } func getURL(client *gophercloud.ServiceClient, id string) string { return resourceURL(client, id) } func updateURL(client *gophercloud.ServiceClient, id string) string { return resourceURL(client, id) } golang-github-gophercloud-gophercloud-0.12.0/openstack/baremetalintrospection/000077500000000000000000000000001367513235700277235ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/baremetalintrospection/httpbasic/000077500000000000000000000000001367513235700317045ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/baremetalintrospection/httpbasic/doc.go000066400000000000000000000007741367513235700330100ustar00rootroot00000000000000/* Package httpbasic provides support for http_basic bare metal introspection endpoints. Example of obtaining and using a client: client, err := httpbasic.NewBareMetalIntrospectionHTTPBasic(httpbasic.EndpointOpts{ IronicInspectorEndpoint: "http://localhost:5050/v1/", IronicInspectorUser: "myUser", IronicInspectorUserPassword: "myPassword", }) if err != nil { panic(err) } introspection.GetIntrospectionStatus(client, "a62b8495-52e2-407b-b3cb-62775d04c2b8") */ package httpbasic golang-github-gophercloud-gophercloud-0.12.0/openstack/baremetalintrospection/httpbasic/requests.go000066400000000000000000000026071367513235700341130ustar00rootroot00000000000000package httpbasic import ( "encoding/base64" "fmt" "github.com/gophercloud/gophercloud" ) // EndpointOpts specifies a "http_basic" Ironic Inspector Endpoint. type EndpointOpts struct { IronicInspectorEndpoint string IronicInspectorUser string IronicInspectorUserPassword string } func initClientOpts(client *gophercloud.ProviderClient, eo EndpointOpts) (*gophercloud.ServiceClient, error) { sc := new(gophercloud.ServiceClient) if eo.IronicInspectorEndpoint == "" { return nil, fmt.Errorf("IronicInspectorEndpoint is required") } if eo.IronicInspectorUser == "" || eo.IronicInspectorUserPassword == "" { return nil, fmt.Errorf("User and Password are required") } token := []byte(eo.IronicInspectorUser + ":" + eo.IronicInspectorUserPassword) encodedToken := base64.StdEncoding.EncodeToString(token) sc.MoreHeaders = map[string]string{"Authorization": "Basic " + encodedToken} sc.Endpoint = gophercloud.NormalizeURL(eo.IronicInspectorEndpoint) sc.ProviderClient = client return sc, nil } // NewBareMetalIntrospectionHTTPBasic creates a ServiceClient that may be used to access a // "http_basic" bare metal introspection service. func NewBareMetalIntrospectionHTTPBasic(eo EndpointOpts) (*gophercloud.ServiceClient, error) { sc, err := initClientOpts(&gophercloud.ProviderClient{}, eo) if err != nil { return nil, err } sc.Type = "baremetal-inspector" return sc, nil } golang-github-gophercloud-gophercloud-0.12.0/openstack/baremetalintrospection/httpbasic/testing/000077500000000000000000000000001367513235700333615ustar00rootroot00000000000000requests_test.go000066400000000000000000000022031367513235700365400ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/baremetalintrospection/httpbasic/testingpackage testing import ( "encoding/base64" "testing" "github.com/gophercloud/gophercloud/openstack/baremetalintrospection/httpbasic" th "github.com/gophercloud/gophercloud/testhelper" ) func TestNoAuth(t *testing.T) { httpClient, err := httpbasic.NewBareMetalIntrospectionHTTPBasic(httpbasic.EndpointOpts{ IronicInspectorEndpoint: "http://ironic:5050/v1", IronicInspectorUser: "myUser", IronicInspectorUserPassword: "myPasswd", }) th.AssertNoErr(t, err) encToken := base64.StdEncoding.EncodeToString([]byte("myUser:myPasswd")) headerValue := "Basic " + encToken th.AssertEquals(t, headerValue, httpClient.MoreHeaders["Authorization"]) errTest1, err := httpbasic.NewBareMetalIntrospectionHTTPBasic(httpbasic.EndpointOpts{ IronicInspectorEndpoint: "http://ironic:5050/v1", }) _ = errTest1 th.AssertEquals(t, "User and Password are required", err.Error()) errTest2, err := httpbasic.NewBareMetalIntrospectionHTTPBasic(httpbasic.EndpointOpts{ IronicInspectorUser: "myUser", IronicInspectorUserPassword: "myPasswd", }) _ = errTest2 th.AssertEquals(t, "IronicInspectorEndpoint is required", err.Error()) } golang-github-gophercloud-gophercloud-0.12.0/openstack/baremetalintrospection/noauth/000077500000000000000000000000001367513235700312215ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/baremetalintrospection/noauth/doc.go000066400000000000000000000006171367513235700323210ustar00rootroot00000000000000/* Package noauth provides support for noauth bare metal introspection endpoints. Example of obtaining and using a client: client, err := noauth.NewBareMetalIntrospectionNoAuth(noauth.EndpointOpts{ IronicInspectorEndpoint: "http://localhost:5050/v1/", }) if err != nil { panic(err) } introspection.GetIntrospectionStatus(client, "a62b8495-52e2-407b-b3cb-62775d04c2b8") */ package noauth golang-github-gophercloud-gophercloud-0.12.0/openstack/baremetalintrospection/noauth/requests.go000066400000000000000000000022271367513235700334260ustar00rootroot00000000000000package noauth import ( "fmt" "github.com/gophercloud/gophercloud" ) // EndpointOpts specifies a "noauth" Ironic Inspector Endpoint. type EndpointOpts struct { // IronicInspectorEndpoint [required] is currently only used with "noauth" Ironic introspection. // An Ironic inspector endpoint with "auth_strategy=noauth" is necessary, for example: // http://ironic.example.com:5050/v1. IronicInspectorEndpoint string } func initClientOpts(client *gophercloud.ProviderClient, eo EndpointOpts) (*gophercloud.ServiceClient, error) { sc := new(gophercloud.ServiceClient) if eo.IronicInspectorEndpoint == "" { return nil, fmt.Errorf("IronicInspectorEndpoint is required") } sc.Endpoint = gophercloud.NormalizeURL(eo.IronicInspectorEndpoint) sc.ProviderClient = client return sc, nil } // NewBareMetalIntrospectionNoAuth creates a ServiceClient that may be used to access a // "noauth" bare metal introspection service. func NewBareMetalIntrospectionNoAuth(eo EndpointOpts) (*gophercloud.ServiceClient, error) { sc, err := initClientOpts(&gophercloud.ProviderClient{}, eo) if err != nil { return nil, err } sc.Type = "baremetal-inspector" return sc, nil } golang-github-gophercloud-gophercloud-0.12.0/openstack/baremetalintrospection/noauth/testing/000077500000000000000000000000001367513235700326765ustar00rootroot00000000000000requests_test.go000066400000000000000000000006351367513235700360640ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/baremetalintrospection/noauth/testingpackage testing import ( "testing" "github.com/gophercloud/gophercloud/openstack/baremetalintrospection/noauth" th "github.com/gophercloud/gophercloud/testhelper" ) func TestNoAuth(t *testing.T) { noauthClient, err := noauth.NewBareMetalIntrospectionNoAuth(noauth.EndpointOpts{ IronicInspectorEndpoint: "http://ironic:5050/v1", }) th.AssertNoErr(t, err) th.AssertEquals(t, "", noauthClient.TokenID) } golang-github-gophercloud-gophercloud-0.12.0/openstack/baremetalintrospection/v1/000077500000000000000000000000001367513235700302515ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/baremetalintrospection/v1/introspection/000077500000000000000000000000001367513235700331515ustar00rootroot00000000000000doc.go000066400000000000000000000026711367513235700341740ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/baremetalintrospection/v1/introspection/* Package introspection contains the functionality for Starting introspection, Get introspection status, List all introspection statuses, Abort an introspection, Get stored introspection data and reapply introspection on stored data. API reference https://developer.openstack.org/api-ref/baremetal-introspection/#node-introspection Example to Start Introspection err := introspection.StartIntrospection(client, NodeUUID, introspection.StartOpts{}).ExtractErr() if err != nil { panic(err) } Example to Get an Introspection status _, err := introspection.GetIntrospectionStatus(client, NodeUUID).Extract() if err != nil { panic(err) } Example to List all introspection statuses introspection.ListIntrospections(client.ServiceClient(), nil).EachPage(func(page pagination.Page) (bool, error) { introspectionsList, err := introspection.ExtractIntrospections(page) if err != nil { return false, err } for _, n := range introspectionsList { // Do something } return true, nil }) Example to Abort an Introspection err := introspection.AbortIntrospection(client, NodeUUID).ExtractErr() if err != nil { panic(err) } Example to Get stored Introspection Data v, err := introspection.GetIntrospectionData(c, NodeUUID).Extract() if err != nil { panic(err) } Example to apply Introspection Data err := introspection.ApplyIntrospectionData(c, NodeUUID).ExtractErr() if err != nil { panic(err) } */ package introspection requests.go000066400000000000000000000103331367513235700352740ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/baremetalintrospection/v1/introspectionpackage introspection import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // ListIntrospectionsOptsBuilder allows extensions to add additional parameters to the // ListIntrospections request. type ListIntrospectionsOptsBuilder interface { ToIntrospectionsListQuery() (string, error) } // ListIntrospectionsOpts allows the filtering and sorting of paginated collections through // the Introspection API. Filtering is achieved by passing in struct field values that map to // the node attributes you want to see returned. Marker and Limit are used // for pagination. type ListIntrospectionsOpts struct { // Requests a page size of items. Limit int `q:"limit"` // The ID of the last-seen item. Marker string `q:"marker"` } // ToIntrospectionsListQuery formats a ListIntrospectionsOpts into a query string. func (opts ListIntrospectionsOpts) ToIntrospectionsListQuery() (string, error) { q, err := gophercloud.BuildQueryString(opts) return q.String(), err } // ListIntrospections makes a request against the Inspector API to list the current introspections. func ListIntrospections(client *gophercloud.ServiceClient, opts ListIntrospectionsOptsBuilder) pagination.Pager { url := listIntrospectionsURL(client) if opts != nil { query, err := opts.ToIntrospectionsListQuery() if err != nil { return pagination.Pager{Err: err} } url += query } return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { var rpage = IntrospectionPage{pagination.LinkedPageBase{PageResult: r}} return rpage }) } // GetIntrospectionStatus makes a request against the Inspector API to get the // status of a single introspection. func GetIntrospectionStatus(client *gophercloud.ServiceClient, nodeID string) (r GetIntrospectionStatusResult) { resp, err := client.Get(introspectionURL(client, nodeID), &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // StartOptsBuilder allows extensions to add additional parameters to the // Start request. type StartOptsBuilder interface { ToStartIntrospectionQuery() (string, error) } // StartOpts represents options to start an introspection. type StartOpts struct { // Whether the current installation of ironic-inspector can manage PXE booting of nodes. ManageBoot *bool `q:"manage_boot"` } // ToStartIntrospectionQuery converts a StartOpts into a request. func (opts StartOpts) ToStartIntrospectionQuery() (string, error) { q, err := gophercloud.BuildQueryString(opts) return q.String(), err } // StartIntrospection initiate hardware introspection for node NodeID . // All power management configuration for this node needs to be done prior to calling the endpoint. func StartIntrospection(client *gophercloud.ServiceClient, nodeID string, opts StartOptsBuilder) (r StartResult) { _, err := opts.ToStartIntrospectionQuery() if err != nil { r.Err = err return } resp, err := client.Post(introspectionURL(client, nodeID), nil, nil, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // AbortIntrospection abort running introspection. func AbortIntrospection(client *gophercloud.ServiceClient, nodeID string) (r AbortResult) { resp, err := client.Post(abortIntrospectionURL(client, nodeID), nil, nil, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // GetIntrospectionData return stored data from successful introspection. func GetIntrospectionData(client *gophercloud.ServiceClient, nodeID string) (r DataResult) { resp, err := client.Get(introspectionDataURL(client, nodeID), &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // ReApplyIntrospection triggers introspection on stored unprocessed data. // No data is allowed to be sent along with the request. func ReApplyIntrospection(client *gophercloud.ServiceClient, nodeID string) (r ApplyDataResult) { resp, err := client.Post(introspectionUnprocessedDataURL(client, nodeID), nil, nil, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } results.go000066400000000000000000000235521367513235700351310ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/baremetalintrospection/v1/introspectionpackage introspection import ( "encoding/json" "fmt" "time" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) type introspectionResult struct { gophercloud.Result } // Extract interprets any introspectionResult as an Introspection, if possible. func (r introspectionResult) Extract() (*Introspection, error) { var s Introspection err := r.ExtractInto(&s) return &s, err } // ExtractInto will extract a response body into an Introspection struct. func (r introspectionResult) ExtractInto(v interface{}) error { return r.Result.ExtractIntoStructPtr(v, "") } // ExtractIntrospectionsInto will extract a collection of introspectResult pages into a // slice of Introspection entities. func ExtractIntrospectionsInto(r pagination.Page, v interface{}) error { return r.(IntrospectionPage).Result.ExtractIntoSlicePtr(v, "introspection") } // ExtractIntrospections interprets the results of a single page from a // ListIntrospections() call, producing a slice of Introspection entities. func ExtractIntrospections(r pagination.Page) ([]Introspection, error) { var s []Introspection err := ExtractIntrospectionsInto(r, &s) return s, err } // IntrospectionPage abstracts the raw results of making a ListIntrospections() // request against the Inspector 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 ExtractIntrospections call. type IntrospectionPage struct { pagination.LinkedPageBase } // Introspection represents an introspection in the OpenStack Bare Metal Introspector API. type Introspection struct { // Whether introspection is finished Finished bool `json:"finished"` // State of the introspection State string `json:"state"` // Error message, can be null; "Canceled by operator" in case introspection was aborted Error string `json:"error"` // UUID of the introspection UUID string `json:"uuid"` // UTC ISO8601 timestamp StartedAt time.Time `json:"-"` // UTC ISO8601 timestamp or null FinishedAt time.Time `json:"-"` // Link to the introspection URL Links []interface{} `json:"links"` } // IsEmpty returns true if a page contains no Introspection results. func (r IntrospectionPage) IsEmpty() (bool, error) { s, err := ExtractIntrospections(r) return len(s) == 0, err } // NextPageURL uses the response's embedded link reference to navigate to the // next page of results. func (r IntrospectionPage) NextPageURL() (string, error) { var s struct { Links []gophercloud.Link `json:"introspection_links"` } err := r.ExtractInto(&s) if err != nil { return "", err } return gophercloud.ExtractNextURL(s.Links) } // UnmarshalJSON trie to convert values for started_at and finished_at from the // json response into RFC3339 standard. Since Introspection API can remove the // Z from the format, if the conversion fails, it falls back to an RFC3339 // with no Z format supported by gophercloud. func (r *Introspection) UnmarshalJSON(b []byte) error { type tmp Introspection var s struct { tmp StartedAt string `json:"started_at"` FinishedAt string `json:"finished_at"` } err := json.Unmarshal(b, &s) if err != nil { return err } *r = Introspection(s.tmp) if s.StartedAt != "" { t, err := time.Parse(time.RFC3339, s.StartedAt) if err != nil { t, err = time.Parse(gophercloud.RFC3339NoZ, s.StartedAt) if err != nil { return err } } r.StartedAt = t } if s.FinishedAt != "" { t, err := time.Parse(time.RFC3339, s.FinishedAt) if err != nil { t, err = time.Parse(gophercloud.RFC3339NoZ, s.FinishedAt) if err != nil { return err } } r.FinishedAt = t } return nil } // GetIntrospectionStatusResult is the response from a GetIntrospectionStatus operation. // Call its Extract method to interpret it as an Introspection. type GetIntrospectionStatusResult struct { introspectionResult } // StartResult is the response from a StartIntrospection operation. // Call its ExtractErr method to determine if the call succeeded or failed. type StartResult struct { gophercloud.ErrResult } // AbortResult is the response from an AbortIntrospection operation. // Call its ExtractErr method to determine if the call succeeded or failed. type AbortResult struct { gophercloud.ErrResult } // Data represents the full introspection data collected. // The format and contents of the stored data depends on the ramdisk used // and plugins enabled both in the ramdisk and in inspector itself. // This structure has been provided for basic compatibility but it // will need extensions type Data struct { AllInterfaces map[string]BaseInterfaceType `json:"all_interfaces"` BootInterface string `json:"boot_interface"` CPUArch string `json:"cpu_arch"` CPUs int `json:"cpus"` Error string `json:"error"` Interfaces map[string]BaseInterfaceType `json:"interfaces"` Inventory InventoryType `json:"inventory"` IPMIAddress string `json:"ipmi_address"` LocalGB int `json:"local_gb"` MACs []string `json:"macs"` MemoryMB int `json:"memory_mb"` RootDisk RootDiskType `json:"root_disk"` Extra ExtraHardwareDataType `json:"extra"` NUMATopology NUMATopology `json:"numa_topology"` } // Sub Types defined under Data and deeper in the structure type BaseInterfaceType struct { ClientID string `json:"client_id"` IP string `json:"ip"` MAC string `json:"mac"` PXE bool `json:"pxe"` LLDPProcessed map[string]interface{} `json:"lldp_processed"` } type BootInfoType struct { CurrentBootMode string `json:"current_boot_mode"` PXEInterface string `json:"pxe_interface"` } type CPUType struct { Architecture string `json:"architecture"` Count int `json:"count"` Flags []string `json:"flags"` Frequency string `json:"frequency"` ModelName string `json:"model_name"` } type LLDPTLVType struct { Type int Value string } type InterfaceType struct { BIOSDevName string `json:"biosdevname"` ClientID string `json:"client_id"` HasCarrier bool `json:"has_carrier"` IPV4Address string `json:"ipv4_address"` IPV6Address string `json:"ipv6_address"` LLDP []LLDPTLVType `json:"lldp"` MACAddress string `json:"mac_address"` Name string `json:"name"` Product string `json:"product"` Vendor string `json:"vendor"` } type InventoryType struct { BmcAddress string `json:"bmc_address"` Boot BootInfoType `json:"boot"` CPU CPUType `json:"cpu"` Disks []RootDiskType `json:"disks"` Interfaces []InterfaceType `json:"interfaces"` Memory MemoryType `json:"memory"` SystemVendor SystemVendorType `json:"system_vendor"` Hostname string `json:"hostname"` } type MemoryType struct { PhysicalMb int `json:"physical_mb"` Total int `json:"total"` } type RootDiskType struct { Hctl string `json:"hctl"` Model string `json:"model"` Name string `json:"name"` ByPath string `json:"by_path"` Rotational bool `json:"rotational"` Serial string `json:"serial"` Size int `json:"size"` Vendor string `json:"vendor"` Wwn string `json:"wwn"` WwnVendorExtension string `json:"wwn_vendor_extension"` WwnWithExtension string `json:"wwn_with_extension"` } type SystemVendorType struct { Manufacturer string `json:"manufacturer"` ProductName string `json:"product_name"` SerialNumber string `json:"serial_number"` } type ExtraHardwareData map[string]interface{} type ExtraHardwareDataSection map[string]ExtraHardwareData type ExtraHardwareDataType struct { CPU ExtraHardwareDataSection `json:"cpu"` Disk ExtraHardwareDataSection `json:"disk"` Firmware ExtraHardwareDataSection `json:"firmware"` IPMI ExtraHardwareDataSection `json:"ipmi"` Memory ExtraHardwareDataSection `json:"memory"` Network ExtraHardwareDataSection `json:"network"` System ExtraHardwareDataSection `json:"system"` } type NUMATopology struct { CPUs []NUMACPU `json:"cpus"` NICs []NUMANIC `json:"nics"` RAM []NUMARAM `json:"ram"` } type NUMACPU struct { CPU int `json:"cpu"` NUMANode int `json:"numa_node"` ThreadSiblings []int `json:"thread_siblings"` } type NUMANIC struct { Name string `json:"name"` NUMANode int `json:"numa_node"` } type NUMARAM struct { NUMANode int `json:"numa_node"` SizeKB int `json:"size_kb"` } // UnmarshalJSON interprets an LLDP TLV [key, value] pair as an LLDPTLVType structure func (r *LLDPTLVType) UnmarshalJSON(data []byte) error { var list []interface{} if err := json.Unmarshal(data, &list); err != nil { return err } if len(list) != 2 { return fmt.Errorf("Invalid LLDP TLV key-value pair") } fieldtype, ok := list[0].(float64) if !ok { return fmt.Errorf("LLDP TLV key is not number") } value, ok := list[1].(string) if !ok { return fmt.Errorf("LLDP TLV value is not string") } r.Type = int(fieldtype) r.Value = value return nil } // Extract interprets any IntrospectionDataResult as IntrospectionData, if possible. func (r DataResult) Extract() (*Data, error) { var s Data err := r.ExtractInto(&s) return &s, err } // DataResult represents the response from a GetIntrospectionData operation. // Call its Extract method to interpret it as a Data. type DataResult struct { gophercloud.Result } // ApplyDataResult is the response from an ApplyData operation. // Call its ExtractErr method to determine if the call succeeded or failed. type ApplyDataResult struct { gophercloud.ErrResult } testing/000077500000000000000000000000001367513235700345475ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/baremetalintrospection/v1/introspectiondoc.go000066400000000000000000000000201367513235700356330ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/baremetalintrospection/v1/introspection/testingpackage testing fixtures.go000066400000000000000000000401401367513235700367460ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/baremetalintrospection/v1/introspection/testingpackage testing import ( "fmt" "net/http" "testing" "time" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack/baremetalintrospection/v1/introspection" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) // IntrospectionListBody contains the canned body of a introspection.IntrospectionList response. const IntrospectionListBody = ` { "introspection": [ { "error": null, "finished": true, "finished_at": "2017-08-17T11:36:16", "links": [ { "href": "http://127.0.0.1:5050/v1/introspection/05ccda19-581b-49bf-8f5a-6ded99701d87", "rel": "self" } ], "started_at": "2017-08-17T11:33:43", "state": "finished", "uuid": "05ccda19-581b-49bf-8f5a-6ded99701d87" }, { "error": null, "finished": true, "finished_at": "2017-08-16T12:24:30", "links": [ { "href": "http://127.0.0.1:5050/v1/introspection/c244557e-899f-46fa-a1ff-5b2c6718616b", "rel": "self" } ], "started_at": "2017-08-16T12:22:01", "state": "finished", "uuid": "c244557e-899f-46fa-a1ff-5b2c6718616b" } ] } ` // IntrospectionStatus contains the respnse of a single introspection satus. const IntrospectionStatus = ` { "error": null, "finished": true, "finished_at": "2017-08-16T12:24:30", "links": [ { "href": "http://127.0.0.1:5050/v1/introspection/c244557e-899f-46fa-a1ff-5b2c6718616b", "rel": "self" } ], "started_at": "2017-08-16T12:22:01", "state": "finished", "uuid": "c244557e-899f-46fa-a1ff-5b2c6718616b" } ` // IntrospectionDataJSONSample contains sample data reported by the introspection process. const IntrospectionDataJSONSample = ` { "all_interfaces": { "eth0": { "client_id": null, "ip": "172.24.42.100", "lldp_processed": { "switch_chassis_id": "11:22:33:aa:bb:cc", "switch_system_name": "sw01-dist-1b-b12" }, "mac": "52:54:00:4e:3d:30", "pxe": true }, "eth1": { "client_id": null, "ip": "172.24.42.101", "mac": "52:54:00:47:20:4d", "pxe": false } }, "boot_interface": "52:54:00:4e:3d:30", "cpu_arch": "x86_64", "cpus": 2, "error": null, "interfaces": { "eth0": { "client_id": null, "ip": "172.24.42.100", "mac": "52:54:00:4e:3d:30", "pxe": true } }, "inventory": { "bmc_address": "192.167.2.134", "boot": { "current_boot_mode": "bios", "pxe_interface": "52:54:00:4e:3d:30" }, "cpu": { "architecture": "x86_64", "count": 2, "flags": [ "fpu", "mmx", "fxsr", "sse", "sse2" ], "frequency": "2100.084" }, "disks": [ { "hctl": null, "model": "", "name": "/dev/vda", "rotational": true, "serial": null, "size": 13958643712, "vendor": "0x1af4", "wwn": null, "wwn_vendor_extension": null, "wwn_with_extension": null } ], "hostname": "myawesomehost", "interfaces": [ { "client_id": null, "has_carrier": true, "ipv4_address": "172.24.42.101", "lldp": [], "mac_address": "52:54:00:47:20:4d", "name": "eth1", "product": "0x0001", "vendor": "0x1af4" }, { "client_id": null, "has_carrier": true, "ipv4_address": "172.24.42.100", "lldp": [ [ 1, "04112233aabbcc" ], [ 5, "737730312d646973742d31622d623132" ] ], "mac_address": "52:54:00:4e:3d:30", "name": "eth0", "product": "0x0001", "vendor": "0x1af4" } ], "memory": { "physical_mb": 2048, "total": 2105864192 }, "system_vendor": { "manufacturer": "Bochs", "product_name": "Bochs", "serial_number": "Not Specified" } }, "ipmi_address": "192.167.2.134", "local_gb": 12, "macs": [ "52:54:00:4e:3d:30" ], "memory_mb": 2048, "root_disk": { "hctl": null, "model": "", "name": "/dev/vda", "rotational": true, "serial": null, "size": 13958643712, "vendor": "0x1af4", "wwn": null, "wwn_vendor_extension": null, "wwn_with_extension": null } } ` // IntrospectionExtraHardwareJSONSample contains extra hardware sample data // reported by the introspection process. const IntrospectionExtraHardwareJSONSample = ` { "cpu": { "logical": { "number": 16 }, "physical": { "clock": 2105032704, "cores": 8, "flags": "lm fpu fpu_exception wp vme de" } }, "disk": { "sda": { "rotational": 1, "vendor": "TEST" } }, "firmware": { "bios": { "date": "01/01/1970", "vendor": "test" } }, "ipmi": { "Fan1A RPM": { "unit": "RPM", "value": 3120 }, "Fan1B RPM": { "unit": "RPM", "value": 2280 } }, "memory": { "bank0": { "clock": 1600000000.0, "description": "DIMM DDR3 Synchronous Registered (Buffered) 1600 MHz (0.6 ns)" }, "bank1": { "clock": 1600000000.0, "description": "DIMM DDR3 Synchronous Registered (Buffered) 1600 MHz (0.6 ns)" } }, "network": { "em1": { "Autonegotiate": "on", "loopback": "off [fixed]" }, "p2p1": { "Autonegotiate": "on", "loopback": "off [fixed]" } }, "system": { "ipmi": { "channel": 1 }, "kernel": { "arch": "x86_64", "version": "3.10.0" }, "motherboard": { "vendor": "Test" }, "product": { "name": "test", "vendor": "Test" } } } ` // IntrospectionNUMADataJSONSample contains NUMA sample data // reported by the introspection process. const IntrospectionNUMADataJSONSample = ` { "numa_topology": { "cpus": [ { "cpu": 6, "numa_node": 1, "thread_siblings": [ 3, 27 ] }, { "cpu": 10, "numa_node": 0, "thread_siblings": [ 20, 44 ] } ], "nics": [ { "name": "p2p1", "numa_node": 0 }, { "name": "p2p2", "numa_node": 1 } ], "ram": [ { "numa_node": 0, "size_kb": 99289532 }, { "numa_node": 1, "size_kb": 100663296 } ] } } ` var ( fooTimeStarted, _ = time.Parse(gophercloud.RFC3339NoZ, "2017-08-17T11:33:43") fooTimeFinished, _ = time.Parse(gophercloud.RFC3339NoZ, "2017-08-17T11:36:16") IntrospectionFoo = introspection.Introspection{ Finished: true, State: "finished", Error: "", UUID: "05ccda19-581b-49bf-8f5a-6ded99701d87", StartedAt: fooTimeStarted, FinishedAt: fooTimeFinished, Links: []interface{}{ map[string]interface{}{ "href": "http://127.0.0.1:5050/v1/introspection/05ccda19-581b-49bf-8f5a-6ded99701d87", "rel": "self", }, }, } barTimeStarted, _ = time.Parse(gophercloud.RFC3339NoZ, "2017-08-16T12:22:01") barTimeFinished, _ = time.Parse(gophercloud.RFC3339NoZ, "2017-08-16T12:24:30") IntrospectionBar = introspection.Introspection{ Finished: true, State: "finished", Error: "", UUID: "c244557e-899f-46fa-a1ff-5b2c6718616b", StartedAt: barTimeStarted, FinishedAt: barTimeFinished, Links: []interface{}{ map[string]interface{}{ "href": "http://127.0.0.1:5050/v1/introspection/c244557e-899f-46fa-a1ff-5b2c6718616b", "rel": "self", }, }, } IntrospectionDataRes = introspection.Data{ CPUArch: "x86_64", MACs: []string{"52:54:00:4e:3d:30"}, RootDisk: introspection.RootDiskType{ Rotational: true, Model: "", Name: "/dev/vda", Size: 13958643712, Vendor: "0x1af4", }, Interfaces: map[string]introspection.BaseInterfaceType{ "eth0": { IP: "172.24.42.100", MAC: "52:54:00:4e:3d:30", PXE: true, }, }, CPUs: 2, BootInterface: "52:54:00:4e:3d:30", MemoryMB: 2048, IPMIAddress: "192.167.2.134", Inventory: introspection.InventoryType{ SystemVendor: introspection.SystemVendorType{ Manufacturer: "Bochs", ProductName: "Bochs", SerialNumber: "Not Specified", }, BmcAddress: "192.167.2.134", Boot: introspection.BootInfoType{ CurrentBootMode: "bios", PXEInterface: "52:54:00:4e:3d:30", }, CPU: introspection.CPUType{ Count: 2, Flags: []string{"fpu", "mmx", "fxsr", "sse", "sse2"}, Frequency: "2100.084", Architecture: "x86_64", }, Disks: []introspection.RootDiskType{ introspection.RootDiskType{ Rotational: true, Model: "", Name: "/dev/vda", Size: 13958643712, Vendor: "0x1af4", }, }, Interfaces: []introspection.InterfaceType{ introspection.InterfaceType{ Vendor: "0x1af4", HasCarrier: true, MACAddress: "52:54:00:47:20:4d", Name: "eth1", Product: "0x0001", IPV4Address: "172.24.42.101", LLDP: []introspection.LLDPTLVType{}, }, introspection.InterfaceType{ IPV4Address: "172.24.42.100", MACAddress: "52:54:00:4e:3d:30", Name: "eth0", Product: "0x0001", HasCarrier: true, Vendor: "0x1af4", LLDP: []introspection.LLDPTLVType{ introspection.LLDPTLVType{ Type: 1, Value: "04112233aabbcc", }, introspection.LLDPTLVType{ Type: 5, Value: "737730312d646973742d31622d623132", }, }, }, }, Memory: introspection.MemoryType{ PhysicalMb: 2048.0, Total: 2.105864192e+09, }, Hostname: "myawesomehost", }, Error: "", LocalGB: 12, AllInterfaces: map[string]introspection.BaseInterfaceType{ "eth1": { IP: "172.24.42.101", MAC: "52:54:00:47:20:4d", PXE: false, }, "eth0": { IP: "172.24.42.100", MAC: "52:54:00:4e:3d:30", PXE: true, LLDPProcessed: map[string]interface{}{ "switch_chassis_id": "11:22:33:aa:bb:cc", "switch_system_name": "sw01-dist-1b-b12", }, }, }, } IntrospectionExtraHardware = introspection.ExtraHardwareDataType{ CPU: introspection.ExtraHardwareDataSection{ "logical": map[string]interface{}{ "number": float64(16), }, "physical": map[string]interface{}{ "clock": float64(2105032704), "cores": float64(8), "flags": "lm fpu fpu_exception wp vme de", }, }, Disk: introspection.ExtraHardwareDataSection{ "sda": map[string]interface{}{ "rotational": float64(1), "vendor": "TEST", }, }, Firmware: introspection.ExtraHardwareDataSection{ "bios": map[string]interface{}{ "date": "01/01/1970", "vendor": "test", }, }, IPMI: introspection.ExtraHardwareDataSection{ "Fan1A RPM": map[string]interface{}{ "unit": "RPM", "value": float64(3120), }, "Fan1B RPM": map[string]interface{}{ "unit": "RPM", "value": float64(2280), }, }, Memory: introspection.ExtraHardwareDataSection{ "bank0": map[string]interface{}{ "clock": 1600000000.0, "description": "DIMM DDR3 Synchronous Registered (Buffered) 1600 MHz (0.6 ns)", }, "bank1": map[string]interface{}{ "clock": 1600000000.0, "description": "DIMM DDR3 Synchronous Registered (Buffered) 1600 MHz (0.6 ns)", }, }, Network: introspection.ExtraHardwareDataSection{ "em1": map[string]interface{}{ "Autonegotiate": "on", "loopback": "off [fixed]", }, "p2p1": map[string]interface{}{ "Autonegotiate": "on", "loopback": "off [fixed]", }, }, System: introspection.ExtraHardwareDataSection{ "ipmi": map[string]interface{}{ "channel": float64(1), }, "kernel": map[string]interface{}{ "arch": "x86_64", "version": "3.10.0", }, "motherboard": map[string]interface{}{ "vendor": "Test", }, "product": map[string]interface{}{ "name": "test", "vendor": "Test", }, }, } IntrospectionNUMA = introspection.NUMATopology{ CPUs: []introspection.NUMACPU{ { CPU: 6, NUMANode: 1, ThreadSiblings: []int{3, 27}, }, { CPU: 10, NUMANode: 0, ThreadSiblings: []int{20, 44}, }, }, NICs: []introspection.NUMANIC{ { Name: "p2p1", NUMANode: 0, }, { Name: "p2p2", NUMANode: 1, }, }, RAM: []introspection.NUMARAM{ { NUMANode: 0, SizeKB: 99289532, }, { NUMANode: 1, SizeKB: 100663296, }, }, } ) // HandleListIntrospectionsSuccessfully sets up the test server to respond to a server ListIntrospections request. func HandleListIntrospectionsSuccessfully(t *testing.T) { th.Mux.HandleFunc("/introspection", 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, IntrospectionListBody) case "c244557e-899f-46fa-a1ff-5b2c6718616b": fmt.Fprintf(w, `{ "introspection": [] }`) default: t.Fatalf("/introspection invoked with unexpected marker=[%s]", marker) } }) } // HandleGetIntrospectionStatusSuccessfully sets up the test server to respond to a GetIntrospectionStatus request. func HandleGetIntrospectionStatusSuccessfully(t *testing.T) { th.Mux.HandleFunc("/introspection/c244557e-899f-46fa-a1ff-5b2c6718616b", 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, IntrospectionStatus) }) } // HandleStartIntrospectionSuccessfully sets up the test server to respond to a StartIntrospection request. func HandleStartIntrospectionSuccessfully(t *testing.T) { th.Mux.HandleFunc("/introspection/c244557e-899f-46fa-a1ff-5b2c6718616b", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.WriteHeader(http.StatusAccepted) }) } // HandleAbortIntrospectionSuccessfully sets up the test server to respond to an AbortIntrospection request. func HandleAbortIntrospectionSuccessfully(t *testing.T) { th.Mux.HandleFunc("/introspection/c244557e-899f-46fa-a1ff-5b2c6718616b/abort", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.WriteHeader(http.StatusAccepted) }) } // HandleGetIntrospectionDataSuccessfully sets up the test server to respond to a GetIntrospectionData request. func HandleGetIntrospectionDataSuccessfully(t *testing.T) { th.Mux.HandleFunc("/introspection/c244557e-899f-46fa-a1ff-5b2c6718616b/data", 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, IntrospectionDataJSONSample) }) } // HandleReApplyIntrospectionSuccessfully sets up the test server to respond to a ReApplyIntrospection request. func HandleReApplyIntrospectionSuccessfully(t *testing.T) { th.Mux.HandleFunc("/introspection/c244557e-899f-46fa-a1ff-5b2c6718616b/data/unprocessed", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.WriteHeader(http.StatusAccepted) }) } requests_test.go000066400000000000000000000051401367513235700400100ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/baremetalintrospection/v1/introspection/testingpackage testing import ( "testing" "github.com/gophercloud/gophercloud/openstack/baremetalintrospection/v1/introspection" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) func TestListIntrospections(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleListIntrospectionsSuccessfully(t) pages := 0 err := introspection.ListIntrospections(client.ServiceClient(), introspection.ListIntrospectionsOpts{}).EachPage(func(page pagination.Page) (bool, error) { pages++ actual, err := introspection.ExtractIntrospections(page) if err != nil { return false, err } if len(actual) != 2 { t.Fatalf("Expected 2 introspections, got %d", len(actual)) } th.CheckDeepEquals(t, IntrospectionFoo, actual[0]) th.CheckDeepEquals(t, IntrospectionBar, actual[1]) return true, nil }) th.AssertNoErr(t, err) if pages != 1 { t.Errorf("Expected 1 page, saw %d", pages) } } func TestGetIntrospectionStatus(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleGetIntrospectionStatusSuccessfully(t) c := client.ServiceClient() actual, err := introspection.GetIntrospectionStatus(c, "c244557e-899f-46fa-a1ff-5b2c6718616b").Extract() if err != nil { t.Fatalf("Unexpected Get error: %v", err) } th.CheckDeepEquals(t, IntrospectionBar, *actual) } func TestStartIntrospection(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleStartIntrospectionSuccessfully(t) c := client.ServiceClient() err := introspection.StartIntrospection(c, "c244557e-899f-46fa-a1ff-5b2c6718616b", introspection.StartOpts{}).ExtractErr() th.AssertNoErr(t, err) } func TestAbortIntrospection(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleAbortIntrospectionSuccessfully(t) c := client.ServiceClient() err := introspection.AbortIntrospection(c, "c244557e-899f-46fa-a1ff-5b2c6718616b").ExtractErr() th.AssertNoErr(t, err) } func TestGetIntrospectionData(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleGetIntrospectionDataSuccessfully(t) c := client.ServiceClient() actual, err := introspection.GetIntrospectionData(c, "c244557e-899f-46fa-a1ff-5b2c6718616b").Extract() if err != nil { t.Fatalf("Unexpected Get error: %v", err) } th.CheckDeepEquals(t, IntrospectionDataRes, *actual) } func TestReApplyIntrospection(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleReApplyIntrospectionSuccessfully(t) c := client.ServiceClient() err := introspection.ReApplyIntrospection(c, "c244557e-899f-46fa-a1ff-5b2c6718616b").ExtractErr() th.AssertNoErr(t, err) } results_test.go000066400000000000000000000030561367513235700376420ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/baremetalintrospection/v1/introspection/testingpackage testing import ( "encoding/json" "strings" "testing" "github.com/gophercloud/gophercloud/openstack/baremetalintrospection/v1/introspection" th "github.com/gophercloud/gophercloud/testhelper" ) func TestLLDPTLVErrors(t *testing.T) { badInputs := []string{ "[1]", "[1, 2]", "[\"foo\", \"bar\"]", } for _, input := range badInputs { var output introspection.LLDPTLVType err := json.Unmarshal([]byte(input), &output) if err == nil { t.Fatalf("No JSON parse error for invalid LLDP TLV %s", input) } if !strings.Contains(err.Error(), "LLDP TLV") { t.Fatalf("Unexpected JSON parse error \"%s\" for invalid LLDP TLV %s", err, input) } } } func TestExtraHardware(t *testing.T) { var output introspection.ExtraHardwareDataType err := json.Unmarshal([]byte(IntrospectionExtraHardwareJSONSample), &output) if err != nil { t.Fatalf("Failed to unmarshal ExtraHardware data: %s", err) } th.CheckDeepEquals(t, IntrospectionExtraHardware, output) } func TestIntrospectionNUMA(t *testing.T) { var output introspection.Data err := json.Unmarshal([]byte(IntrospectionNUMADataJSONSample), &output) if err != nil { t.Fatalf("Failed to unmarshal NUMA Data: %s", err) } th.CheckDeepEquals(t, IntrospectionNUMA, output.NUMATopology) } func TestHostnameInInventory(t *testing.T) { var output introspection.Data err := json.Unmarshal([]byte(IntrospectionDataJSONSample), &output) if err != nil { t.Fatalf("Failed to unmarshal Inventory data: %s", err) } th.CheckDeepEquals(t, IntrospectionDataRes.Inventory.Hostname, "myawesomehost") } urls.go000066400000000000000000000014241367513235700344070ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/baremetalintrospection/v1/introspectionpackage introspection import "github.com/gophercloud/gophercloud" func listIntrospectionsURL(client *gophercloud.ServiceClient) string { return client.ServiceURL("introspection") } func introspectionURL(client *gophercloud.ServiceClient, nodeID string) string { return client.ServiceURL("introspection", nodeID) } func abortIntrospectionURL(client *gophercloud.ServiceClient, nodeID string) string { return client.ServiceURL("introspection", nodeID, "abort") } func introspectionDataURL(client *gophercloud.ServiceClient, nodeID string) string { return client.ServiceURL("introspection", nodeID, "data") } func introspectionUnprocessedDataURL(client *gophercloud.ServiceClient, nodeID string) string { return client.ServiceURL("introspection", nodeID, "data", "unprocessed") } golang-github-gophercloud-gophercloud-0.12.0/openstack/blockstorage/000077500000000000000000000000001367513235700256255ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/blockstorage/apiversions/000077500000000000000000000000001367513235700301675ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/blockstorage/apiversions/doc.go000066400000000000000000000013401367513235700312610ustar00rootroot00000000000000/* Package apiversions provides information and interaction with the different API versions for the OpenStack Block Storage service, code-named Cinder. Example of Retrieving all API Versions allPages, err := apiversions.List(client).AllPages() if err != nil { panic("Unable to get API versions: %s", err) } allVersions, err := apiversions.ExtractAPIVersions(allPages) if err != nil { panic("Unable to extract API versions: %s", err) } for _, version := range versions { fmt.Printf("%+v\n", version) } Example of Retrieving an API Version version, err := apiversions.Get(client, "v3").Extract() if err != nil { panic("Unable to get API version: %s", err) } fmt.Printf("%+v\n", version) */ package apiversions golang-github-gophercloud-gophercloud-0.12.0/openstack/blockstorage/apiversions/errors.go000066400000000000000000000010271367513235700320320ustar00rootroot00000000000000package 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) } golang-github-gophercloud-gophercloud-0.12.0/openstack/blockstorage/apiversions/requests.go000066400000000000000000000006141367513235700323720ustar00rootroot00000000000000package 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)} }) } golang-github-gophercloud-gophercloud-0.12.0/openstack/blockstorage/apiversions/results.go000066400000000000000000000032451367513235700322230ustar00rootroot00000000000000package apiversions import ( "time" "github.com/gophercloud/gophercloud/pagination" ) // APIVersion represents an API version for Cinder. 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 represents the status of the API version. Status string `json:"status"` // Updated is the date the API version was updated. Updated time.Time `json:"updated"` // Version is the current version and microversion. 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 } // ExtractAPIVersion takes a List result and extracts a single requested // version, which is returned as an APIVersion func ExtractAPIVersion(r pagination.Page, v string) (*APIVersion, error) { allVersions, err := ExtractAPIVersions(r) if err != nil { return nil, err } for _, version := range allVersions { if version.ID == v { return &version, nil } } return nil, ErrVersionNotFound{} } golang-github-gophercloud-gophercloud-0.12.0/openstack/blockstorage/apiversions/testing/000077500000000000000000000000001367513235700316445ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/blockstorage/apiversions/testing/doc.go000066400000000000000000000000421367513235700327340ustar00rootroot00000000000000// apiversions_v1 package testing golang-github-gophercloud-gophercloud-0.12.0/openstack/blockstorage/apiversions/testing/fixtures.go000066400000000000000000000067671367513235700340640ustar00rootroot00000000000000package testing import ( "fmt" "net/http" "testing" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) const APIListResponse = ` { "versions": [ { "id": "v1.0", "links": [ { "href": "http://docs.openstack.org/", "rel": "describedby", "type": "text/html" }, { "href": "http://localhost:8776/v1/", "rel": "self" } ], "media-types": [ { "base": "application/json", "type": "application/vnd.openstack.volume+json;version=1" } ], "min_version": "", "status": "DEPRECATED", "updated": "2016-05-02T20:25:19Z", "version": "" }, { "id": "v2.0", "links": [ { "href": "http://docs.openstack.org/", "rel": "describedby", "type": "text/html" }, { "href": "http://localhost:8776/v2/", "rel": "self" } ], "media-types": [ { "base": "application/json", "type": "application/vnd.openstack.volume+json;version=1" } ], "min_version": "", "status": "SUPPORTED", "updated": "2014-06-28T12:20:21Z", "version": "" }, { "id": "v3.0", "links": [ { "href": "http://docs.openstack.org/", "rel": "describedby", "type": "text/html" }, { "href": "http://localhost:8776/v3/", "rel": "self" } ], "media-types": [ { "base": "application/json", "type": "application/vnd.openstack.volume+json;version=1" } ], "min_version": "3.0", "status": "CURRENT", "updated": "2016-02-08T12:20:21Z", "version": "3.27" } ] } ` const APIListOldResponse = ` { "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 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, APIListResponse) }) } func MockListOldResponse(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, APIListOldResponse) }) } requests_test.go000066400000000000000000000054701367513235700350340ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/blockstorage/apiversions/testingpackage testing import ( "testing" "time" "github.com/gophercloud/gophercloud/openstack/blockstorage/apiversions" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) func TestListVersions(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) expected := []apiversions.APIVersion{ { ID: "v1.0", Status: "DEPRECATED", Updated: time.Date(2016, 5, 2, 20, 25, 19, 0, time.UTC), }, { ID: "v2.0", Status: "SUPPORTED", Updated: time.Date(2014, 6, 28, 12, 20, 21, 0, time.UTC), }, { ID: "v3.0", MinVersion: "3.0", Status: "CURRENT", Updated: time.Date(2016, 2, 8, 12, 20, 21, 0, time.UTC), Version: "3.27", }, } th.AssertDeepEquals(t, expected, actual) } func TestListOldVersions(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() MockListOldResponse(t) allVersions, err := apiversions.List(client.ServiceClient()).AllPages() th.AssertNoErr(t, err) actual, err := apiversions.ExtractAPIVersions(allVersions) th.AssertNoErr(t, err) expected := []apiversions.APIVersion{ { ID: "v1.0", Status: "CURRENT", Updated: time.Date(2012, 1, 4, 11, 33, 21, 0, time.UTC), }, { ID: "v2.0", Status: "CURRENT", Updated: time.Date(2012, 11, 21, 11, 33, 21, 0, time.UTC), }, } th.AssertDeepEquals(t, expected, actual) } func TestGetVersion(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() MockListResponse(t) allVersions, err := apiversions.List(client.ServiceClient()).AllPages() th.AssertNoErr(t, err) actual, err := apiversions.ExtractAPIVersion(allVersions, "v3.0") th.AssertNoErr(t, err) expected := apiversions.APIVersion{ ID: "v3.0", MinVersion: "3.0", Status: "CURRENT", Updated: time.Date(2016, 2, 8, 12, 20, 21, 0, time.UTC), Version: "3.27", } th.AssertEquals(t, actual.ID, expected.ID) th.AssertEquals(t, actual.Status, expected.Status) th.AssertEquals(t, actual.Updated, expected.Updated) } func TestGetOldVersion(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() MockListOldResponse(t) allVersions, err := apiversions.List(client.ServiceClient()).AllPages() th.AssertNoErr(t, err) actual, err := apiversions.ExtractAPIVersion(allVersions, "v2.0") th.AssertNoErr(t, err) expected := apiversions.APIVersion{ ID: "v2.0", MinVersion: "", Status: "CURRENT", Updated: time.Date(2012, 11, 21, 11, 33, 21, 0, time.UTC), Version: "", } th.AssertEquals(t, actual.ID, expected.ID) th.AssertEquals(t, actual.Status, expected.Status) th.AssertEquals(t, actual.Updated, expected.Updated) } golang-github-gophercloud-gophercloud-0.12.0/openstack/blockstorage/apiversions/urls.go000066400000000000000000000004731367513235700315070ustar00rootroot00000000000000package apiversions import ( "strings" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack/utils" ) func listURL(c *gophercloud.ServiceClient) string { baseEndpoint, _ := utils.BaseEndpoint(c.Endpoint) endpoint := strings.TrimRight(baseEndpoint, "/") + "/" return endpoint } golang-github-gophercloud-gophercloud-0.12.0/openstack/blockstorage/extensions/000077500000000000000000000000001367513235700300245ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/blockstorage/extensions/backups/000077500000000000000000000000001367513235700314545ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/blockstorage/extensions/backups/doc.go000066400000000000000000000055231367513235700325550ustar00rootroot00000000000000/* Package backups provides information and interaction with backups in the OpenStack Block Storage service. A backup is a point in time copy of the data contained in an external storage volume, and can be controlled programmatically. Example to List Backups listOpts := backups.ListOpts{ VolumeID: "uuid", } allPages, err := backups.List(client, listOpts).AllPages() if err != nil { panic(err) } allBackups, err := backups.ExtractBackups(allPages) if err != nil { panic(err) } for _, backup := range allBackups { fmt.Println(backup) } Example to Create a Backup createOpts := backups.CreateOpts{ VolumeID: "uuid", Name: "my-backup", } backup, err := backups.Create(client, createOpts).Extract() if err != nil { panic(err) } fmt.Println(backup) Example to Update a Backup updateOpts := backups.UpdateOpts{ Name: "new-name", } backup, err := backups.Update(client, "uuid", updateOpts).Extract() if err != nil { panic(err) } fmt.Println(backup) Example to Restore a Backup to a Volume options := backups.RestoreOpts{ VolumeID: "1234", Name: "vol-001", } restore, err := backups.RestoreFromBackup(client, "uuid", options).Extract() if err != nil { panic(err) } fmt.Println(restore) Example to Delete a Backup err := backups.Delete(client, "uuid").ExtractErr() if err != nil { panic(err) } Example to Export a Backup export, err := backups.Export(client, "uuid").Extract() if err != nil { panic(err) } fmt.Println(export) Example to Import a Backup status := "available" availabilityZone := "region1b" host := "cinder-backup-host1" serviceMetadata := "volume_cf9bc6fa-c5bc-41f6-bc4e-6e76c0bea959/20200311192855/az_regionb_backup_b87bb1e5-0d4e-445e-a548-5ae742562bac" size := 1 objectCount := 2 container := "my-test-backup" service := "cinder.backup.drivers.swift.SwiftBackupDriver" backupURL, _ := json.Marshal(backups.ImportBackup{ ID: "d32019d3-bc6e-4319-9c1d-6722fc136a22", Status: &status, AvailabilityZone: &availabilityZone, VolumeID: "cf9bc6fa-c5bc-41f6-bc4e-6e76c0bea959", UpdatedAt: time.Date(2020, 3, 11, 19, 29, 8, 0, time.UTC), Host: &host, UserID: "93514e04-a026-4f60-8176-395c859501dd", ServiceMetadata: &serviceMetadata, Size: &size, ObjectCount: &objectCount, Container: &container, Service: &service, CreatedAt: time.Date(2020, 3, 11, 19, 25, 24, 0, time.UTC), DataTimestamp: time.Date(2020, 3, 11, 19, 25, 24, 0, time.UTC), ProjectID: "14f1c1f5d12b4755b94edef78ff8b325", }) options := backups.ImportOpts{ BackupService: "cinder.backup.drivers.swift.SwiftBackupDriver", BackupURL: backupURL, } backup, err := backups.Import(client, options).Extract() if err != nil { panic(err) } fmt.Println(backup) */ package backups golang-github-gophercloud-gophercloud-0.12.0/openstack/blockstorage/extensions/backups/requests.go000066400000000000000000000205131367513235700336570ustar00rootroot00000000000000package backups import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // CreateOptsBuilder allows extensions to add additional parameters to the // Create request. type CreateOptsBuilder interface { ToBackupCreateMap() (map[string]interface{}, error) } // CreateOpts contains options for creating a Backup. This object is passed to // the backups.Create function. For more information about these parameters, // see the Backup object. type CreateOpts struct { // VolumeID is the ID of the volume to create the backup from. VolumeID string `json:"volume_id" required:"true"` // Force will force the creation of a backup regardless of the //volume's status. Force bool `json:"force,omitempty"` // Name is the name of the backup. Name string `json:"name,omitempty"` // Description is the description of the backup. Description string `json:"description,omitempty"` // Metadata is metadata for the backup. // Requires microversion 3.43 or later. Metadata map[string]string `json:"metadata,omitempty"` // Container is a container to store the backup. Container string `json:"container,omitempty"` // Incremental is whether the backup should be incremental or not. Incremental bool `json:"incremental,omitempty"` // SnapshotID is the ID of a snapshot to backup. SnapshotID string `json:"snapshot_id,omitempty"` // AvailabilityZone is an availability zone to locate the volume or snapshot. // Requires microversion 3.51 or later. AvailabilityZone string `json:"availability_zone,omitempty"` } // ToBackupCreateMap assembles a request body based on the contents of a // CreateOpts. func (opts CreateOpts) ToBackupCreateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "backup") } // Create will create a new Backup based on the values in CreateOpts. To // extract the Backup object from the response, call the Extract method on the // CreateResult. func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToBackupCreateMap() if err != nil { r.Err = err return } resp, err := client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete will delete the existing Backup with the provided ID. func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { resp, err := client.Delete(deleteURL(client, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get retrieves the Backup with the provided ID. To extract the Backup // object from the response, call the Extract method on the GetResult. func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { resp, err := client.Get(getURL(client, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // ListOptsBuilder allows extensions to add additional parameters to the List // request. type ListOptsBuilder interface { ToBackupListQuery() (string, error) } type ListOpts struct { // AllTenants will retrieve backups of all tenants/projects. AllTenants bool `q:"all_tenants"` // Name will filter by the specified backup name. // This does not work in later microversions. Name string `q:"name"` // Status will filter by the specified status. // This does not work in later microversions. 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. // This does not work in later microversions. 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"` } // ToBackupListQuery formats a ListOpts into a query string. func (opts ListOpts) ToBackupListQuery() (string, error) { q, err := gophercloud.BuildQueryString(opts) return q.String(), err } // List returns Backups 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.ToBackupListQuery() if err != nil { return pagination.Pager{Err: err} } url += query } return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { return BackupPage{pagination.LinkedPageBase{PageResult: r}} }) } // UpdateOptsBuilder allows extensions to add additional parameters to // the Update request. type UpdateOptsBuilder interface { ToBackupUpdateMap() (map[string]interface{}, error) } // UpdateOpts contain options for updating an existing Backup. type UpdateOpts struct { // Name is the name of the backup. Name *string `json:"name,omitempty"` // Description is the description of the backup. Description *string `json:"description,omitempty"` // Metadata is metadata for the backup. // Requires microversion 3.43 or later. Metadata map[string]string `json:"metadata,omitempty"` } // ToBackupUpdateMap assembles a request body based on the contents of // an UpdateOpts. func (opts UpdateOpts) ToBackupUpdateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "") } // Update will update the Backup with provided information. To extract // the updated Backup from the response, call the Extract method on the // UpdateResult. // Requires microversion 3.9 or later. func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToBackupUpdateMap() if err != nil { r.Err = err return } resp, err := client.Put(updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // RestoreOpts contains options for restoring a Backup. This object is passed to // the backups.RestoreFromBackup function. type RestoreOpts struct { // VolumeID is the ID of the existing volume to restore the backup to. VolumeID string `json:"volume_id,omitempty"` // Name is the name of the new volume to restore the backup to. Name string `json:"name,omitempty"` } // ToRestoreMap assembles a request body based on the contents of a // RestoreOpts. func (opts RestoreOpts) ToRestoreMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "restore") } // RestoreFromBackup will restore a Backup to a volume based on the values in // RestoreOpts. To extract the Restore object from the response, call the // Extract method on the RestoreResult. func RestoreFromBackup(client *gophercloud.ServiceClient, id string, opts RestoreOpts) (r RestoreResult) { b, err := opts.ToRestoreMap() if err != nil { r.Err = err return } resp, err := client.Post(restoreURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Export will export a Backup information. To extract the Backup export record // object from the response, call the Extract method on the ExportResult. func Export(client *gophercloud.ServiceClient, id string) (r ExportResult) { resp, err := client.Get(exportURL(client, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // ImportOpts contains options for importing a Backup. This object is passed to // the backups.ImportBackup function. type ImportOpts BackupRecord // ToBackupImportMap assembles a request body based on the contents of a // ImportOpts. func (opts ImportOpts) ToBackupImportMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "backup-record") } // Import will import a Backup data to a backup based on the values in // ImportOpts. To extract the Backup object from the response, call the // Extract method on the ImportResult. func Import(client *gophercloud.ServiceClient, opts ImportOpts) (r ImportResult) { b, err := opts.ToBackupImportMap() if err != nil { r.Err = err return } resp, err := client.Post(importURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{201}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } golang-github-gophercloud-gophercloud-0.12.0/openstack/blockstorage/extensions/backups/results.go000066400000000000000000000233221367513235700335060ustar00rootroot00000000000000package backups import ( "encoding/json" "time" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // Backup contains all the information associated with a Cinder Backup. type Backup struct { // ID is the Unique identifier of the backup. ID string `json:"id"` // CreatedAt is the date the backup was created. CreatedAt time.Time `json:"-"` // UpdatedAt is the date the backup was updated. UpdatedAt time.Time `json:"-"` // Name is the display name of the backup. Name string `json:"name"` // Description is the description of the backup. Description string `json:"description"` // VolumeID is the ID of the Volume from which this backup was created. VolumeID string `json:"volume_id"` // SnapshotID is the ID of the snapshot from which this backup was created. SnapshotID string `json:"snapshot_id"` // Status is the status of the backup. Status string `json:"status"` // Size is the size of the backup, in GB. Size int `json:"size"` // Object Count is the number of objects in the backup. ObjectCount int `json:"object_count"` // Container is the container where the backup is stored. Container string `json:"container"` // HasDependentBackups is whether there are other backups // depending on this backup. HasDependentBackups bool `json:"has_dependent_backups"` // FailReason has the reason for the backup failure. FailReason string `json:"fail_reason"` // IsIncremental is whether this is an incremental backup. IsIncremental bool `json:"is_incremental"` // DataTimestamp is the time when the data on the volume was first saved. DataTimestamp time.Time `json:"-"` // ProjectID is the ID of the project that owns the backup. This is // an admin-only field. ProjectID string `json:"os-backup-project-attr:project_id"` // Metadata is metadata about the backup. // This requires microversion 3.43 or later. Metadata *map[string]string `json:"metadata"` // AvailabilityZone is the Availability Zone of the backup. // This requires microversion 3.51 or later. AvailabilityZone *string `json:"availability_zone"` } // 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 } // BackupPage is a pagination.Pager that is returned from a call to the List function. type BackupPage struct { pagination.LinkedPageBase } // UnmarshalJSON converts our JSON API response into our backup struct func (r *Backup) UnmarshalJSON(b []byte) error { type tmp Backup var s struct { tmp CreatedAt gophercloud.JSONRFC3339MilliNoZ `json:"created_at"` UpdatedAt gophercloud.JSONRFC3339MilliNoZ `json:"updated_at"` DataTimestamp gophercloud.JSONRFC3339MilliNoZ `json:"data_timestamp"` } err := json.Unmarshal(b, &s) if err != nil { return err } *r = Backup(s.tmp) r.CreatedAt = time.Time(s.CreatedAt) r.UpdatedAt = time.Time(s.UpdatedAt) r.DataTimestamp = time.Time(s.DataTimestamp) return err } // IsEmpty returns true if a BackupPage contains no Backups. func (r BackupPage) IsEmpty() (bool, error) { volumes, err := ExtractBackups(r) return len(volumes) == 0, err } func (page BackupPage) NextPageURL() (string, error) { var s struct { Links []gophercloud.Link `json:"backups_links"` } err := page.ExtractInto(&s) if err != nil { return "", err } return gophercloud.ExtractNextURL(s.Links) } // ExtractBackups extracts and returns Backups. It is used while iterating over a backups.List call. func ExtractBackups(r pagination.Page) ([]Backup, error) { var s []Backup err := ExtractBackupsInto(r, &s) return s, 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 Backup object out of the commonResult object. func (r commonResult) Extract() (*Backup, error) { var s Backup err := r.ExtractInto(&s) return &s, err } func (r commonResult) ExtractInto(v interface{}) error { return r.Result.ExtractIntoStructPtr(v, "backup") } func ExtractBackupsInto(r pagination.Page, v interface{}) error { return r.(BackupPage).Result.ExtractIntoSlicePtr(v, "backups") } // RestoreResult contains the response body and error from a restore request. type RestoreResult struct { commonResult } // Restore contains all the information associated with a Cinder Backup restore // response. type Restore struct { // BackupID is the Unique identifier of the backup. BackupID string `json:"backup_id"` // VolumeID is the Unique identifier of the volume. VolumeID string `json:"volume_id"` // Name is the name of the volume, where the backup was restored to. VolumeName string `json:"volume_name"` } // Extract will get the Backup restore object out of the RestoreResult object. func (r RestoreResult) Extract() (*Restore, error) { var s Restore err := r.ExtractInto(&s) return &s, err } func (r RestoreResult) ExtractInto(v interface{}) error { return r.Result.ExtractIntoStructPtr(v, "restore") } // ExportResult contains the response body and error from an export request. type ExportResult struct { commonResult } // BackupRecord contains an information about a backup backend storage. type BackupRecord struct { // The service used to perform the backup. BackupService string `json:"backup_service"` // An identifier string to locate the backup. BackupURL []byte `json:"backup_url"` } // Extract will get the Backup record object out of the ExportResult object. func (r ExportResult) Extract() (*BackupRecord, error) { var s BackupRecord err := r.ExtractInto(&s) return &s, err } func (r ExportResult) ExtractInto(v interface{}) error { return r.Result.ExtractIntoStructPtr(v, "backup-record") } // ImportResponse struct contains the response of the Backup Import action. type ImportResponse struct { ID string `json:"id"` Name string `json:"name"` } // ImportResult contains the response body and error from an import request. type ImportResult struct { gophercloud.Result } // Extract will get the Backup object out of the commonResult object. func (r ImportResult) Extract() (*ImportResponse, error) { var s ImportResponse err := r.ExtractInto(&s) return &s, err } func (r ImportResult) ExtractInto(v interface{}) error { return r.Result.ExtractIntoStructPtr(v, "backup") } // ImportBackup contains all the information to import a Cinder Backup. type ImportBackup struct { ID string `json:"id"` CreatedAt time.Time `json:"-"` UpdatedAt time.Time `json:"-"` VolumeID string `json:"volume_id"` SnapshotID *string `json:"snapshot_id"` Status *string `json:"status"` Size *int `json:"size"` ObjectCount *int `json:"object_count"` Container *string `json:"container"` ServiceMetadata *string `json:"service_metadata"` Service *string `json:"service"` Host *string `json:"host"` UserID string `json:"user_id"` DeletedAt time.Time `json:"-"` DataTimestamp time.Time `json:"-"` TempSnapshotID *string `json:"temp_snapshot_id"` TempVolumeID *string `json:"temp_volume_id"` RestoreVolumeID *string `json:"restore_volume_id"` NumDependentBackups *int `json:"num_dependent_backups"` EncryptionKeyID *string `json:"encryption_key_id"` ParentID *string `json:"parent_id"` Deleted bool `json:"deleted"` DisplayName *string `json:"display_name"` DisplayDescription *string `json:"display_description"` DriverInfo interface{} `json:"driver_info"` FailReason *string `json:"fail_reason"` ProjectID string `json:"project_id"` Metadata map[string]string `json:"metadata"` AvailabilityZone *string `json:"availability_zone"` } // UnmarshalJSON converts our JSON API response into our backup struct func (r *ImportBackup) UnmarshalJSON(b []byte) error { type tmp ImportBackup var s struct { tmp CreatedAt time.Time `json:"created_at"` UpdatedAt time.Time `json:"updated_at"` DeletedAt time.Time `json:"deleted_at"` DataTimestamp time.Time `json:"data_timestamp"` } err := json.Unmarshal(b, &s) if err != nil { return err } *r = ImportBackup(s.tmp) r.CreatedAt = time.Time(s.CreatedAt) r.UpdatedAt = time.Time(s.UpdatedAt) r.DeletedAt = time.Time(s.DeletedAt) r.DataTimestamp = time.Time(s.DataTimestamp) return err } // MarshalJSON converts our struct request into JSON backup import request func (r ImportBackup) MarshalJSON() ([]byte, error) { type b ImportBackup type ext struct { CreatedAt *string `json:"created_at"` UpdatedAt *string `json:"updated_at"` DeletedAt *string `json:"deleted_at"` DataTimestamp *string `json:"data_timestamp"` } type tmp struct { b ext } var t ext if r.CreatedAt != (time.Time{}) { v := r.CreatedAt.Format(time.RFC3339) t.CreatedAt = &v } if r.UpdatedAt != (time.Time{}) { v := r.UpdatedAt.Format(time.RFC3339) t.UpdatedAt = &v } if r.DeletedAt != (time.Time{}) { v := r.DeletedAt.Format(time.RFC3339) t.DeletedAt = &v } if r.DataTimestamp != (time.Time{}) { v := r.DataTimestamp.Format(time.RFC3339) t.DataTimestamp = &v } if r.Metadata == nil { r.Metadata = make(map[string]string) } s := tmp{ b(r), t, } return json.Marshal(s) } golang-github-gophercloud-gophercloud-0.12.0/openstack/blockstorage/extensions/backups/testing/000077500000000000000000000000001367513235700331315ustar00rootroot00000000000000fixtures.go000066400000000000000000000177461367513235700352710ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/blockstorage/extensions/backups/testingpackage testing import ( "encoding/json" "fmt" "net/http" "testing" "time" "github.com/gophercloud/gophercloud/openstack/blockstorage/extensions/backups" th "github.com/gophercloud/gophercloud/testhelper" fake "github.com/gophercloud/gophercloud/testhelper/client" ) const ListResponse = ` { "backups": [ { "id": "289da7f8-6440-407c-9fb4-7db01ec49164", "name": "backup-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": "backup-002", "volume_id": "76b8950a-8594-4e5b-8dce-0dfa9c696358", "description": "Weekly Backup", "status": "available", "size": 25, "created_at": "2017-05-30T03:35:03.000000" } ], "backups_links": [ { "href": "%s/backups?marker=1", "rel": "next" } ] } ` const GetResponse = ` { "backup": { "id": "d32019d3-bc6e-4319-9c1d-6722fc136a22", "name": "backup-001", "description": "Daily backup", "volume_id": "521752a6-acf6-4b2d-bc7a-119f9148cd8c", "status": "available", "size": 30, "created_at": "2017-05-30T03:35:03.000000" } } ` const CreateRequest = ` { "backup": { "volume_id": "1234", "name": "backup-001" } } ` const CreateResponse = ` { "backup": { "volume_id": "1234", "name": "backup-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" } } ` const RestoreRequest = ` { "restore": { "name": "vol-001", "volume_id": "1234" } } ` const RestoreResponse = ` { "restore": { "backup_id": "d32019d3-bc6e-4319-9c1d-6722fc136a22", "volume_id": "1234", "volume_name": "vol-001" } } ` const ExportResponse = ` { "backup-record": { "backup_service": "cinder.backup.drivers.swift.SwiftBackupDriver", "backup_url": "eyJpZCI6ImQzMjAxOWQzLWJjNmUtNDMxOS05YzFkLTY3MjJmYzEzNmEyMiIsInZvbHVtZV9pZCI6ImNmOWJjNmZhLWM1YmMtNDFmNi1iYzRlLTZlNzZjMGJlYTk1OSIsInNuYXBzaG90X2lkIjpudWxsLCJzdGF0dXMiOiJhdmFpbGFibGUiLCJzaXplIjoxLCJvYmplY3RfY291bnQiOjIsImNvbnRhaW5lciI6Im15LXRlc3QtYmFja3VwIiwic2VydmljZV9tZXRhZGF0YSI6InZvbHVtZV9jZjliYzZmYS1jNWJjLTQxZjYtYmM0ZS02ZTc2YzBiZWE5NTkvMjAyMDAzMTExOTI4NTUvYXpfcmVnaW9uYl9iYWNrdXBfYjg3YmIxZTUtMGQ0ZS00NDVlLWE1NDgtNWFlNzQyNTYyYmFjIiwic2VydmljZSI6ImNpbmRlci5iYWNrdXAuZHJpdmVycy5zd2lmdC5Td2lmdEJhY2t1cERyaXZlciIsImhvc3QiOiJjaW5kZXItYmFja3VwLWhvc3QxIiwidXNlcl9pZCI6IjkzNTE0ZTA0LWEwMjYtNGY2MC04MTc2LTM5NWM4NTk1MDFkZCIsInRlbXBfc25hcHNob3RfaWQiOm51bGwsInRlbXBfdm9sdW1lX2lkIjpudWxsLCJyZXN0b3JlX3ZvbHVtZV9pZCI6bnVsbCwibnVtX2RlcGVuZGVudF9iYWNrdXBzIjpudWxsLCJlbmNyeXB0aW9uX2tleV9pZCI6bnVsbCwicGFyZW50X2lkIjpudWxsLCJkZWxldGVkIjpmYWxzZSwiZGlzcGxheV9uYW1lIjpudWxsLCJkaXNwbGF5X2Rlc2NyaXB0aW9uIjpudWxsLCJkcml2ZXJfaW5mbyI6bnVsbCwiZmFpbF9yZWFzb24iOm51bGwsInByb2plY3RfaWQiOiIxNGYxYzFmNWQxMmI0NzU1Yjk0ZWRlZjc4ZmY4YjMyNSIsIm1ldGFkYXRhIjp7fSwiYXZhaWxhYmlsaXR5X3pvbmUiOiJyZWdpb24xYiIsImNyZWF0ZWRfYXQiOiIyMDIwLTAzLTExVDE5OjI1OjI0WiIsInVwZGF0ZWRfYXQiOiIyMDIwLTAzLTExVDE5OjI5OjA4WiIsImRlbGV0ZWRfYXQiOm51bGwsImRhdGFfdGltZXN0YW1wIjoiMjAyMC0wMy0xMVQxOToyNToyNFoifQ==" } } ` const ImportRequest = ExportResponse const ImportResponse = ` { "backup": { "id": "d32019d3-bc6e-4319-9c1d-6722fc136a22", "links": [ { "href": "https://volume/v2/14f1c1f5d12b4755b94edef78ff8b325/backups/d32019d3-bc6e-4319-9c1d-6722fc136a22", "rel": "self" }, { "href": "https://volume/14f1c1f5d12b4755b94edef78ff8b325/backups/d32019d3-bc6e-4319-9c1d-6722fc136a22", "rel": "bookmark" } ], "name": null } } ` var ( status = "available" availabilityZone = "region1b" host = "cinder-backup-host1" serviceMetadata = "volume_cf9bc6fa-c5bc-41f6-bc4e-6e76c0bea959/20200311192855/az_regionb_backup_b87bb1e5-0d4e-445e-a548-5ae742562bac" size = 1 objectCount = 2 container = "my-test-backup" service = "cinder.backup.drivers.swift.SwiftBackupDriver" backupImport = backups.ImportBackup{ ID: "d32019d3-bc6e-4319-9c1d-6722fc136a22", Status: &status, AvailabilityZone: &availabilityZone, VolumeID: "cf9bc6fa-c5bc-41f6-bc4e-6e76c0bea959", UpdatedAt: time.Date(2020, 3, 11, 19, 29, 8, 0, time.UTC), Host: &host, UserID: "93514e04-a026-4f60-8176-395c859501dd", ServiceMetadata: &serviceMetadata, Size: &size, ObjectCount: &objectCount, Container: &container, Service: &service, CreatedAt: time.Date(2020, 3, 11, 19, 25, 24, 0, time.UTC), DataTimestamp: time.Date(2020, 3, 11, 19, 25, 24, 0, time.UTC), ProjectID: "14f1c1f5d12b4755b94edef78ff8b325", Metadata: make(map[string]string), } backupURL, _ = json.Marshal(backupImport) ) func MockListResponse(t *testing.T) { th.Mux.HandleFunc("/backups", 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, ListResponse, th.Server.URL) case "1": fmt.Fprintf(w, `{"backups": []}`) default: t.Fatalf("Unexpected marker: [%s]", marker) } }) } func MockGetResponse(t *testing.T) { th.Mux.HandleFunc("/backups/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) }) } func MockCreateResponse(t *testing.T) { th.Mux.HandleFunc("/backups", 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.StatusAccepted) fmt.Fprintf(w, CreateResponse) }) } func MockRestoreResponse(t *testing.T) { th.Mux.HandleFunc("/backups/d32019d3-bc6e-4319-9c1d-6722fc136a22/restore", 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, RestoreRequest) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusAccepted) fmt.Fprintf(w, RestoreResponse) }) } func MockDeleteResponse(t *testing.T) { th.Mux.HandleFunc("/backups/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 MockExportResponse(t *testing.T) { th.Mux.HandleFunc("/backups/d32019d3-bc6e-4319-9c1d-6722fc136a22/export_record", 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().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ExportResponse) }) } func MockImportResponse(t *testing.T) { th.Mux.HandleFunc("/backups/import_record", 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, ImportRequest) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) fmt.Fprintf(w, ImportResponse) }) } requests_test.go000066400000000000000000000072641367513235700363240ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/blockstorage/extensions/backups/testingpackage testing import ( "encoding/json" "testing" "time" "github.com/gophercloud/gophercloud/openstack/blockstorage/extensions/backups" "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 backups.List(client.ServiceClient(), &backups.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { count++ actual, err := backups.ExtractBackups(page) if err != nil { t.Errorf("Failed to extract backups: %v", err) return false, err } expected := []backups.Backup{ { ID: "289da7f8-6440-407c-9fb4-7db01ec49164", Name: "backup-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: "backup-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 := backups.Get(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").Extract() th.AssertNoErr(t, err) th.AssertEquals(t, v.Name, "backup-001") th.AssertEquals(t, v.ID, "d32019d3-bc6e-4319-9c1d-6722fc136a22") } func TestCreate(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() MockCreateResponse(t) options := backups.CreateOpts{VolumeID: "1234", Name: "backup-001"} n, err := backups.Create(client.ServiceClient(), options).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, n.VolumeID, "1234") th.AssertEquals(t, n.Name, "backup-001") th.AssertEquals(t, n.ID, "d32019d3-bc6e-4319-9c1d-6722fc136a22") } func TestRestore(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() MockRestoreResponse(t) options := backups.RestoreOpts{VolumeID: "1234", Name: "vol-001"} n, err := backups.RestoreFromBackup(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22", options).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, n.VolumeID, "1234") th.AssertEquals(t, n.VolumeName, "vol-001") th.AssertEquals(t, n.BackupID, "d32019d3-bc6e-4319-9c1d-6722fc136a22") } func TestDelete(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() MockDeleteResponse(t) res := backups.Delete(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22") th.AssertNoErr(t, res.Err) } func TestExport(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() MockExportResponse(t) n, err := backups.Export(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").Extract() th.AssertNoErr(t, err) th.AssertEquals(t, n.BackupService, "cinder.backup.drivers.swift.SwiftBackupDriver") th.AssertDeepEquals(t, n.BackupURL, backupURL) tmp := backups.ImportBackup{} err = json.Unmarshal(backupURL, &tmp) th.AssertNoErr(t, err) th.AssertDeepEquals(t, tmp, backupImport) } func TestImport(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() MockImportResponse(t) options := backups.ImportOpts{ BackupService: "cinder.backup.drivers.swift.SwiftBackupDriver", BackupURL: backupURL, } n, err := backups.Import(client.ServiceClient(), options).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, n.ID, "d32019d3-bc6e-4319-9c1d-6722fc136a22") } golang-github-gophercloud-gophercloud-0.12.0/openstack/blockstorage/extensions/backups/urls.go000066400000000000000000000015711367513235700327740ustar00rootroot00000000000000package backups import "github.com/gophercloud/gophercloud" func createURL(c *gophercloud.ServiceClient) string { return c.ServiceURL("backups") } func deleteURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL("backups", id) } func getURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL("backups", id) } func listURL(c *gophercloud.ServiceClient) string { return c.ServiceURL("backups") } func updateURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL("backups", id) } func restoreURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL("backups", id, "restore") } func exportURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL("backups", id, "export_record") } func importURL(c *gophercloud.ServiceClient) string { return c.ServiceURL("backups", "import_record") } golang-github-gophercloud-gophercloud-0.12.0/openstack/blockstorage/extensions/quotasets/000077500000000000000000000000001367513235700320545ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/blockstorage/extensions/quotasets/doc.go000066400000000000000000000015251367513235700331530ustar00rootroot00000000000000/* 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 golang-github-gophercloud-gophercloud-0.12.0/openstack/blockstorage/extensions/quotasets/requests.go000066400000000000000000000070651367513235700342660ustar00rootroot00000000000000package 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) { resp, err := client.Get(getURL(client, projectID), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // GetDefaults returns public data about the project's default block storage quotas. func GetDefaults(client *gophercloud.ServiceClient, projectID string) (r GetResult) { resp, err := client.Get(getDefaultsURL(client, projectID), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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)) resp, err := client.Get(u, &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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 } resp, err := client.Put(updateURL(client, projectID), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // 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) { resp, err := client.Delete(updateURL(client, projectID), &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } golang-github-gophercloud-gophercloud-0.12.0/openstack/blockstorage/extensions/quotasets/results.go000066400000000000000000000134141367513235700341070ustar00rootroot00000000000000package 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"` // Groups is the number of groups that are allowed for each project. Groups int `json:"groups,omitempty"` } // 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"` // Groups is the number of groups that are allowed for each project. // Note: allocated attribute is available only when nested quota is // enabled. Groups QuotaUsage `json:"groups"` } // 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 } golang-github-gophercloud-gophercloud-0.12.0/openstack/blockstorage/extensions/quotasets/testing/000077500000000000000000000000001367513235700335315ustar00rootroot00000000000000doc.go000066400000000000000000000000501367513235700345410ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/blockstorage/extensions/quotasets/testing// quotasets unit tests package testing fixtures.go000066400000000000000000000112341367513235700356530ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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, "groups": 14 } }` var getExpectedQuotaSet = quotasets.QuotaSet{ Volumes: 8, Snapshots: 9, Gigabytes: 10, PerVolumeGigabytes: 11, Backups: 12, BackupGigabytes: 13, Groups: 14, } 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 }, "groups" : { "in_use": 40, "limit": 41, "reserved": 42 } } }` 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}, Groups: quotasets.QuotaUsage{InUse: 40, Limit: 41, Reserved: 42}, } var fullUpdateExpectedJSONBody = ` { "quota_set": { "volumes": 8, "snapshots": 9, "gigabytes": 10, "per_volume_gigabytes": 11, "backups": 12, "backup_gigabytes": 13, "groups": 14 } }` 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), Groups: gophercloud.IntToPointer(14), } var fullUpdateExpectedQuotaSet = quotasets.QuotaSet{ Volumes: 8, Snapshots: 9, Gigabytes: 10, PerVolumeGigabytes: 11, Backups: 12, BackupGigabytes: 13, Groups: 14, } 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.go000066400000000000000000000050411367513235700367130ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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) } golang-github-gophercloud-gophercloud-0.12.0/openstack/blockstorage/extensions/quotasets/urls.go000066400000000000000000000010651367513235700333720ustar00rootroot00000000000000package 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) } golang-github-gophercloud-gophercloud-0.12.0/openstack/blockstorage/extensions/schedulerhints/000077500000000000000000000000001367513235700330505ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/blockstorage/extensions/schedulerhints/doc.go000066400000000000000000000022061367513235700341440ustar00rootroot00000000000000/* Package schedulerhints extends the volume create request with the ability to specify additional parameters which determine where the volume will be created in the OpenStack cloud. Example to Place Volume B on a Different Host than Volume A schedulerHints := schedulerhints.SchedulerHints{ DifferentHost: []string{ "volume-a-uuid", } } volumeCreateOpts := volumes.CreateOpts{ Name: "volume_b", Size: 10, } createOpts := schedulerhints.CreateOptsExt{ VolumeCreateOptsBuilder: volumeCreateOpts, SchedulerHints: schedulerHints, } volume, err := volumes.Create(computeClient, createOpts).Extract() if err != nil { panic(err) } Example to Place Volume B on the Same Host as Volume A schedulerHints := schedulerhints.SchedulerHints{ SameHost: []string{ "volume-a-uuid", } } volumeCreateOpts := volumes.CreateOpts{ Name: "volume_b", Size: 10 } createOpts := schedulerhints.CreateOptsExt{ VolumeCreateOptsBuilder: volumeCreateOpts, SchedulerHints: schedulerHints, } volume, err := volumes.Create(computeClient, createOpts).Extract() if err != nil { panic(err) } */ package schedulerhints requests.go000066400000000000000000000066611367513235700352040ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/blockstorage/extensions/schedulerhintspackage schedulerhints import ( "regexp" "github.com/gophercloud/gophercloud" ) // SchedulerHints represents a set of scheduling hints that are passed to the // OpenStack scheduler. type SchedulerHints struct { // DifferentHost will place the volume on a different back-end that does not // host the given volumes. DifferentHost []string // SameHost will place the volume on a back-end that hosts the given volumes. SameHost []string // LocalToInstance will place volume on same host on a given instance LocalToInstance string // Query is a conditional statement that results in back-ends able to // host the volume. Query string // AdditionalProperies are arbitrary key/values that are not validated by nova. AdditionalProperties map[string]interface{} } // VolumeCreateOptsBuilder allows extensions to add additional parameters to the // Create request. type VolumeCreateOptsBuilder interface { ToVolumeCreateMap() (map[string]interface{}, error) } // CreateOptsBuilder builds the scheduler hints into a serializable format. type CreateOptsBuilder interface { ToVolumeSchedulerHintsCreateMap() (map[string]interface{}, error) } // ToVolumeSchedulerHintsMap builds the scheduler hints into a serializable format. func (opts SchedulerHints) ToVolumeSchedulerHintsCreateMap() (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 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 } if opts.LocalToInstance != "" { if !uuidRegex.MatchString(opts.LocalToInstance) { err := gophercloud.ErrInvalidInput{} err.Argument = "schedulerhints.SchedulerHints.LocalToInstance" err.Value = opts.LocalToInstance err.Info = "The instance must be in UUID format." return nil, err } sh["local_to_instance"] = opts.LocalToInstance } if opts.Query != "" { sh["query"] = opts.Query } 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 { VolumeCreateOptsBuilder // SchedulerHints provides a set of hints to the scheduler. SchedulerHints CreateOptsBuilder } // ToVolumeCreateMap adds the SchedulerHints option to the base volume creation options. func (opts CreateOptsExt) ToVolumeCreateMap() (map[string]interface{}, error) { base, err := opts.VolumeCreateOptsBuilder.ToVolumeCreateMap() if err != nil { return nil, err } schedulerHints, err := opts.SchedulerHints.ToVolumeSchedulerHintsCreateMap() if err != nil { return nil, err } if len(schedulerHints) == 0 { return base, nil } base["OS-SCH-HNT:scheduler_hints"] = schedulerHints return base, nil } testing/000077500000000000000000000000001367513235700344465ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/blockstorage/extensions/schedulerhintsdoc.go000066400000000000000000000000551367513235700355420ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/blockstorage/extensions/schedulerhints/testing// schedulerhints unit tests package testing requests_test.go000066400000000000000000000027401367513235700377120ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/blockstorage/extensions/schedulerhints/testingpackage testing import ( "testing" "github.com/gophercloud/gophercloud/openstack/blockstorage/extensions/schedulerhints" "github.com/gophercloud/gophercloud/openstack/blockstorage/v3/volumes" th "github.com/gophercloud/gophercloud/testhelper" ) func TestCreateOpts(t *testing.T) { base := volumes.CreateOpts{ Size: 10, Name: "testvolume", } schedulerHints := schedulerhints.SchedulerHints{ DifferentHost: []string{ "a0cf03a5-d921-4877-bb5c-86d26cf818e1", "8c19174f-4220-44f0-824a-cd1eeef10287", }, SameHost: []string{ "a0cf03a5-d921-4877-bb5c-86d26cf818e1", "8c19174f-4220-44f0-824a-cd1eeef10287", }, LocalToInstance: "0ffb2c1b-d621-4fc1-9ae4-88d99c088ff6", AdditionalProperties: map[string]interface{}{"mark": "a0cf03a5-d921-4877-bb5c-86d26cf818e1"}, } ext := schedulerhints.CreateOptsExt{ VolumeCreateOptsBuilder: base, SchedulerHints: schedulerHints, } expected := ` { "volume": { "size": 10, "name": "testvolume" }, "OS-SCH-HNT:scheduler_hints": { "different_host": [ "a0cf03a5-d921-4877-bb5c-86d26cf818e1", "8c19174f-4220-44f0-824a-cd1eeef10287" ], "same_host": [ "a0cf03a5-d921-4877-bb5c-86d26cf818e1", "8c19174f-4220-44f0-824a-cd1eeef10287" ], "local_to_instance": "0ffb2c1b-d621-4fc1-9ae4-88d99c088ff6", "mark": "a0cf03a5-d921-4877-bb5c-86d26cf818e1" } } ` actual, err := ext.ToVolumeCreateMap() th.AssertNoErr(t, err) th.CheckJSONEquals(t, expected, actual) } golang-github-gophercloud-gophercloud-0.12.0/openstack/blockstorage/extensions/schedulerstats/000077500000000000000000000000001367513235700330615ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/blockstorage/extensions/schedulerstats/doc.go000066400000000000000000000007051367513235700341570ustar00rootroot00000000000000/* 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.go000066400000000000000000000024041367513235700352040ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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.go000066400000000000000000000065251367513235700350420ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/blockstorage/extensions/schedulerstatspackage schedulerstats import ( "encoding/json" "math" "strconv" "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:"-"` 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"` MaxOverSubscriptionRatio interface{} `json:"max_over_subscription_ratio"` 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) if s.MaxOverSubscriptionRatio != nil { switch t := s.MaxOverSubscriptionRatio.(type) { case float64: r.MaxOverSubscriptionRatio = strconv.FormatFloat(t, 'f', -1, 64) case string: r.MaxOverSubscriptionRatio = t } } 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/000077500000000000000000000000001367513235700344575ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/blockstorage/extensions/schedulerstatsfixtures.go000066400000000000000000000065151367513235700366660ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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, "max_over_subscription_ratio": "1.5", "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, "max_over_subscription_ratio": 1.5, "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, MaxOverSubscriptionRatio: "1.5", 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, MaxOverSubscriptionRatio: "1.5", 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.go000066400000000000000000000017701367513235700377250ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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.go000066400000000000000000000002741367513235700343210ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/blockstorage/extensions/schedulerstatspackage schedulerstats import "github.com/gophercloud/gophercloud" func storagePoolsListURL(c *gophercloud.ServiceClient) string { return c.ServiceURL("scheduler-stats", "get_pools") } golang-github-gophercloud-gophercloud-0.12.0/openstack/blockstorage/extensions/services/000077500000000000000000000000001367513235700316475ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/blockstorage/extensions/services/doc.go000066400000000000000000000006731367513235700327510ustar00rootroot00000000000000/* 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 golang-github-gophercloud-gophercloud-0.12.0/openstack/blockstorage/extensions/services/requests.go000066400000000000000000000022401367513235700340470ustar00rootroot00000000000000package 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)} }) } golang-github-gophercloud-gophercloud-0.12.0/openstack/blockstorage/extensions/services/results.go000066400000000000000000000040661367513235700337050ustar00rootroot00000000000000package 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 } golang-github-gophercloud-gophercloud-0.12.0/openstack/blockstorage/extensions/services/testing/000077500000000000000000000000001367513235700333245ustar00rootroot00000000000000fixtures.go000066400000000000000000000051601367513235700354470ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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.go000066400000000000000000000020021367513235700365000ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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) } } golang-github-gophercloud-gophercloud-0.12.0/openstack/blockstorage/extensions/services/urls.go000066400000000000000000000002311367513235700331570ustar00rootroot00000000000000package services import "github.com/gophercloud/gophercloud" func listURL(c *gophercloud.ServiceClient) string { return c.ServiceURL("os-services") } golang-github-gophercloud-gophercloud-0.12.0/openstack/blockstorage/extensions/volumeactions/000077500000000000000000000000001367513235700327145ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/blockstorage/extensions/volumeactions/doc.go000066400000000000000000000043211367513235700340100ustar00rootroot00000000000000/* 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) } Example of Setting a Volume's Bootable status options := volumeactions.BootableOpts{ Bootable: true, } err := volumeactions.SetBootable(client, volume.ID, options).ExtractErr() if err != nil { panic(err) } */ package volumeactions requests.go000066400000000000000000000302051367513235700350370ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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 } resp, err := client.Post(actionURL(client, id), b, nil, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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{})} resp, err := client.Post(actionURL(client, id), b, nil, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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 } resp, err := client.Post(actionURL(client, id), b, nil, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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{})} resp, err := client.Post(actionURL(client, id), b, nil, &gophercloud.RequestOpts{ OkCodes: []int{200, 201, 202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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{})} resp, err := client.Post(actionURL(client, id), b, nil, &gophercloud.RequestOpts{ OkCodes: []int{200, 201, 202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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 } resp, err := client.Post(actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201, 202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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 } resp, err := client.Post(actionURL(client, id), b, nil, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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 } resp, err := client.Post(actionURL(client, id), b, nil, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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"` // Visibility defines who can see/use the image. // supported since 3.1 microversion Visibility string `json:"visibility,omitempty"` // whether the image is not deletable. // supported since 3.1 microversion Protected bool `json:"protected,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 } resp, err := client.Post(actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // ForceDelete will delete the volume regardless of state. func ForceDelete(client *gophercloud.ServiceClient, id string) (r ForceDeleteResult) { resp, err := client.Post(actionURL(client, id), map[string]interface{}{"os-force_delete": ""}, nil, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // ImageMetadataOptsBuilder allows extensions to add additional parameters to the // ImageMetadataRequest request. type ImageMetadataOptsBuilder interface { ToImageMetadataMap() (map[string]interface{}, error) } // ImageMetadataOpts contains options for setting image metadata to a volume. type ImageMetadataOpts struct { // The image metadata to add to the volume as a set of metadata key and value pairs. Metadata map[string]string `json:"metadata"` } // ToImageMetadataMap assembles a request body based on the contents of a // ImageMetadataOpts. func (opts ImageMetadataOpts) ToImageMetadataMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "os-set_image_metadata") } // SetImageMetadata will set image metadata on a volume based on the values in ImageMetadataOptsBuilder. func SetImageMetadata(client *gophercloud.ServiceClient, id string, opts ImageMetadataOptsBuilder) (r SetImageMetadataResult) { b, err := opts.ToImageMetadataMap() if err != nil { r.Err = err return } resp, err := client.Post(actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // BootableOpts contains options for setting bootable status to a volume. type BootableOpts struct { // Enables or disables the bootable attribute. You can boot an instance from a bootable volume. Bootable bool `json:"bootable"` } // ToBootableMap assembles a request body based on the contents of a // BootableOpts. func (opts BootableOpts) ToBootableMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "os-set_bootable") } // SetBootable will set bootable status on a volume based on the values in BootableOpts func SetBootable(client *gophercloud.ServiceClient, id string, opts BootableOpts) (r SetBootableResult) { b, err := opts.ToBootableMap() if err != nil { r.Err = err return } resp, err := client.Post(actionURL(client, id), b, nil, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } results.go000066400000000000000000000126101367513235700346650ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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 } // SetImageMetadataResult contains the response body and error from an SetImageMetadata // request. type SetImageMetadataResult struct { gophercloud.ErrResult } // SetBootableResult contains the response body and error from a SetBootable // request. type SetBootableResult struct { gophercloud.ErrResult } // 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"` // Visibility defines who can see/use the image. // supported since 3.1 microversion Visibility string `json:"visibility"` // whether the image is not deletable. // supported since 3.1 microversion Protected bool `json:"protected"` // 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/000077500000000000000000000000001367513235700343125ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/blockstorage/extensions/volumeactionsdoc.go000066400000000000000000000000541367513235700354050ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/blockstorage/extensions/volumeactions/testing// volumeactions unit tests package testing fixtures.go000066400000000000000000000216631367513235700365220ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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) }) } func MockSetImageMetadataResponse(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-set_image_metadata": { "metadata": { "label": "test" } } } `) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, `{}`) }) } func MockSetBootableResponse(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-set_bootable": { "bootable": true } } `) w.Header().Add("Content-Type", "application/json") w.Header().Add("Content-Length", "0") w.WriteHeader(http.StatusOK) }) } requests_test.go000066400000000000000000000121471367513235700375600ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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) } func TestSetImageMetadata(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() MockSetImageMetadataResponse(t) options := &volumeactions.ImageMetadataOpts{ Metadata: map[string]string{ "label": "test", }, } err := volumeactions.SetImageMetadata(client.ServiceClient(), "cd281d77-8217-4830-be95-9528227c105c", options).ExtractErr() th.AssertNoErr(t, err) } func TestSetBootable(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() MockSetBootableResponse(t) options := volumeactions.BootableOpts{ Bootable: true, } err := volumeactions.SetBootable(client.ServiceClient(), "cd281d77-8217-4830-be95-9528227c105c", options).ExtractErr() th.AssertNoErr(t, err) } golang-github-gophercloud-gophercloud-0.12.0/openstack/blockstorage/extensions/volumeactions/urls.go000066400000000000000000000002651367513235700342330ustar00rootroot00000000000000package volumeactions import "github.com/gophercloud/gophercloud" func actionURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL("volumes", id, "action") } golang-github-gophercloud-gophercloud-0.12.0/openstack/blockstorage/extensions/volumetenants/000077500000000000000000000000001367513235700327305ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/blockstorage/extensions/volumetenants/doc.go000066400000000000000000000011031367513235700340170ustar00rootroot00000000000000/* 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.go000066400000000000000000000003471367513235700347050ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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.12.0/openstack/blockstorage/extensions/volumetransfers/000077500000000000000000000000001367513235700332635ustar00rootroot00000000000000doc.go000066400000000000000000000031701367513235700343010ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/blockstorage/extensions/volumetransfers/* Package volumetransfers provides an interaction with volume transfers in the OpenStack Block Storage service. A volume transfer allows to transfer volumes between projects withing the same OpenStack region. Example to List all Volume Transfer requests being an OpenStack admin listOpts := &volumetransfers.ListOpts{ // this option is available only for OpenStack cloud admin AllTenants: true, } allPages, err := volumetransfers.List(client, listOpts).AllPages() if err != nil { panic(err) } allTransfers, err := volumetransfers.ExtractTransfers(allPages) if err != nil { panic(err) } for _, transfer := range allTransfers { fmt.Println(transfer) } Example to Create a Volume Transfer request createOpts := volumetransfers.CreateOpts{ VolumeID: "uuid", Name: "my-volume-transfer", } transfer, err := volumetransfers.Create(client, createOpts).Extract() if err != nil { panic(err) } fmt.Println(transfer) // secret auth key is returned only once as a create response fmt.Printf("AuthKey: %s\n", transfer.AuthKey) Example to Accept a Volume Transfer request from the target project acceptOpts := volumetransfers.AcceptOpts{ // see the create response above AuthKey: "volume-transfer-secret-auth-key", } // see the transfer ID from the create response above transfer, err := volumetransfers.Accept(client, "transfer-uuid", acceptOpts).Extract() if err != nil { panic(err) } fmt.Println(transfer) Example to Delete a Volume Transfer request from the source project err := volumetransfers.Delete(client, "transfer-uuid").ExtractErr() if err != nil { panic(err) } */ package volumetransfers requests.go000066400000000000000000000074121367513235700354120ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/blockstorage/extensions/volumetransferspackage volumetransfers import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // CreateOpts contains options for a Volume transfer. type CreateOpts struct { // The ID of the volume to transfer. VolumeID string `json:"volume_id" required:"true"` // The name of the volume transfer Name string `json:"name,omitempty"` } // ToCreateMap assembles a request body based on the contents of a // TransferOpts. func (opts CreateOpts) ToCreateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "transfer") } // Create will create a volume tranfer request based on the values in CreateOpts. func Create(client *gophercloud.ServiceClient, opts CreateOpts) (r CreateResult) { b, err := opts.ToCreateMap() if err != nil { r.Err = err return } resp, err := client.Post(transferURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // AcceptOpts contains options for a Volume transfer accept reqeust. type AcceptOpts struct { // The auth key of the volume transfer to accept. AuthKey string `json:"auth_key" required:"true"` } // ToAcceptMap assembles a request body based on the contents of a // AcceptOpts. func (opts AcceptOpts) ToAcceptMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "accept") } // Accept will accept a volume tranfer request based on the values in AcceptOpts. func Accept(client *gophercloud.ServiceClient, id string, opts AcceptOpts) (r CreateResult) { b, err := opts.ToAcceptMap() if err != nil { r.Err = err return } resp, err := client.Post(acceptURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete deletes a volume transfer. func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { resp, err := client.Delete(deleteURL(client, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // ListOptsBuilder allows extensions to add additional parameters to the List // request. type ListOptsBuilder interface { ToTransferListQuery() (string, error) } // ListOpts holds options for listing Transfers. It is passed to the transfers.List // function. type ListOpts struct { // AllTenants will retrieve transfers of all tenants/projects. AllTenants bool `q:"all_tenants"` // 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"` } // ToTransferListQuery formats a ListOpts into a query string. func (opts ListOpts) ToTransferListQuery() (string, error) { q, err := gophercloud.BuildQueryString(opts) return q.String(), err } // List returns Transfers 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.ToTransferListQuery() if err != nil { return pagination.Pager{Err: err} } url += query } return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { return TransferPage{pagination.LinkedPageBase{PageResult: r}} }) } // Get retrieves the Transfer with the provided ID. To extract the Transfer object // from the response, call the Extract method on the GetResult. func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { resp, err := client.Get(getURL(client, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } results.go000066400000000000000000000051371367513235700352420ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/blockstorage/extensions/volumetransferspackage volumetransfers import ( "encoding/json" "time" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // Transfer represents a Volume Transfer record type Transfer struct { ID string `json:"id"` AuthKey string `json:"auth_key"` Name string `json:"name"` VolumeID string `json:"volume_id"` CreatedAt time.Time `json:"-"` Links []map[string]string `json:"links"` } // UnmarshalJSON is our unmarshalling helper func (r *Transfer) UnmarshalJSON(b []byte) error { type tmp Transfer var s struct { tmp CreatedAt gophercloud.JSONRFC3339MilliNoZ `json:"created_at"` } err := json.Unmarshal(b, &s) if err != nil { return err } *r = Transfer(s.tmp) r.CreatedAt = time.Time(s.CreatedAt) return err } type commonResult struct { gophercloud.Result } // Extract will get the Transfer object out of the commonResult object. func (r commonResult) Extract() (*Transfer, error) { var s Transfer err := r.ExtractInto(&s) return &s, err } // ExtractInto converts our response data into a transfer struct func (r commonResult) ExtractInto(v interface{}) error { return r.Result.ExtractIntoStructPtr(v, "transfer") } // 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 } // ExtractTransfers extracts and returns Transfers. It is used while iterating over a transfers.List call. func ExtractTransfers(r pagination.Page) ([]Transfer, error) { var s []Transfer err := ExtractTransfersInto(r, &s) return s, err } // ExtractTransfersInto similar to ExtractInto but operates on a `list` of transfers func ExtractTransfersInto(r pagination.Page, v interface{}) error { return r.(TransferPage).Result.ExtractIntoSlicePtr(v, "transfers") } // TransferPage is a pagination.pager that is returned from a call to the List function. type TransferPage struct { pagination.LinkedPageBase } // IsEmpty returns true if a ListResult contains no Transfers. func (r TransferPage) IsEmpty() (bool, error) { transfers, err := ExtractTransfers(r) return len(transfers) == 0, err } func (page TransferPage) NextPageURL() (string, error) { var s struct { Links []gophercloud.Link `json:"transfers_links"` } err := page.ExtractInto(&s) if err != nil { return "", err } return gophercloud.ExtractNextURL(s.Links) } testing/000077500000000000000000000000001367513235700346615ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/blockstorage/extensions/volumetransfersfixtures.go000066400000000000000000000140161367513235700370630ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/blockstorage/extensions/volumetransfers/testingpackage testing import ( "fmt" "net/http" "testing" "time" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack/blockstorage/extensions/volumetransfers" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) const ListOutput = ` { "transfers": [ { "created_at": "2020-02-28T12:44:28.051989", "volume_id": "2f6f1684-1ded-40db-8a49-7c87dedbc758", "id": "b8913bfd-a4d3-4ec5-bd8b-fe2dbeef9f4f", "links": [ { "href": "https://volume/v3/53c2b94f63fb4f43a21b92d119ce549f/os-volume-transfer/b8913bfd-a4d3-4ec5-bd8b-fe2dbeef9f4f", "rel": "self" }, { "href": "https://volume/53c2b94f63fb4f43a21b92d119ce549f/os-volume-transfer/b8913bfd-a4d3-4ec5-bd8b-fe2dbeef9f4f", "rel": "bookmark" } ], "name": null } ] } ` const GetOutput = ` { "transfer": { "created_at": "2020-02-28T12:44:28.051989", "volume_id": "2f6f1684-1ded-40db-8a49-7c87dedbc758", "id": "b8913bfd-a4d3-4ec5-bd8b-fe2dbeef9f4f", "links": [ { "href": "https://volume/v3/53c2b94f63fb4f43a21b92d119ce549f/os-volume-transfer/b8913bfd-a4d3-4ec5-bd8b-fe2dbeef9f4f", "rel": "self" }, { "href": "https://volume/53c2b94f63fb4f43a21b92d119ce549f/os-volume-transfer/b8913bfd-a4d3-4ec5-bd8b-fe2dbeef9f4f", "rel": "bookmark" } ], "name": null } } ` const CreateRequest = ` { "transfer": { "volume_id": "2f6f1684-1ded-40db-8a49-7c87dedbc758" } } ` const CreateResponse = ` { "transfer": { "auth_key": "cb67e0e7387d9eac", "created_at": "2020-02-28T12:44:28.051989", "id": "b8913bfd-a4d3-4ec5-bd8b-fe2dbeef9f4f", "links": [ { "href": "https://volume/v3/53c2b94f63fb4f43a21b92d119ce549f/os-volume-transfer/b8913bfd-a4d3-4ec5-bd8b-fe2dbeef9f4f", "rel": "self" }, { "href": "https://volume/53c2b94f63fb4f43a21b92d119ce549f/os-volume-transfer/b8913bfd-a4d3-4ec5-bd8b-fe2dbeef9f4f", "rel": "bookmark" } ], "name": null, "volume_id": "2f6f1684-1ded-40db-8a49-7c87dedbc758" } } ` const AcceptTransferRequest = ` { "accept": { "auth_key": "9266c59563c84664" } } ` const AcceptTransferResponse = ` { "transfer": { "id": "b8913bfd-a4d3-4ec5-bd8b-fe2dbeef9f4f", "links": [ { "href": "https://volume/v3/53c2b94f63fb4f43a21b92d119ce549f/os-volume-transfer/b8913bfd-a4d3-4ec5-bd8b-fe2dbeef9f4f", "rel": "self" }, { "href": "https://volume/53c2b94f63fb4f43a21b92d119ce549f/os-volume-transfer/b8913bfd-a4d3-4ec5-bd8b-fe2dbeef9f4f", "rel": "bookmark" } ], "name": null, "volume_id": "2f6f1684-1ded-40db-8a49-7c87dedbc758" } } ` var TransferRequest = volumetransfers.CreateOpts{ VolumeID: "2f6f1684-1ded-40db-8a49-7c87dedbc758", } var createdAt, _ = time.Parse(gophercloud.RFC3339MilliNoZ, "2020-02-28T12:44:28.051989") var TransferResponse = volumetransfers.Transfer{ ID: "b8913bfd-a4d3-4ec5-bd8b-fe2dbeef9f4f", AuthKey: "cb67e0e7387d9eac", Name: "", VolumeID: "2f6f1684-1ded-40db-8a49-7c87dedbc758", CreatedAt: createdAt, Links: []map[string]string{ { "href": "https://volume/v3/53c2b94f63fb4f43a21b92d119ce549f/os-volume-transfer/b8913bfd-a4d3-4ec5-bd8b-fe2dbeef9f4f", "rel": "self", }, { "href": "https://volume/53c2b94f63fb4f43a21b92d119ce549f/os-volume-transfer/b8913bfd-a4d3-4ec5-bd8b-fe2dbeef9f4f", "rel": "bookmark", }, }, } var TransferListResponse = []volumetransfers.Transfer{TransferResponse} var AcceptRequest = volumetransfers.AcceptOpts{ AuthKey: "9266c59563c84664", } var AcceptResponse = volumetransfers.Transfer{ ID: "b8913bfd-a4d3-4ec5-bd8b-fe2dbeef9f4f", Name: "", VolumeID: "2f6f1684-1ded-40db-8a49-7c87dedbc758", Links: []map[string]string{ { "href": "https://volume/v3/53c2b94f63fb4f43a21b92d119ce549f/os-volume-transfer/b8913bfd-a4d3-4ec5-bd8b-fe2dbeef9f4f", "rel": "self", }, { "href": "https://volume/53c2b94f63fb4f43a21b92d119ce549f/os-volume-transfer/b8913bfd-a4d3-4ec5-bd8b-fe2dbeef9f4f", "rel": "bookmark", }, }, } func HandleCreateTransfer(t *testing.T) { th.Mux.HandleFunc("/os-volume-transfer", 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") th.TestJSONRequest(t, r, CreateRequest) w.WriteHeader(http.StatusAccepted) fmt.Fprintf(w, CreateResponse) }) } func HandleAcceptTransfer(t *testing.T) { th.Mux.HandleFunc("/os-volume-transfer/b8913bfd-a4d3-4ec5-bd8b-fe2dbeef9f4f/accept", 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") th.TestJSONRequest(t, r, AcceptTransferRequest) w.WriteHeader(http.StatusAccepted) fmt.Fprintf(w, AcceptTransferResponse) }) } func HandleDeleteTransfer(t *testing.T) { th.Mux.HandleFunc("/os-volume-transfer/b8913bfd-a4d3-4ec5-bd8b-fe2dbeef9f4f", 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 HandleListTransfers(t *testing.T) { th.Mux.HandleFunc("/os-volume-transfer/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") th.TestFormValues(t, r, map[string]string{"all_tenants": "true"}) w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ListOutput) }) } func HandleGetTransfer(t *testing.T) { th.Mux.HandleFunc("/os-volume-transfer/b8913bfd-a4d3-4ec5-bd8b-fe2dbeef9f4f", 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.go000066400000000000000000000046171367513235700401320ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/blockstorage/extensions/volumetransfers/testingpackage testing import ( "testing" "github.com/gophercloud/gophercloud/openstack/blockstorage/extensions/volumetransfers" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) func TestCreateTransfer(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleCreateTransfer(t) actual, err := volumetransfers.Create(client.ServiceClient(), TransferRequest).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, TransferResponse, *actual) } func TestAcceptTransfer(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleAcceptTransfer(t) actual, err := volumetransfers.Accept(client.ServiceClient(), TransferResponse.ID, AcceptRequest).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, AcceptResponse, *actual) } func TestDeleteTransfer(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleDeleteTransfer(t) err := volumetransfers.Delete(client.ServiceClient(), TransferResponse.ID).ExtractErr() th.AssertNoErr(t, err) } func TestListTransfers(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleListTransfers(t) expectedResponse := TransferListResponse expectedResponse[0].AuthKey = "" count := 0 err := volumetransfers.List(client.ServiceClient(), &volumetransfers.ListOpts{AllTenants: true}).EachPage(func(page pagination.Page) (bool, error) { count++ actual, err := volumetransfers.ExtractTransfers(page) th.AssertNoErr(t, err) th.CheckDeepEquals(t, expectedResponse, actual) return true, nil }) th.AssertNoErr(t, err) th.CheckEquals(t, count, 1) } func TestListTransfersAllPages(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleListTransfers(t) expectedResponse := TransferListResponse expectedResponse[0].AuthKey = "" allPages, err := volumetransfers.List(client.ServiceClient(), &volumetransfers.ListOpts{AllTenants: true}).AllPages() th.AssertNoErr(t, err) actual, err := volumetransfers.ExtractTransfers(allPages) th.AssertNoErr(t, err) th.CheckDeepEquals(t, expectedResponse, actual) } func TestGetTransfer(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleGetTransfer(t) expectedResponse := TransferResponse expectedResponse.AuthKey = "" actual, err := volumetransfers.Get(client.ServiceClient(), TransferResponse.ID).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, expectedResponse, *actual) } urls.go000066400000000000000000000011671367513235700345250ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/blockstorage/extensions/volumetransferspackage volumetransfers import "github.com/gophercloud/gophercloud" func transferURL(c *gophercloud.ServiceClient) string { return c.ServiceURL("os-volume-transfer") } func acceptURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL("os-volume-transfer", id, "accept") } func deleteURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL("os-volume-transfer", id) } func listURL(c *gophercloud.ServiceClient) string { return c.ServiceURL("os-volume-transfer", "detail") } func getURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL("os-volume-transfer", id) } golang-github-gophercloud-gophercloud-0.12.0/openstack/blockstorage/noauth/000077500000000000000000000000001367513235700271235ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/blockstorage/noauth/doc.go000066400000000000000000000010571367513235700302220ustar00rootroot00000000000000/* 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 golang-github-gophercloud-gophercloud-0.12.0/openstack/blockstorage/noauth/requests.go000066400000000000000000000031351367513235700313270ustar00rootroot00000000000000package 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) } golang-github-gophercloud-gophercloud-0.12.0/openstack/blockstorage/noauth/testing/000077500000000000000000000000001367513235700306005ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/blockstorage/noauth/testing/doc.go000066400000000000000000000000451367513235700316730ustar00rootroot00000000000000// noauth unit tests package testing golang-github-gophercloud-gophercloud-0.12.0/openstack/blockstorage/noauth/testing/fixtures.go000066400000000000000000000006171367513235700330040ustar00rootroot00000000000000package 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" golang-github-gophercloud-gophercloud-0.12.0/openstack/blockstorage/noauth/testing/requests_test.go000066400000000000000000000022321367513235700340400ustar00rootroot00000000000000package 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.12.0/openstack/blockstorage/v1/000077500000000000000000000000001367513235700261535ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/blockstorage/v1/apiversions/000077500000000000000000000000001367513235700305155ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/blockstorage/v1/apiversions/doc.go000066400000000000000000000005671367513235700316210ustar00rootroot00000000000000/* Package apiversions provides information and interaction with the different API versions for the OpenStack Block Storage service, code-named Cinder. This package is deprecated and should only be used in environments where the older API version format is expected. Consider using the gophercloud/openstack/blockstorage/apiversions package instead. */ package apiversions golang-github-gophercloud-gophercloud-0.12.0/openstack/blockstorage/v1/apiversions/requests.go000066400000000000000000000013461367513235700327230ustar00rootroot00000000000000package 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) { resp, err := client.Get(getURL(client, v), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } golang-github-gophercloud-gophercloud-0.12.0/openstack/blockstorage/v1/apiversions/results.go000066400000000000000000000025661367513235700325560ustar00rootroot00000000000000package 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 } golang-github-gophercloud-gophercloud-0.12.0/openstack/blockstorage/v1/apiversions/testing/000077500000000000000000000000001367513235700321725ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/blockstorage/v1/apiversions/testing/doc.go000066400000000000000000000000421367513235700332620ustar00rootroot00000000000000// apiversions_v1 package testing fixtures.go000066400000000000000000000037501367513235700343200ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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.go000066400000000000000000000025271367513235700353620ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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) } golang-github-gophercloud-gophercloud-0.12.0/openstack/blockstorage/v1/apiversions/urls.go000066400000000000000000000010451367513235700320310ustar00rootroot00000000000000package apiversions import ( "strings" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack/utils" ) func getURL(c *gophercloud.ServiceClient, version string) string { baseEndpoint, _ := utils.BaseEndpoint(c.Endpoint) endpoint := strings.TrimRight(baseEndpoint, "/") + "/" + strings.TrimRight(version, "/") + "/" return endpoint } func listURL(c *gophercloud.ServiceClient) string { baseEndpoint, _ := utils.BaseEndpoint(c.Endpoint) endpoint := strings.TrimRight(baseEndpoint, "/") + "/" return endpoint } golang-github-gophercloud-gophercloud-0.12.0/openstack/blockstorage/v1/snapshots/000077500000000000000000000000001367513235700301755ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/blockstorage/v1/snapshots/doc.go000066400000000000000000000004141367513235700312700ustar00rootroot00000000000000// 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 golang-github-gophercloud-gophercloud-0.12.0/openstack/blockstorage/v1/snapshots/requests.go000066400000000000000000000110561367513235700324020ustar00rootroot00000000000000package 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 } resp, err := client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete will delete the existing Snapshot with the provided ID. func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { resp, err := client.Delete(deleteURL(client, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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) { resp, err := client.Get(getURL(client, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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 } resp, err := client.Put(updateMetadataURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } golang-github-gophercloud-gophercloud-0.12.0/openstack/blockstorage/v1/snapshots/results.go000066400000000000000000000062341367513235700322320ustar00rootroot00000000000000package 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 } golang-github-gophercloud-gophercloud-0.12.0/openstack/blockstorage/v1/snapshots/testing/000077500000000000000000000000001367513235700316525ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/blockstorage/v1/snapshots/testing/doc.go000066400000000000000000000000401367513235700327400ustar00rootroot00000000000000// snapshots_v1 package testing golang-github-gophercloud-gophercloud-0.12.0/openstack/blockstorage/v1/snapshots/testing/fixtures.go000066400000000000000000000067131367513235700340610ustar00rootroot00000000000000package 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.go000066400000000000000000000054441367513235700350430ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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) } golang-github-gophercloud-gophercloud-0.12.0/openstack/blockstorage/v1/snapshots/urls.go000066400000000000000000000012111367513235700315040ustar00rootroot00000000000000package 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) } golang-github-gophercloud-gophercloud-0.12.0/openstack/blockstorage/v1/snapshots/util.go000066400000000000000000000010071367513235700314770ustar00rootroot00000000000000package 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.12.0/openstack/blockstorage/v1/volumes/000077500000000000000000000000001367513235700276455ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/blockstorage/v1/volumes/doc.go000066400000000000000000000004021367513235700307350ustar00rootroot00000000000000// 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 golang-github-gophercloud-gophercloud-0.12.0/openstack/blockstorage/v1/volumes/requests.go000066400000000000000000000120051367513235700320450ustar00rootroot00000000000000package 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 } resp, err := client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201, 202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete will delete the existing Volume with the provided ID. func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { resp, err := client.Delete(deleteURL(client, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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) { resp, err := client.Get(getURL(client, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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 } resp, err := client.Put(updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } golang-github-gophercloud-gophercloud-0.12.0/openstack/blockstorage/v1/volumes/results.go000066400000000000000000000057201367513235700317010ustar00rootroot00000000000000package 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 } golang-github-gophercloud-gophercloud-0.12.0/openstack/blockstorage/v1/volumes/testing/000077500000000000000000000000001367513235700313225ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/blockstorage/v1/volumes/testing/doc.go000066400000000000000000000004221367513235700324140ustar00rootroot00000000000000// 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 */ golang-github-gophercloud-gophercloud-0.12.0/openstack/blockstorage/v1/volumes/testing/fixtures.go000066400000000000000000000065311367513235700335270ustar00rootroot00000000000000package 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.go000066400000000000000000000067121367513235700345120ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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) var name = "vol-002" options := volumes.UpdateOpts{Name: &name} v, err := volumes.Update(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22", options).Extract() th.AssertNoErr(t, err) th.CheckEquals(t, "vol-002", v.Name) } golang-github-gophercloud-gophercloud-0.12.0/openstack/blockstorage/v1/volumes/urls.go000066400000000000000000000010011367513235700311510ustar00rootroot00000000000000package 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) } golang-github-gophercloud-gophercloud-0.12.0/openstack/blockstorage/v1/volumes/util.go000066400000000000000000000010051367513235700311450ustar00rootroot00000000000000package 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.12.0/openstack/blockstorage/v1/volumetypes/000077500000000000000000000000001367513235700305475ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/blockstorage/v1/volumetypes/doc.go000066400000000000000000000010121367513235700316350ustar00rootroot00000000000000// 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 golang-github-gophercloud-gophercloud-0.12.0/openstack/blockstorage/v1/volumetypes/requests.go000066400000000000000000000040541367513235700327540ustar00rootroot00000000000000package 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 } resp, err := client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete will delete the volume type with the provided ID. func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { resp, err := client.Delete(deleteURL(client, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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) { resp, err := client.Get(getURL(client, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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)} }) } golang-github-gophercloud-gophercloud-0.12.0/openstack/blockstorage/v1/volumetypes/results.go000066400000000000000000000032771367513235700326100ustar00rootroot00000000000000package 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 } golang-github-gophercloud-gophercloud-0.12.0/openstack/blockstorage/v1/volumetypes/testing/000077500000000000000000000000001367513235700322245ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/blockstorage/v1/volumetypes/testing/doc.go000066400000000000000000000000421367513235700333140ustar00rootroot00000000000000// volumetypes_v1 package testing fixtures.go000066400000000000000000000025371367513235700343540ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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.go000066400000000000000000000056051367513235700354140ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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) } golang-github-gophercloud-gophercloud-0.12.0/openstack/blockstorage/v1/volumetypes/urls.go000066400000000000000000000006371367513235700320710ustar00rootroot00000000000000package 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.12.0/openstack/blockstorage/v2/000077500000000000000000000000001367513235700261545ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/blockstorage/v2/snapshots/000077500000000000000000000000001367513235700301765ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/blockstorage/v2/snapshots/doc.go000066400000000000000000000004141367513235700312710ustar00rootroot00000000000000// 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 golang-github-gophercloud-gophercloud-0.12.0/openstack/blockstorage/v2/snapshots/requests.go000066400000000000000000000115751367513235700324110ustar00rootroot00000000000000package 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 } resp, err := client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete will delete the existing Snapshot with the provided ID. func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { resp, err := client.Delete(deleteURL(client, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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) { resp, err := client.Get(getURL(client, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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 } resp, err := client.Put(updateMetadataURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } golang-github-gophercloud-gophercloud-0.12.0/openstack/blockstorage/v2/snapshots/results.go000066400000000000000000000055711367513235700322360ustar00rootroot00000000000000package 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 } golang-github-gophercloud-gophercloud-0.12.0/openstack/blockstorage/v2/snapshots/testing/000077500000000000000000000000001367513235700316535ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/blockstorage/v2/snapshots/testing/doc.go000066400000000000000000000000401367513235700327410ustar00rootroot00000000000000// snapshots_v2 package testing golang-github-gophercloud-gophercloud-0.12.0/openstack/blockstorage/v2/snapshots/testing/fixtures.go000066400000000000000000000066101367513235700340560ustar00rootroot00000000000000package 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.go000066400000000000000000000054421367513235700350420ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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) } golang-github-gophercloud-gophercloud-0.12.0/openstack/blockstorage/v2/snapshots/urls.go000066400000000000000000000012111367513235700315050ustar00rootroot00000000000000package 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) } golang-github-gophercloud-gophercloud-0.12.0/openstack/blockstorage/v2/snapshots/util.go000066400000000000000000000010071367513235700315000ustar00rootroot00000000000000package 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.12.0/openstack/blockstorage/v2/volumes/000077500000000000000000000000001367513235700276465ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/blockstorage/v2/volumes/doc.go000066400000000000000000000004021367513235700307360ustar00rootroot00000000000000// 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 golang-github-gophercloud-gophercloud-0.12.0/openstack/blockstorage/v2/volumes/requests.go000066400000000000000000000152441367513235700320560ustar00rootroot00000000000000package 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 } resp, err := client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // DeleteOptsBuilder allows extensions to add additional parameters to the // Delete request. type DeleteOptsBuilder interface { ToVolumeDeleteQuery() (string, error) } // DeleteOpts contains options for deleting a Volume. This object is passed to // the volumes.Delete function. type DeleteOpts struct { // Delete all snapshots of this volume as well. Cascade bool `q:"cascade"` } // ToLoadBalancerDeleteQuery formats a DeleteOpts into a query string. func (opts DeleteOpts) ToVolumeDeleteQuery() (string, error) { q, err := gophercloud.BuildQueryString(opts) return q.String(), err } // Delete will delete the existing Volume with the provided ID. func Delete(client *gophercloud.ServiceClient, id string, opts DeleteOptsBuilder) (r DeleteResult) { url := deleteURL(client, id) if opts != nil { query, err := opts.ToVolumeDeleteQuery() if err != nil { r.Err = err return } url += query } resp, err := client.Delete(url, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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) { resp, err := client.Get(getURL(client, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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 } resp, err := client.Put(updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } golang-github-gophercloud-gophercloud-0.12.0/openstack/blockstorage/v2/volumes/results.go000066400000000000000000000112771367513235700317060ustar00rootroot00000000000000package 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 } golang-github-gophercloud-gophercloud-0.12.0/openstack/blockstorage/v2/volumes/testing/000077500000000000000000000000001367513235700313235ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/blockstorage/v2/volumes/testing/doc.go000066400000000000000000000000361367513235700324160ustar00rootroot00000000000000// volumes_v2 package testing golang-github-gophercloud-gophercloud-0.12.0/openstack/blockstorage/v2/volumes/testing/fixtures.go000066400000000000000000000135531367513235700335320ustar00rootroot00000000000000package 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.go000066400000000000000000000167351367513235700345210ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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", volumes.DeleteOpts{}) th.AssertNoErr(t, res.Err) } func TestUpdate(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() MockUpdateResponse(t) var name = "vol-002" options := volumes.UpdateOpts{Name: &name} 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") } } golang-github-gophercloud-gophercloud-0.12.0/openstack/blockstorage/v2/volumes/urls.go000066400000000000000000000010261367513235700311610ustar00rootroot00000000000000package 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) } golang-github-gophercloud-gophercloud-0.12.0/openstack/blockstorage/v2/volumes/util.go000066400000000000000000000010051367513235700311460ustar00rootroot00000000000000package 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.12.0/openstack/blockstorage/v3/000077500000000000000000000000001367513235700261555ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/blockstorage/v3/attachments/000077500000000000000000000000001367513235700304705ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/blockstorage/v3/attachments/doc.go000066400000000000000000000032641367513235700315710ustar00rootroot00000000000000/* Package attachments provides access to OpenStack Block Storage Attachment API's. Use of this package requires Cinder version 3.27 at a minimum. For more information, see: https://docs.openstack.org/api-ref/block-storage/v3/index.html#attachments Example to List Attachments listOpts := &attachments.ListOpts{ InstanceID: "uuid", } client.Microversion = "3.27" allPages, err := attachments.List(client, listOpts).AllPages() if err != nil { panic(err) } allAttachments, err := attachments.ExtractAttachments(allPages) if err != nil { panic(err) } for _, attachment := range allAttachments { fmt.Println(attachment) } Example to Create Attachment createOpts := &attachments.CreateOpts{ InstanceiUUID: "uuid", VolumeUUID: "uuid" } client.Microversion = "3.27" attachment, err := attachments.Create(client, createOpts).Extract() if err != nil { panic(err) } fmt.Println(attachment) Example to Get Attachment client.Microversion = "3.27" attachment, err := attachments.Get(client, "uuid").Extract() if err != nil { panic(err) } fmt.Println(attachment) Example to Update Attachment opts := &attachments.UpdateOpts{ Connector: map[string]interface{}{ "mode": "ro", } } client.Microversion = "3.27" attachment, err := attachments.Update(client, "uuid", opts).Extract() if err != nil { panic(err) } fmt.Println(attachment) Example to Complete Attachment client.Microversion = "3.44" err := attachments.Complete(client, "uuid").ExtractErr() if err != nil { panic(err) } Example to Delete Attachment client.Microversion = "3.27" err := attachments.Delete(client, "uuid").ExtractErr() if err != nil { panic(err) } */ package attachments golang-github-gophercloud-gophercloud-0.12.0/openstack/blockstorage/v3/attachments/requests.go000066400000000000000000000137561367513235700327060ustar00rootroot00000000000000package attachments import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // CreateOptsBuilder allows extensions to add additional parameters to the // Create request. type CreateOptsBuilder interface { ToAttachmentCreateMap() (map[string]interface{}, error) } // CreateOpts contains options for creating a Volume attachment. This object is //passed to the Create function. For more information about these parameters, // see the Attachment object. type CreateOpts struct { // VolumeUUID is the UUID of the Cinder volume to create the attachment // record for. VolumeUUID string `json:"volume_uuid"` // InstanceUUID is the ID of the Server to create the attachment for. // When attaching to a Nova Server this is the Nova Server (Instance) // UUID. InstanceUUID string `json:"instance_uuid"` // Connector is an optional map containing all of the needed atachment // information for exmaple initiator IQN, etc. Connector map[string]interface{} `json:"connector,omitempty"` // Mode is an attachment mode. Acceptable values are read-only ('ro') // and read-and-write ('rw'). Available only since 3.54 microversion. // For APIs from 3.27 till 3.53 use Connector["mode"] = "rw|ro". Mode string `json:"mode,omitempty"` } // ToAttachmentCreateMap assembles a request body based on the contents of a // CreateOpts. func (opts CreateOpts) ToAttachmentCreateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "attachment") } // Create will create a new Attachment based on the values in CreateOpts. To // extract the Attachment object from the response, call the Extract method on // the CreateResult. func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToAttachmentCreateMap() if err != nil { r.Err = err return } resp, err := client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete will delete the existing Attachment with the provided ID. func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { resp, err := client.Delete(deleteURL(client, id), &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get retrieves the Attachment with the provided ID. To extract the Attachment // object from the response, call the Extract method on the GetResult. func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { resp, err := client.Get(getURL(client, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // ListOptsBuilder allows extensions to add additional parameters to the List // request. type ListOptsBuilder interface { ToAttachmentListQuery() (string, error) } // ListOpts holds options for listing Attachments. It is passed to the attachments.List // function. type ListOpts struct { // AllTenants will retrieve attachments of all tenants/projects. AllTenants bool `q:"all_tenants"` // Status will filter by the specified status. Status string `q:"status"` // ProjectID will filter by a specific tenant/project ID. ProjectID string `q:"project_id"` // VolumeID will filter by a specific volume ID. VolumeID string `q:"volume_id"` // InstanceID will filter by a specific instance ID. InstanceID string `q:"instance_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"` } // ToAttachmentListQuery formats a ListOpts into a query string. func (opts ListOpts) ToAttachmentListQuery() (string, error) { q, err := gophercloud.BuildQueryString(opts) return q.String(), err } // List returns Attachments 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.ToAttachmentListQuery() if err != nil { return pagination.Pager{Err: err} } url += query } return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { return AttachmentPage{pagination.LinkedPageBase{PageResult: r}} }) } // UpdateOptsBuilder allows extensions to add additional parameters to the // Update request. type UpdateOptsBuilder interface { ToAttachmentUpdateMap() (map[string]interface{}, error) } // UpdateOpts contain options for updating an existing Attachment. // This is used to finalize an attachment that was created without a // connector (reserve). type UpdateOpts struct { Connector map[string]interface{} `json:"connector"` } // ToAttachmentUpdateMap assembles a request body based on the contents of an // UpdateOpts. func (opts UpdateOpts) ToAttachmentUpdateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "attachment") } // Update will update the Attachment with provided information. To extract the // updated Attachment from the response, call the Extract method on the // UpdateResult. func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToAttachmentUpdateMap() if err != nil { r.Err = err return } resp, err := client.Put(updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Complete will complete an attachment for a cinder volume. // Available starting in the 3.44 microversion. func Complete(client *gophercloud.ServiceClient, id string) (r CompleteResult) { b := map[string]interface{}{ "os-complete": nil, } resp, err := client.Post(completeURL(client, id), b, nil, &gophercloud.RequestOpts{ OkCodes: []int{204}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } golang-github-gophercloud-gophercloud-0.12.0/openstack/blockstorage/v3/attachments/results.go000066400000000000000000000067361367513235700325340ustar00rootroot00000000000000// Package attachments provides access to OpenStack Block Storage Attachment // API's. Use of this package requires Cinder version 3.27 at a minimum. package attachments import ( "encoding/json" "time" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // Attachment contains all the information associated with an OpenStack // Attachment. type Attachment struct { // ID is the Unique identifier for the attachment. ID string `json:"id"` // VolumeID is the UUID of the Volume associated with this attachment. VolumeID string `json:"volume_id"` // Instance is the Instance/Server UUID associated with this attachment. Instance string `json:"instance"` // AttachedAt is the time the attachment was created. AttachedAt time.Time `json:"-"` // DetachedAt is the time the attachment was detached. DetachedAt time.Time `json:"-"` // Status is the current attach status. Status string `json:"status"` // AttachMode includes things like Read Only etc. AttachMode string `json:"attach_mode"` // ConnectionInfo is the required info for a node to make a connection // provided by the driver. ConnectionInfo map[string]interface{} `json:"connection_info"` } // 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"` DetachedAt gophercloud.JSONRFC3339MilliNoZ `json:"detached_at"` } err := json.Unmarshal(b, &s) if err != nil { return err } *r = Attachment(s.tmp) r.AttachedAt = time.Time(s.AttachedAt) r.DetachedAt = time.Time(s.DetachedAt) return err } // AttachmentPage is a pagination.pager that is returned from a call to the List // function. type AttachmentPage struct { pagination.LinkedPageBase } // IsEmpty returns true if a ListResult contains no Attachments. func (r AttachmentPage) IsEmpty() (bool, error) { attachments, err := ExtractAttachments(r) return len(attachments) == 0, err } // ExtractAttachments extracts and returns Attachments. It is used while // iterating over a attachment.List call. func ExtractAttachments(r pagination.Page) ([]Attachment, error) { var s []Attachment err := ExtractAttachmentsInto(r, &s) return s, err } type commonResult struct { gophercloud.Result } // Extract will get the Attachment object out of the commonResult object. func (r commonResult) Extract() (*Attachment, error) { var s Attachment err := r.ExtractInto(&s) return &s, err } // ExtractInto converts our response data into a attachment struct. func (r commonResult) ExtractInto(a interface{}) error { return r.Result.ExtractIntoStructPtr(a, "attachment") } // ExtractAttachmentsInto similar to ExtractInto but operates on a List of // attachments. func ExtractAttachmentsInto(r pagination.Page, a interface{}) error { return r.(AttachmentPage).Result.ExtractIntoSlicePtr(a, "attachments") } // 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 } // CompleteResult contains the response body and error from a Complete request. type CompleteResult struct { gophercloud.ErrResult } golang-github-gophercloud-gophercloud-0.12.0/openstack/blockstorage/v3/attachments/testing/000077500000000000000000000000001367513235700321455ustar00rootroot00000000000000fixtures.go000066400000000000000000000150261367513235700342720ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/blockstorage/v3/attachments/testingpackage testing import ( "fmt" "net/http" "testing" "time" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack/blockstorage/v3/attachments" th "github.com/gophercloud/gophercloud/testhelper" fake "github.com/gophercloud/gophercloud/testhelper/client" ) var ( attachedAt, _ = time.Parse(gophercloud.RFC3339MilliNoZ, "2015-09-16T09:28:52.000000") detachedAt, _ = time.Parse(gophercloud.RFC3339MilliNoZ, "2015-09-16T09:28:52.000000") expectedAttachment = &attachments.Attachment{ ID: "05551600-a936-4d4a-ba42-79a037c1-c91a", VolumeID: "289da7f8-6440-407c-9fb4-7db01ec49164", Instance: "83ec2e3b-4321-422b-8706-a84185f52a0a", AttachMode: "rw", Status: "attaching", AttachedAt: attachedAt, DetachedAt: detachedAt, ConnectionInfo: map[string]interface{}{}, } ) func MockListResponse(t *testing.T) { th.Mux.HandleFunc("/attachments/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, ` { "attachments": [ { "status": "attaching", "detached_at": "2015-09-16T09:28:52.000000", "connection_info": {}, "attached_at": "2015-09-16T09:28:52.000000", "attach_mode": "rw", "instance": "83ec2e3b-4321-422b-8706-a84185f52a0a", "volume_id": "289da7f8-6440-407c-9fb4-7db01ec49164", "id": "05551600-a936-4d4a-ba42-79a037c1-c91a" } ], "attachments_links": [ { "href": "%s/attachments/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("/attachments/05551600-a936-4d4a-ba42-79a037c1-c91a", 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, ` { "attachment": { "status": "attaching", "detached_at": "2015-09-16T09:28:52.000000", "connection_info": {}, "attached_at": "2015-09-16T09:28:52.000000", "attach_mode": "rw", "instance": "83ec2e3b-4321-422b-8706-a84185f52a0a", "volume_id": "289da7f8-6440-407c-9fb4-7db01ec49164", "id": "05551600-a936-4d4a-ba42-79a037c1-c91a" } } `) }) } func MockCreateResponse(t *testing.T) { th.Mux.HandleFunc("/attachments", 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, ` { "attachment": { "instance_uuid": "83ec2e3b-4321-422b-8706-a84185f52a0a", "connector": { "initiator": "iqn.1993-08.org.debian: 01: cad181614cec", "ip": "192.168.1.20", "platform": "x86_64", "host": "tempest-1", "os_type": "linux2", "multipath": false, "mountpoint": "/dev/vdb", "mode": "rw" }, "volume_uuid": "289da7f8-6440-407c-9fb4-7db01ec49164" } } `) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ` { "attachment": { "status": "attaching", "detached_at": "2015-09-16T09:28:52.000000", "connection_info": {}, "attached_at": "2015-09-16T09:28:52.000000", "attach_mode": "rw", "instance": "83ec2e3b-4321-422b-8706-a84185f52a0a", "volume_id": "289da7f8-6440-407c-9fb4-7db01ec49164", "id": "05551600-a936-4d4a-ba42-79a037c1-c91a" } } `) }) } func MockDeleteResponse(t *testing.T) { th.Mux.HandleFunc("/attachments/05551600-a936-4d4a-ba42-79a037c1-c91a", 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) }) } func MockUpdateResponse(t *testing.T) { th.Mux.HandleFunc("/attachments/05551600-a936-4d4a-ba42-79a037c1-c91a", 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, ` { "attachment": { "connector": { "initiator": "iqn.1993-08.org.debian: 01: cad181614cec", "ip": "192.168.1.20", "platform": "x86_64", "host": "tempest-1", "os_type": "linux2", "multipath": false, "mountpoint": "/dev/vdb", "mode": "rw" } } } `) w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ` { "attachment": { "status": "attaching", "detached_at": "2015-09-16T09:28:52.000000", "connection_info": {}, "attached_at": "2015-09-16T09:28:52.000000", "attach_mode": "rw", "instance": "83ec2e3b-4321-422b-8706-a84185f52a0a", "volume_id": "289da7f8-6440-407c-9fb4-7db01ec49164", "id": "05551600-a936-4d4a-ba42-79a037c1-c91a" } } `) }) } func MockUpdateEmptyResponse(t *testing.T) { th.Mux.HandleFunc("/attachments/05551600-a936-4d4a-ba42-79a037c1-c91a", 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, ` { "attachment": { "connector": null } } `) w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ` { "attachment": { "status": "attaching", "detached_at": "2015-09-16T09:28:52.000000", "connection_info": {}, "attached_at": "2015-09-16T09:28:52.000000", "attach_mode": "rw", "instance": "83ec2e3b-4321-422b-8706-a84185f52a0a", "volume_id": "289da7f8-6440-407c-9fb4-7db01ec49164", "id": "05551600-a936-4d4a-ba42-79a037c1-c91a" } } `) }) } var completeRequest = ` { "os-complete": null } ` func MockCompleteResponse(t *testing.T) { th.Mux.HandleFunc("/attachments/05551600-a936-4d4a-ba42-79a037c1-c91a/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, completeRequest) w.WriteHeader(http.StatusNoContent) }) } requests_test.go000066400000000000000000000060001367513235700353230ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/blockstorage/v3/attachments/testingpackage testing import ( "testing" "github.com/gophercloud/gophercloud/openstack/blockstorage/v3/attachments" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) func TestListAll(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() MockListResponse(t) allPages, err := attachments.List(client.ServiceClient(), &attachments.ListOpts{}).AllPages() th.AssertNoErr(t, err) actual, err := attachments.ExtractAttachments(allPages) th.AssertNoErr(t, err) expected := []attachments.Attachment{*expectedAttachment} th.CheckDeepEquals(t, expected, actual) } func TestGet(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() MockGetResponse(t) attachment, err := attachments.Get(client.ServiceClient(), "05551600-a936-4d4a-ba42-79a037c1-c91a").Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, expectedAttachment, attachment) } func TestCreate(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() MockCreateResponse(t) options := &attachments.CreateOpts{ InstanceUUID: "83ec2e3b-4321-422b-8706-a84185f52a0a", Connector: map[string]interface{}{ "initiator": "iqn.1993-08.org.debian: 01: cad181614cec", "ip": "192.168.1.20", "platform": "x86_64", "host": "tempest-1", "os_type": "linux2", "multipath": false, "mountpoint": "/dev/vdb", "mode": "rw", }, VolumeUUID: "289da7f8-6440-407c-9fb4-7db01ec49164", } attachment, err := attachments.Create(client.ServiceClient(), options).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, expectedAttachment, attachment) } func TestDelete(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() MockDeleteResponse(t) res := attachments.Delete(client.ServiceClient(), "05551600-a936-4d4a-ba42-79a037c1-c91a") th.AssertNoErr(t, res.Err) } func TestUpdate(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() MockUpdateResponse(t) options := &attachments.UpdateOpts{ Connector: map[string]interface{}{ "initiator": "iqn.1993-08.org.debian: 01: cad181614cec", "ip": "192.168.1.20", "platform": "x86_64", "host": "tempest-1", "os_type": "linux2", "multipath": false, "mountpoint": "/dev/vdb", "mode": "rw", }, } attachment, err := attachments.Update(client.ServiceClient(), "05551600-a936-4d4a-ba42-79a037c1-c91a", options).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, expectedAttachment, attachment) } func TestUpdateEmpty(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() MockUpdateEmptyResponse(t) options := attachments.UpdateOpts{} attachment, err := attachments.Update(client.ServiceClient(), "05551600-a936-4d4a-ba42-79a037c1-c91a", options).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, expectedAttachment, attachment) } func TestComplete(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() MockCompleteResponse(t) err := attachments.Complete(client.ServiceClient(), "05551600-a936-4d4a-ba42-79a037c1-c91a").ExtractErr() th.AssertNoErr(t, err) } golang-github-gophercloud-gophercloud-0.12.0/openstack/blockstorage/v3/attachments/urls.go000066400000000000000000000012741367513235700320100ustar00rootroot00000000000000package attachments import "github.com/gophercloud/gophercloud" func createURL(c *gophercloud.ServiceClient) string { return c.ServiceURL("attachments") } func listURL(c *gophercloud.ServiceClient) string { return c.ServiceURL("attachments", "detail") } func getURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL("attachments", id) } func updateURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL("attachments", id) } func deleteURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL("attachments", id) } func completeURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL("attachments", id, "action") } golang-github-gophercloud-gophercloud-0.12.0/openstack/blockstorage/v3/attachments/util.go000066400000000000000000000010111367513235700317650ustar00rootroot00000000000000package attachments 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.12.0/openstack/blockstorage/v3/snapshots/000077500000000000000000000000001367513235700301775ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/blockstorage/v3/snapshots/doc.go000066400000000000000000000004141367513235700312720ustar00rootroot00000000000000// 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 golang-github-gophercloud-gophercloud-0.12.0/openstack/blockstorage/v3/snapshots/requests.go000066400000000000000000000122001367513235700323740ustar00rootroot00000000000000package 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 } resp, err := client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete will delete the existing Snapshot with the provided ID. func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { resp, err := client.Delete(deleteURL(client, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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) { resp, err := client.Get(getURL(client, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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 } resp, err := client.Put(updateMetadataURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } golang-github-gophercloud-gophercloud-0.12.0/openstack/blockstorage/v3/snapshots/results.go000066400000000000000000000062631367513235700322360ustar00rootroot00000000000000package 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 } golang-github-gophercloud-gophercloud-0.12.0/openstack/blockstorage/v3/snapshots/testing/000077500000000000000000000000001367513235700316545ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/blockstorage/v3/snapshots/testing/doc.go000066400000000000000000000000401367513235700327420ustar00rootroot00000000000000// snapshots_v3 package testing golang-github-gophercloud-gophercloud-0.12.0/openstack/blockstorage/v3/snapshots/testing/fixtures.go000066400000000000000000000073171367513235700340640ustar00rootroot00000000000000package 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.go000066400000000000000000000054421367513235700350430ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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) } golang-github-gophercloud-gophercloud-0.12.0/openstack/blockstorage/v3/snapshots/urls.go000066400000000000000000000012111367513235700315060ustar00rootroot00000000000000package 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) } golang-github-gophercloud-gophercloud-0.12.0/openstack/blockstorage/v3/snapshots/util.go000066400000000000000000000010071367513235700315010ustar00rootroot00000000000000package 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.12.0/openstack/blockstorage/v3/volumes/000077500000000000000000000000001367513235700276475ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/blockstorage/v3/volumes/doc.go000066400000000000000000000010741367513235700307450ustar00rootroot00000000000000/* 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. Example to create a Volume from a Backup backupID := "20c792f0-bb03-434f-b653-06ef238e337e" options := volumes.CreateOpts{ Name: "vol-001", BackupID: &backupID, } client.Microversion = "3.47" volume, err := volumes.Create(client, options).Extract() if err != nil { panic(err) } fmt.Println(volume) */ package volumes golang-github-gophercloud-gophercloud-0.12.0/openstack/blockstorage/v3/volumes/requests.go000066400000000000000000000157121367513235700320570ustar00rootroot00000000000000package 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,omitempty"` // 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"` // Specifies the backup ID, from which you want to create the volume. // Create a volume from a backup is supported since 3.47 microversion BackupID string `json:"backup_id,omitempty"` // The associated volume type VolumeType string `json:"volume_type,omitempty"` // Multiattach denotes if the volume is multi-attach capable. Multiattach bool `json:"multiattach,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 } resp, err := client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // DeleteOptsBuilder allows extensions to add additional parameters to the // Delete request. type DeleteOptsBuilder interface { ToVolumeDeleteQuery() (string, error) } // DeleteOpts contains options for deleting a Volume. This object is passed to // the volumes.Delete function. type DeleteOpts struct { // Delete all snapshots of this volume as well. Cascade bool `q:"cascade"` } // ToLoadBalancerDeleteQuery formats a DeleteOpts into a query string. func (opts DeleteOpts) ToVolumeDeleteQuery() (string, error) { q, err := gophercloud.BuildQueryString(opts) return q.String(), err } // Delete will delete the existing Volume with the provided ID. func Delete(client *gophercloud.ServiceClient, id string, opts DeleteOptsBuilder) (r DeleteResult) { url := deleteURL(client, id) if opts != nil { query, err := opts.ToVolumeDeleteQuery() if err != nil { r.Err = err return } url += query } resp, err := client.Delete(url, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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) { resp, err := client.Get(getURL(client, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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 } resp, err := client.Put(updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } golang-github-gophercloud-gophercloud-0.12.0/openstack/blockstorage/v3/volumes/results.go000066400000000000000000000123541367513235700317040ustar00rootroot00000000000000package 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"` // The backup ID, from which the volume was restored // This field is supported since 3.47 microversion BackupID *string `json:"backup_id"` // 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"` // Image metadata entries, only included for volumes that were created from an image, or from a snapshot of a volume originally created from an image. VolumeImageMetadata map[string]string `json:"volume_image_metadata"` } // 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 } golang-github-gophercloud-gophercloud-0.12.0/openstack/blockstorage/v3/volumes/testing/000077500000000000000000000000001367513235700313245ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/blockstorage/v3/volumes/testing/doc.go000066400000000000000000000000361367513235700324170ustar00rootroot00000000000000// volumes_v3 package testing golang-github-gophercloud-gophercloud-0.12.0/openstack/blockstorage/v3/volumes/testing/fixtures.go000066400000000000000000000166601367513235700335350ustar00rootroot00000000000000package 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", "volume_image_metadata": { "container_format": "bare", "image_name": "centos" }, "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" } } `) }) } func MockCreateVolumeFromBackupResponse(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", "backup_id": "20c792f0-bb03-434f-b653-06ef238e337e" } } `) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusAccepted) fmt.Fprintf(w, ` { "volume": { "size": 30, "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, "backup_id": "20c792f0-bb03-434f-b653-06ef238e337e", "multiattach": false } }`) }) } requests_test.go000066400000000000000000000200361367513235700345070ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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", volumes.DeleteOpts{}) th.AssertNoErr(t, res.Err) } func TestUpdate(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() MockUpdateResponse(t) var name = "vol-002" options := volumes.UpdateOpts{Name: &name} 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) th.AssertEquals(t, "centos", s.Volume.VolumeImageMetadata["image_name"]) err = volumes.Get(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").ExtractInto(s) if err == nil { t.Errorf("Expected error when providing non-pointer struct") } } func TestCreateFromBackup(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() MockCreateVolumeFromBackupResponse(t) options := volumes.CreateOpts{ Name: "vol-001", BackupID: "20c792f0-bb03-434f-b653-06ef238e337e", } v, err := volumes.Create(client.ServiceClient(), options).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, v.Size, 30) th.AssertEquals(t, v.ID, "d32019d3-bc6e-4319-9c1d-6722fc136a22") th.AssertEquals(t, *v.BackupID, "20c792f0-bb03-434f-b653-06ef238e337e") } golang-github-gophercloud-gophercloud-0.12.0/openstack/blockstorage/v3/volumes/urls.go000066400000000000000000000010261367513235700311620ustar00rootroot00000000000000package 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) } golang-github-gophercloud-gophercloud-0.12.0/openstack/blockstorage/v3/volumes/util.go000066400000000000000000000010051367513235700311470ustar00rootroot00000000000000package 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.12.0/openstack/blockstorage/v3/volumetypes/000077500000000000000000000000001367513235700305515ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/blockstorage/v3/volumetypes/doc.go000066400000000000000000000026451367513235700316540ustar00rootroot00000000000000/* 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 golang-github-gophercloud-gophercloud-0.12.0/openstack/blockstorage/v3/volumetypes/requests.go000066400000000000000000000114511367513235700327550ustar00rootroot00000000000000package 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 } resp, err := client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete will delete the existing Volume Type with the provided ID. func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { resp, err := client.Delete(deleteURL(client, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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) { resp, err := client.Get(getURL(client, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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 } resp, err := client.Put(updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } golang-github-gophercloud-gophercloud-0.12.0/openstack/blockstorage/v3/volumetypes/results.go000066400000000000000000000053551367513235700326110ustar00rootroot00000000000000package 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 } golang-github-gophercloud-gophercloud-0.12.0/openstack/blockstorage/v3/volumetypes/testing/000077500000000000000000000000001367513235700322265ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/blockstorage/v3/volumetypes/testing/doc.go000066400000000000000000000000401367513235700333140ustar00rootroot00000000000000// volume_types package testing fixtures.go000066400000000000000000000077001367513235700343530ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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.go000066400000000000000000000061221367513235700354110ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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 var name = "vol-type-002" options := volumetypes.UpdateOpts{ Name: &name, 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) } golang-github-gophercloud-gophercloud-0.12.0/openstack/blockstorage/v3/volumetypes/urls.go000066400000000000000000000010341367513235700320630ustar00rootroot00000000000000package 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.12.0/openstack/cdn/000077500000000000000000000000001367513235700237125ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/cdn/v1/000077500000000000000000000000001367513235700242405ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/cdn/v1/base/000077500000000000000000000000001367513235700251525ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/cdn/v1/base/doc.go000066400000000000000000000003251367513235700262460ustar00rootroot00000000000000// 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.12.0/openstack/cdn/v1/base/requests.go000066400000000000000000000011561367513235700273570ustar00rootroot00000000000000package 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) { resp, err := c.Get(getURL(c), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Ping retrieves a ping to the server. func Ping(c *gophercloud.ServiceClient) (r PingResult) { resp, err := c.Get(pingURL(c), nil, &gophercloud.RequestOpts{ OkCodes: []int{204}, MoreHeaders: map[string]string{"Accept": ""}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } golang-github-gophercloud-gophercloud-0.12.0/openstack/cdn/v1/base/results.go000066400000000000000000000011221367513235700271760ustar00rootroot00000000000000package 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.12.0/openstack/cdn/v1/base/testing/000077500000000000000000000000001367513235700266275ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/cdn/v1/base/testing/doc.go000066400000000000000000000000371367513235700277230ustar00rootroot00000000000000// cdn_base_v1 package testing golang-github-gophercloud-gophercloud-0.12.0/openstack/cdn/v1/base/testing/fixtures.go000066400000000000000000000027211367513235700310310ustar00rootroot00000000000000package 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) }) } golang-github-gophercloud-gophercloud-0.12.0/openstack/cdn/v1/base/testing/requests_test.go000066400000000000000000000020761367513235700320750ustar00rootroot00000000000000package 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.12.0/openstack/cdn/v1/base/urls.go000066400000000000000000000003331367513235700264650ustar00rootroot00000000000000package 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.12.0/openstack/cdn/v1/flavors/000077500000000000000000000000001367513235700257145ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/cdn/v1/flavors/doc.go000066400000000000000000000004261367513235700270120ustar00rootroot00000000000000// 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.12.0/openstack/cdn/v1/flavors/requests.go000066400000000000000000000011441367513235700301160ustar00rootroot00000000000000package 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) { resp, err := c.Get(getURL(c, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } golang-github-gophercloud-gophercloud-0.12.0/openstack/cdn/v1/flavors/results.go000066400000000000000000000034461367513235700277530ustar00rootroot00000000000000package 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.12.0/openstack/cdn/v1/flavors/testing/000077500000000000000000000000001367513235700273715ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/cdn/v1/flavors/testing/doc.go000066400000000000000000000000421367513235700304610ustar00rootroot00000000000000// cdn_flavors_v1 package testing golang-github-gophercloud-gophercloud-0.12.0/openstack/cdn/v1/flavors/testing/fixtures.go000066400000000000000000000044451367513235700316000ustar00rootroot00000000000000package 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" } ] } `) }) } golang-github-gophercloud-gophercloud-0.12.0/openstack/cdn/v1/flavors/testing/requests_test.go000066400000000000000000000035251367513235700326370ustar00rootroot00000000000000package 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.12.0/openstack/cdn/v1/flavors/urls.go000066400000000000000000000003711367513235700272310ustar00rootroot00000000000000package 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.12.0/openstack/cdn/v1/serviceassets/000077500000000000000000000000001367513235700271235ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/cdn/v1/serviceassets/doc.go000066400000000000000000000005611367513235700302210ustar00rootroot00000000000000// 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 golang-github-gophercloud-gophercloud-0.12.0/openstack/cdn/v1/serviceassets/requests.go000066400000000000000000000027521367513235700313330ustar00rootroot00000000000000package 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 } resp, err := c.Delete(url, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } golang-github-gophercloud-gophercloud-0.12.0/openstack/cdn/v1/serviceassets/results.go000066400000000000000000000002651367513235700311560ustar00rootroot00000000000000package serviceassets import "github.com/gophercloud/gophercloud" // DeleteResult represents the result of a Delete operation. type DeleteResult struct { gophercloud.ErrResult } golang-github-gophercloud-gophercloud-0.12.0/openstack/cdn/v1/serviceassets/testing/000077500000000000000000000000001367513235700306005ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/cdn/v1/serviceassets/testing/doc.go000066400000000000000000000000501367513235700316670ustar00rootroot00000000000000// cdn_serviceassets_v1 package testing golang-github-gophercloud-gophercloud-0.12.0/openstack/cdn/v1/serviceassets/testing/fixtures.go000066400000000000000000000011601367513235700327760ustar00rootroot00000000000000package 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) }) } golang-github-gophercloud-gophercloud-0.12.0/openstack/cdn/v1/serviceassets/testing/requests_test.go000066400000000000000000000007261367513235700340460ustar00rootroot00000000000000package 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) } golang-github-gophercloud-gophercloud-0.12.0/openstack/cdn/v1/serviceassets/urls.go000066400000000000000000000002661367513235700304430ustar00rootroot00000000000000package 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.12.0/openstack/cdn/v1/services/000077500000000000000000000000001367513235700260635ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/cdn/v1/services/doc.go000066400000000000000000000005061367513235700271600ustar00rootroot00000000000000// 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.12.0/openstack/cdn/v1/services/errors.go000066400000000000000000000001741367513235700277300ustar00rootroot00000000000000package services import "fmt" func no(str string) error { return fmt.Errorf("Required parameter %s not provided", str) } golang-github-gophercloud-gophercloud-0.12.0/openstack/cdn/v1/services/requests.go000066400000000000000000000226431367513235700302740ustar00rootroot00000000000000package 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, r.Err = gophercloud.ParseResponse(resp, 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) } resp, err := c.Get(url, &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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, r.Err = gophercloud.ParseResponse(resp, 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) } resp, err := c.Delete(url, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } golang-github-gophercloud-gophercloud-0.12.0/openstack/cdn/v1/services/results.go000066400000000000000000000223541367513235700301210ustar00rootroot00000000000000package 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.12.0/openstack/cdn/v1/services/testing/000077500000000000000000000000001367513235700275405ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/cdn/v1/services/testing/doc.go000066400000000000000000000000431367513235700306310ustar00rootroot00000000000000// cdn_services_v1 package testing golang-github-gophercloud-gophercloud-0.12.0/openstack/cdn/v1/services/testing/fixtures.go000066400000000000000000000257461367513235700317560ustar00rootroot00000000000000package 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) }) } golang-github-gophercloud-gophercloud-0.12.0/openstack/cdn/v1/services/testing/requests_test.go000066400000000000000000000171311367513235700330040ustar00rootroot00000000000000package 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.12.0/openstack/cdn/v1/services/urls.go000066400000000000000000000007741367513235700274070ustar00rootroot00000000000000package 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.12.0/openstack/client.go000066400000000000000000000405471367513235700247650ustar00rootroot00000000000000package openstack import ( "fmt" "reflect" "strings" "github.com/gophercloud/gophercloud" tokens2 "github.com/gophercloud/gophercloud/openstack/identity/v2/tokens" "github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/ec2tokens" "github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/oauth1" 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(provider, 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) err = client.SetTokenAndAuthResult(result) 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.SetThrowaway(true) tac.ReauthFunc = nil tac.SetTokenAndAuthResult(nil) tao := options tao.AllowReauth = false client.ReauthFunc = func() error { err := v2auth(&tac, endpoint, tao, eo) if err != nil { return err } client.CopyTokenFrom(&tac) return nil } } 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 } var catalog *tokens3.ServiceCatalog var tokenID string // passthroughToken allows to passthrough the token without a scope var passthroughToken bool switch v := opts.(type) { case *gophercloud.AuthOptions: tokenID = v.TokenID passthroughToken = (v.Scope == nil || *v.Scope == gophercloud.AuthScope{}) case *tokens3.AuthOptions: tokenID = v.TokenID passthroughToken = (v.Scope == tokens3.Scope{}) } if tokenID != "" && passthroughToken { // passing through the token ID without requesting a new scope if opts.CanReauth() { return fmt.Errorf("cannot use AllowReauth, when the token ID is defined and auth scope is not set") } v3Client.SetToken(tokenID) result := tokens3.Get(v3Client, tokenID) if result.Err != nil { return result.Err } err = client.SetTokenAndAuthResult(result) if err != nil { return err } catalog, err = result.ExtractServiceCatalog() if err != nil { return err } } else { var result tokens3.CreateResult switch opts.(type) { case *ec2tokens.AuthOptions: result = ec2tokens.Create(v3Client, opts) case *oauth1.AuthOptions: result = oauth1.Create(v3Client, opts) default: result = tokens3.Create(v3Client, opts) } err = client.SetTokenAndAuthResult(result) if err != nil { return err } catalog, err = result.ExtractServiceCatalog() if err != nil { return err } } 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.SetThrowaway(true) tac.ReauthFunc = nil tac.SetTokenAndAuthResult(nil) 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 case *ec2tokens.AuthOptions: o := *ot o.AllowReauth = false tao = &o case *oauth1.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.CopyTokenFrom(&tac) 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 } // NewBareMetalV1 creates a ServiceClient that may be used with the v1 // bare metal package. func NewBareMetalV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { return initClientOpts(client, eo, "baremetal") } // NewBareMetalIntrospectionV1 creates a ServiceClient that may be used with the v1 // bare metal introspection package. func NewBareMetalIntrospectionV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { return initClientOpts(client, eo, "baremetal-inspector") } // 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") // Fixes edge case having an OpenStack lb endpoint with trailing version number. endpoint := strings.Replace(sc.Endpoint, "v2.0/", "", -1) sc.ResourceBase = 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") } // NewPlacementV1 creates a ServiceClient that may be used with the placement package. func NewPlacementV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { return initClientOpts(client, eo, "placement") } golang-github-gophercloud-gophercloud-0.12.0/openstack/clustering/000077500000000000000000000000001367513235700253255ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/clustering/v1/000077500000000000000000000000001367513235700256535ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/clustering/v1/actions/000077500000000000000000000000001367513235700273135ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/clustering/v1/actions/doc.go000066400000000000000000000012751367513235700304140ustar00rootroot00000000000000/* 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 golang-github-gophercloud-gophercloud-0.12.0/openstack/clustering/v1/actions/requests.go000066400000000000000000000030541367513235700315170ustar00rootroot00000000000000package 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) { resp, err := client.Get(getURL(client, id), &r.Body, &gophercloud.RequestOpts{OkCodes: []int{200}}) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } golang-github-gophercloud-gophercloud-0.12.0/openstack/clustering/v1/actions/results.go000066400000000000000000000054521367513235700313510ustar00rootroot00000000000000package 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 } golang-github-gophercloud-gophercloud-0.12.0/openstack/clustering/v1/actions/testing/000077500000000000000000000000001367513235700307705ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/clustering/v1/actions/testing/doc.go000066400000000000000000000000511367513235700320600ustar00rootroot00000000000000// clustering_actions_v1 package testing golang-github-gophercloud-gophercloud-0.12.0/openstack/clustering/v1/actions/testing/fixtures.go000066400000000000000000000116721367513235700331770ustar00rootroot00000000000000package 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.go000066400000000000000000000020151367513235700341500ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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) } golang-github-gophercloud-gophercloud-0.12.0/openstack/clustering/v1/actions/urls.go000066400000000000000000000010071367513235700306250ustar00rootroot00000000000000package 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.12.0/openstack/clustering/v1/clusters/000077500000000000000000000000001367513235700275175ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/clustering/v1/clusters/doc.go000066400000000000000000000154531367513235700306230ustar00rootroot00000000000000/* 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.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.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) } Example to add nodes to a cluster addNodesOpts := clusters.AddNodesOpts{ Nodes: []string{"node-123"}, } clusterID := "b7b870e3-d3c5-4a93-b9d7-846c53b2c2da" actionID, err := clusters.AddNodes(serviceClient, clusterID, addNodesOpts).Extract() if err != nil { panic(err) } fmt.Println("action=", actionID) Example to remove nodes from a cluster removeNodesOpts := clusters.RemoveNodesOpts{ Nodes: []string{"node-123"}, } clusterID := "b7b870e3-d3c5-4a93-b9d7-846c53b2c2da" err := clusters.RemoveNodes(serviceClient, clusterID, removeNodesOpts).ExtractErr() if err != nil { panic(err) } Example to replace nodes for a cluster replaceNodesOpts := clusters.ReplaceNodesOpts{ Nodes: map[string]string{"node-1234": "node-5678"}, } clusterID := "b7b870e3-d3c5-4a93-b9d7-846c53b2c2da" actionID, err := clusters.ReplaceNodes(serviceClient, clusterID, replaceNodesOpts).Extract() if err != nil { panic(err) } Example to collect node attributes across a cluster serviceClient.Microversion = "1.2" clusterID := "b7b870e3-d3c5-4a93-b9d7-846c53b2c2da" opts := clusters.CollectOpts{ Path: "status", } attrs, err := clusters.Collect(serviceClient, clusterID, opts).Extract() if err != nil { panic(err) } Example to perform an operation on a cluster serviceClient.Microversion = "1.4" clusterID := "cluster123" operationOpts := clusters.OperationOpts{ Operation: clusters.RebootOperation, Filters: clusters.OperationFilters{"role": "slave"}, Params: clusters.OperationParams{"type": "SOFT"}, } actionID, err := clusters.Ops(serviceClient, clusterID, operationOpts).Extract() if err != nil { panic(err) } */ package clusters golang-github-gophercloud-gophercloud-0.12.0/openstack/clustering/v1/clusters/requests.go000066400000000000000000000500441367513235700317240ustar00rootroot00000000000000package clusters import ( "fmt" "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 } resp, err := client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201, 202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get retrieves details of a single cluster. func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { resp, err := client.Get(getURL(client, id), &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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 } resp, err := client.Patch(updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete deletes the specified cluster ID. func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { resp, err := client.Delete(deleteURL(client, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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 } resp, err := client.Post(actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201, 202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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 } resp, err := client.Post(actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201, 202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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 } resp, err := client.Post(actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201, 202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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 } resp, err := client.Post(actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201, 202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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 } resp, err := client.Post(actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201, 202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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 } resp, err := client.Post(actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201, 202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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) { resp, err := client.Get(getPolicyURL(client, clusterID, policyID), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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 } resp, err := client.Post(actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201, 202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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{}{}, } resp, err := client.Post(actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201, 202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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 } resp, err := client.Post(actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } func (opts AddNodesOpts) ToClusterAddNodeMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "add_nodes") } type AddNodesOpts struct { Nodes []string `json:"nodes" required:"true"` } func AddNodes(client *gophercloud.ServiceClient, id string, opts AddNodesOpts) (r ActionResult) { b, err := opts.ToClusterAddNodeMap() if err != nil { r.Err = err return } resp, err := client.Post(nodeURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } func (opts RemoveNodesOpts) ToClusterRemoveNodeMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "del_nodes") } type RemoveNodesOpts struct { Nodes []string `json:"nodes" required:"true"` } func RemoveNodes(client *gophercloud.ServiceClient, clusterID string, opts RemoveNodesOpts) (r DeleteResult) { b, err := opts.ToClusterRemoveNodeMap() if err != nil { r.Err = err return } resp, err := client.Post(nodeURL(client, clusterID), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } func (opts ReplaceNodesOpts) ToClusterReplaceNodeMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "replace_nodes") } type ReplaceNodesOpts struct { Nodes map[string]string `json:"nodes" required:"true"` } func ReplaceNodes(client *gophercloud.ServiceClient, id string, opts ReplaceNodesOpts) (r ActionResult) { b, err := opts.ToClusterReplaceNodeMap() if err != nil { r.Err = err return } resp, err := client.Post(nodeURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } type CollectOptsBuilder interface { ToClusterCollectMap() (string, error) } // CollectOpts represents options to collect attribute values across a cluster type CollectOpts struct { Path string `q:"path" required:"true"` } func (opts CollectOpts) ToClusterCollectMap() (string, error) { return opts.Path, nil } // Collect instructs OpenStack to aggregate attribute values across a cluster func Collect(client *gophercloud.ServiceClient, id string, opts CollectOptsBuilder) (r CollectResult) { query, err := opts.ToClusterCollectMap() if err != nil { r.Err = err return } resp, err := client.Get(collectURL(client, id, query), &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // OperationName represents valid values for cluster operation type OperationName string const ( // Nova Profile Op Names RebootOperation OperationName = "reboot" RebuildOperation OperationName = "rebuild" ChangePasswordOperation OperationName = "change_password" PauseOperation OperationName = "pause" UnpauseOperation OperationName = "unpause" SuspendOperation OperationName = "suspend" ResumeOperation OperationName = "resume" LockOperation OperationName = "lock" UnlockOperation OperationName = "unlock" StartOperation OperationName = "start" StopOperation OperationName = "stop" RescueOperation OperationName = "rescue" UnrescueOperation OperationName = "unrescue" EvacuateOperation OperationName = "evacuate" // Heat Pofile Op Names AbandonOperation OperationName = "abandon" ) // ToClusterOperationMap constructs a request body from OperationOpts. func (opts OperationOpts) ToClusterOperationMap() (map[string]interface{}, error) { operationArg := struct { Filters OperationFilters `json:"filters,omitempty"` Params OperationParams `json:"params,omitempty"` }{ Filters: opts.Filters, Params: opts.Params, } return gophercloud.BuildRequestBody(operationArg, string(opts.Operation)) } // OperationOptsBuilder allows extensions to add additional parameters to the // Op request. type OperationOptsBuilder interface { ToClusterOperationMap() (map[string]interface{}, error) } type OperationFilters map[string]interface{} type OperationParams map[string]interface{} // OperationOpts represents options used to perform an operation on a cluster type OperationOpts struct { Operation OperationName `json:"operation" required:"true"` Filters OperationFilters `json:"filters,omitempty"` Params OperationParams `json:"params,omitempty"` } func Ops(client *gophercloud.ServiceClient, id string, opts OperationOptsBuilder) (r ActionResult) { b, err := opts.ToClusterOperationMap() if err != nil { r.Err = err return } resp, err := client.Post(opsURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } golang-github-gophercloud-gophercloud-0.12.0/openstack/clustering/v1/clusters/results.go000066400000000000000000000137731367513235700315620ustar00rootroot00000000000000package 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"` } type ClusterAttributes struct { ID string `json:"id"` Value interface{} `json:"value"` } // 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 } type CollectResult struct { gophercloud.Result } // 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 } // Extract returns collected attributes across a cluster func (r CollectResult) Extract() ([]ClusterAttributes, error) { var s struct { Attributes []ClusterAttributes `json:"cluster_attributes"` } err := r.ExtractInto(&s) return s.Attributes, err } golang-github-gophercloud-gophercloud-0.12.0/openstack/clustering/v1/clusters/testing/000077500000000000000000000000001367513235700311745ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/clustering/v1/clusters/testing/doc.go000066400000000000000000000000521367513235700322650ustar00rootroot00000000000000// clustering_clusters_v1 package testing golang-github-gophercloud-gophercloud-0.12.0/openstack/clustering/v1/clusters/testing/fixtures.go000066400000000000000000000520601367513235700333770ustar00rootroot00000000000000package 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 OperationActionResponse = ` { "action": "2a0ff107-e789-4660-a122-3816c43af703" }` const OperationExpectedActionID = "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" } }` const CollectResponse = ` { "cluster_attributes": [{ "id": "foo", "value": "bar" } ] }` var ExpectedCollectAttributes = []clusters.ClusterAttributes{ { ID: "foo", Value: string("bar"), }, } 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) }) } func HandleAddNodesSuccessfully(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.Header().Add("X-OpenStack-Request-ID", "req-781e9bdc-4163-46eb-91c9-786c53188bbb") w.WriteHeader(http.StatusAccepted) fmt.Fprint(w, ActionResponse) }) } func HandleRemoveNodesSuccessfully(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.Header().Add("X-OpenStack-Request-ID", "req-781e9bdc-4163-46eb-91c9-786c53188bbb") w.WriteHeader(http.StatusAccepted) fmt.Fprint(w, ActionResponse) }) } func HandleReplaceNodeSuccessfully(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.Header().Add("X-OpenStack-Request-ID", "req-781e9bdc-4163-46eb-91c9-786c53188bbb") w.WriteHeader(http.StatusAccepted) fmt.Fprint(w, ActionResponse) }) } func HandleClusterCollectSuccessfully(t *testing.T) { th.Mux.HandleFunc("/v1/clusters/7d85f602-a948-4a30-afd4-e84f47471c15/attrs/foo.bar", 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", "req-781e9bdc-4163-46eb-91c9-786c53188bbb") w.WriteHeader(http.StatusOK) fmt.Fprint(w, CollectResponse) }) } func HandleOpsSuccessfully(t *testing.T) { th.Mux.HandleFunc("/v1/clusters/7d85f602-a948-4a30-afd4-e84f47471c15/ops", 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.StatusAccepted) fmt.Fprint(w, OperationActionResponse) }) } requests_test.go000066400000000000000000000316721367513235700343670ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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) } func TestAddNodes(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleAddNodesSuccessfully(t) opts := clusters.AddNodesOpts{ Nodes: []string{"node1", "node2", "node3"}, } result, err := clusters.AddNodes(fake.ServiceClient(), "7d85f602-a948-4a30-afd4-e84f47471c15", opts).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, result, "2a0ff107-e789-4660-a122-3816c43af703") } func TestRemoveNodes(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleRemoveNodesSuccessfully(t) opts := clusters.RemoveNodesOpts{ Nodes: []string{"node1", "node2", "node3"}, } err := clusters.RemoveNodes(fake.ServiceClient(), "7d85f602-a948-4a30-afd4-e84f47471c15", opts).ExtractErr() th.AssertNoErr(t, err) } func TestReplaceNodes(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleReplaceNodeSuccessfully(t) opts := clusters.ReplaceNodesOpts{ Nodes: map[string]string{"node-1234": "node-5678"}, } actionID, err := clusters.ReplaceNodes(fake.ServiceClient(), "7d85f602-a948-4a30-afd4-e84f47471c15", opts).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, actionID, "2a0ff107-e789-4660-a122-3816c43af703") } func TestClusterCollect(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleClusterCollectSuccessfully(t) opts := clusters.CollectOpts{ Path: "foo.bar", } attributes, err := clusters.Collect(fake.ServiceClient(), "7d85f602-a948-4a30-afd4-e84f47471c15", opts).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, ExpectedCollectAttributes, attributes) } func TestOperation(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleOpsSuccessfully(t) clusterOpts := clusters.OperationOpts{ Operation: clusters.PauseOperation, Filters: clusters.OperationFilters{"role": "slave"}, Params: clusters.OperationParams{"type": "soft"}, } actual, err := clusters.Ops(fake.ServiceClient(), "7d85f602-a948-4a30-afd4-e84f47471c15", clusterOpts).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, OperationExpectedActionID, actual) } golang-github-gophercloud-gophercloud-0.12.0/openstack/clustering/v1/clusters/urls.go000066400000000000000000000032121367513235700310310ustar00rootroot00000000000000package 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) } func nodeURL(client *gophercloud.ServiceClient, id string) string { return actionURL(client, id) } func collectURL(client *gophercloud.ServiceClient, clusterID string, path string) string { return client.ServiceURL(apiVersion, apiName, clusterID, "attrs", path) } func opsURL(client *gophercloud.ServiceClient, id string) string { return client.ServiceURL(apiVersion, apiName, id, "ops") } golang-github-gophercloud-gophercloud-0.12.0/openstack/clustering/v1/events/000077500000000000000000000000001367513235700271575ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/clustering/v1/events/doc.go000066400000000000000000000012521367513235700302530ustar00rootroot00000000000000/* Package events provides listing and retrieving of senlin events for the OpenStack Clustering Service. Example to List Events opts := events.ListOpts{ Limit: 5, } err = events.List(serviceClient, opts).EachPage(func(page pagination.Page) (bool, error) { eventInfos, err := events.ExtractEvents(page) if err != nil { return false, err } for _, eventInfo := range eventInfos { fmt.Println("%+v\n", eventInfo) } return true, nil }) Example to Get an Event eventID := "edce3528-864f-41fb-8759-f4707925cc09" event, err := events.Get(serviceClient, eventID).Extract() if err != nil { panic(err) } fmt.Printf("Event %+v: ", event) */ package events golang-github-gophercloud-gophercloud-0.12.0/openstack/clustering/v1/events/requests.go000066400000000000000000000033021367513235700313570ustar00rootroot00000000000000package events import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // ListOptsBuilder allows extensions to add additional parameters to the // List request. type ListOptsBuilder interface { ToEventListQuery() (string, error) } // ListOpts represents options used to list events. type ListOpts struct { Limit int `q:"limit,omitempty"` Level int `q:"level,omitempty"` Marker string `q:"marker,omitempty"` Sort string `q:"sort,omitempty"` GlobalProject *bool `q:"global_project,omitempty"` OID string `q:"oid,omitempty"` OType string `q:"otype,omitempty"` OName string `q:"oname,omitempty"` ClusterID string `q:"cluster_id,omitempty"` Action string `q:"action"` } // ToEventListQuery builds a query string from ListOpts. func (opts ListOpts) ToEventListQuery() (string, error) { q, err := gophercloud.BuildQueryString(opts) return q.String(), err } // List instructs OpenStack to provide a list of events. func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { url := listURL(client) if opts != nil { query, err := opts.ToEventListQuery() if err != nil { return pagination.Pager{Err: err} } url += query } return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { return EventPage{pagination.LinkedPageBase{PageResult: r}} }) } // Get retrieves details of a single event. func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { resp, err := client.Get(getURL(client, id), &r.Body, &gophercloud.RequestOpts{OkCodes: []int{200}}) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } golang-github-gophercloud-gophercloud-0.12.0/openstack/clustering/v1/events/results.go000066400000000000000000000036101367513235700312070ustar00rootroot00000000000000package events import ( "time" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // Event represents a detailed Event. type Event struct { Action string `json:"action"` Cluster string `json:"cluster"` ClusterID string `json:"cluster_id"` ID string `json:"id"` Level string `json:"level"` Metadata map[string]interface{} `json:"meta_data"` OID string `json:"oid"` OName string `json:"oname"` OType string `json:"otype"` Project string `json:"project"` Status string `json:"status"` StatusReason string `json:"status_reason"` Timestamp time.Time `json:"timestamp"` User string `json:"user"` } // commonResult is the response of a base result. type commonResult struct { gophercloud.Result } // Extract interprets any commonResult-based result as an Event. func (r commonResult) Extract() (*Event, error) { var s struct { Event *Event `json:"event"` } err := r.ExtractInto(&s) return s.Event, err } // GetResult is the response of a Get operations. Call its Extract method to // interpret it as an Event. type GetResult struct { commonResult } // EventPage contains a single page of all events from a List call. type EventPage struct { pagination.LinkedPageBase } // IsEmpty determines if a EventPage contains any results. func (r EventPage) IsEmpty() (bool, error) { events, err := ExtractEvents(r) return len(events) == 0, err } // ExtractEvents returns a slice of Events from the List operation. func ExtractEvents(r pagination.Page) ([]Event, error) { var s struct { Events []Event `json:"events"` } err := (r.(EventPage)).ExtractInto(&s) return s.Events, err } golang-github-gophercloud-gophercloud-0.12.0/openstack/clustering/v1/events/testing/000077500000000000000000000000001367513235700306345ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/clustering/v1/events/testing/doc.go000066400000000000000000000000501367513235700317230ustar00rootroot00000000000000// clustering_events_v1 package testing golang-github-gophercloud-gophercloud-0.12.0/openstack/clustering/v1/events/testing/fixtures.go000066400000000000000000000067001367513235700330370ustar00rootroot00000000000000package testing import ( "fmt" "net/http" "testing" "time" "github.com/gophercloud/gophercloud/openstack/clustering/v1/events" th "github.com/gophercloud/gophercloud/testhelper" fake "github.com/gophercloud/gophercloud/testhelper/client" ) const ListResponse = ` { "events": [ { "action": "CLUSTER_CREATE", "cluster": null, "cluster_id": null, "id": "edce3528-864f-41fb-8759-f4707925cc09", "level": "INFO", "meta_data": {}, "oid": "0df0931b-e251-4f2e-8719-4ebfda3627ba", "oname": "cluster001", "otype": "CLUSTER", "project": "f1fe61dcda2f4618a14c10dc7abc214d", "status": "start", "status_reason": "Initializing", "timestamp": "2015-03-05T08:53:15Z", "user": "8bcd2cdca7684c02afc9e4f2fc0f0c79" }, { "action": "NODE_DELETE", "cluster": null, "cluster_id": null, "id": "abcd1234-864f-41fb-8759-f4707925dd10", "level": "INFO", "meta_data": {}, "oid": "0df0931b-e251-4f2e-8719-4ebfda3627ba", "oname": "node119", "otype": "node", "project": "f1fe61dcda2f4618a14c10dc7abc214d", "status": "start", "status_reason": "84492c96", "timestamp": "2015-03-06T18:53:15Z", "user": "8bcd2cdca7684c02afc9e4f2fc0f0c79" } ] } ` const GetResponse = ` { "event": { "action": "CLUSTER_CREATE", "cluster_id": null, "id": "edce3528-864f-41fb-8759-f4707925cc09", "level": "INFO", "meta_data": {}, "oid": "0df0931b-e251-4f2e-8719-4ebfda3627ba", "oname": "cluster001", "otype": "CLUSTER", "project": "f1fe61dcda2f4618a14c10dc7abc214d", "status": "start", "status_reason": "Initializing", "timestamp": "2015-03-05T08:53:15Z", "user": "8bcd2cdca7684c02afc9e4f2fc0f0c79" } } ` var ExpectedEvent1 = events.Event{ Action: "CLUSTER_CREATE", Cluster: "", ClusterID: "", ID: "edce3528-864f-41fb-8759-f4707925cc09", Level: "INFO", Metadata: map[string]interface{}{}, OID: "0df0931b-e251-4f2e-8719-4ebfda3627ba", OName: "cluster001", OType: "CLUSTER", Project: "f1fe61dcda2f4618a14c10dc7abc214d", Status: "start", StatusReason: "Initializing", Timestamp: time.Date(2015, 3, 5, 8, 53, 15, 0, time.UTC), User: "8bcd2cdca7684c02afc9e4f2fc0f0c79", } var ExpectedEvent2 = events.Event{ Action: "NODE_DELETE", Cluster: "", ClusterID: "", ID: "abcd1234-864f-41fb-8759-f4707925dd10", Level: "INFO", Metadata: map[string]interface{}{}, OID: "0df0931b-e251-4f2e-8719-4ebfda3627ba", OName: "node119", OType: "node", Project: "f1fe61dcda2f4618a14c10dc7abc214d", Status: "start", StatusReason: "84492c96", Timestamp: time.Date(2015, 3, 6, 18, 53, 15, 0, time.UTC), User: "8bcd2cdca7684c02afc9e4f2fc0f0c79", } var ExpectedEvents = []events.Event{ExpectedEvent1, ExpectedEvent2} func HandleListSuccessfully(t *testing.T) { th.Mux.HandleFunc("/v1/events", 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/events/"+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) }) } golang-github-gophercloud-gophercloud-0.12.0/openstack/clustering/v1/events/testing/requests_test.go000066400000000000000000000020031367513235700340700ustar00rootroot00000000000000package testing import ( "testing" "github.com/gophercloud/gophercloud/openstack/clustering/v1/events" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" fake "github.com/gophercloud/gophercloud/testhelper/client" ) func TestListEvents(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleListSuccessfully(t) pageCount := 0 err := events.List(fake.ServiceClient(), nil).EachPage(func(page pagination.Page) (bool, error) { pageCount++ actual, err := events.ExtractEvents(page) th.AssertNoErr(t, err) th.AssertDeepEquals(t, ExpectedEvents, actual) return true, nil }) th.AssertNoErr(t, err) if pageCount != 1 { t.Errorf("Expected 1 page, got %d", pageCount) } } func TestGetEvent(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleGetSuccessfully(t, ExpectedEvent1.ID) actual, err := events.Get(fake.ServiceClient(), ExpectedEvent1.ID).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, ExpectedEvent1, *actual) } golang-github-gophercloud-gophercloud-0.12.0/openstack/clustering/v1/events/urls.go000066400000000000000000000010051367513235700304670ustar00rootroot00000000000000package events import "github.com/gophercloud/gophercloud" var apiVersion = "v1" var apiName = "events" 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.12.0/openstack/clustering/v1/nodes/000077500000000000000000000000001367513235700267635ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/clustering/v1/nodes/doc.go000066400000000000000000000044031367513235700300600ustar00rootroot00000000000000/* Package nodes provides information and interaction with the nodes through the OpenStack Clustering service. Example to Create a Node createOpts := 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, createOpts).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) Example to Perform an Operation on a Node serviceClient.Microversion = "1.4" nodeID := "node123" operationOpts := nodes.OperationOpts{ Operation: nodes.RebootOperation, Params: nodes.OperationParams{"type": "SOFT"}, } actionID, err := nodes.Ops(serviceClient, nodeID, operationOpts).Extract() if err != nil { panic(err) } Example to Recover a Node nodeID := "b7b870e3-d3c5-4a93-b9d7-846c53b2c2da" check := true recoverOpts := nodes.RecoverOpts{ Operation: nodes.RebuildRecovery, Check: &check, } actionID, err := nodes.Recover(computeClient, nodeID, recoverOpts).Extract() if err != nil { panic(err) } fmt.Println("action=", actionID) Example to Check a Node nodeID := "b7b870e3-d3c5-4a93-b9d7-846c53b2c2da" actionID, err := nodes.Check(serviceClient, nodeID).Extract() if err != nil { panic(err) } fmt.Println("action=", actionID) */ package nodes golang-github-gophercloud-gophercloud-0.12.0/openstack/clustering/v1/nodes/requests.go000066400000000000000000000163601367513235700311730ustar00rootroot00000000000000package nodes import ( "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 } resp, err := client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201, 202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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"` } // ToNodeUpdateMap 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 } resp, err := client.Patch(updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201, 202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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) { resp, err := client.Delete(deleteURL(client, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get makes a request against senlin to get a details of a node type func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { resp, err := client.Get(getURL(client, id), &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // OperationName represents valid values for node operation type OperationName string const ( // Nova Profile Op Names RebootOperation OperationName = "reboot" RebuildOperation OperationName = "rebuild" ChangePasswordOperation OperationName = "change_password" PauseOperation OperationName = "pause" UnpauseOperation OperationName = "unpause" SuspendOperation OperationName = "suspend" ResumeOperation OperationName = "resume" LockOperation OperationName = "lock" UnlockOperation OperationName = "unlock" StartOperation OperationName = "start" StopOperation OperationName = "stop" RescueOperation OperationName = "rescue" UnrescueOperation OperationName = "unrescue" EvacuateOperation OperationName = "evacuate" // Heat Pofile Op Names AbandonOperation OperationName = "abandon" ) // ToNodeOperationMap constructs a request body from OperationOpts. func (opts OperationOpts) ToNodeOperationMap() (map[string]interface{}, error) { optsMap := map[string]interface{}{string(opts.Operation): opts.Params} return optsMap, nil } // OperationOptsBuilder allows extensions to add additional parameters to the // Op request. type OperationOptsBuilder interface { ToNodeOperationMap() (map[string]interface{}, error) } type OperationParams map[string]interface{} // OperationOpts represents options used to perform an operation on a node type OperationOpts struct { Operation OperationName `json:"operation" required:"true"` Params OperationParams `json:"params,omitempty"` } func Ops(client *gophercloud.ServiceClient, id string, opts OperationOptsBuilder) (r ActionResult) { b, err := opts.ToNodeOperationMap() if err != nil { r.Err = err return } resp, err := client.Post(opsURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } func (opts RecoverOpts) ToNodeRecoverMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "recover") } // RecoverAction represents valid values for recovering a node. type RecoverAction string const ( RebootRecovery RecoverAction = "REBOOT" RebuildRecovery RecoverAction = "REBUILD" // RECREATE currently is NOT supported. See https://github.com/openstack/senlin/blob/b30b2b8496b2b8af243ccd5292f38aec7a95664f/senlin/profiles/base.py#L533 RecreateRecovery RecoverAction = "RECREATE" ) type RecoverOpts struct { Operation RecoverAction `json:"operation,omitempty"` Check *bool `json:"check,omitempty"` } func Recover(client *gophercloud.ServiceClient, id string, opts RecoverOpts) (r ActionResult) { b, err := opts.ToNodeRecoverMap() if err != nil { r.Err = err return } resp, err := client.Post(actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } func Check(client *gophercloud.ServiceClient, id string) (r ActionResult) { b := map[string]interface{}{ "check": map[string]interface{}{}, } resp, err := client.Post(actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } golang-github-gophercloud-gophercloud-0.12.0/openstack/clustering/v1/nodes/results.go000066400000000000000000000071731367513235700310230ustar00rootroot00000000000000package 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 } // 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 } golang-github-gophercloud-gophercloud-0.12.0/openstack/clustering/v1/nodes/testing/000077500000000000000000000000001367513235700304405ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/clustering/v1/nodes/testing/doc.go000066400000000000000000000000471367513235700315350ustar00rootroot00000000000000// clustering_nodes_v1 package testing golang-github-gophercloud-gophercloud-0.12.0/openstack/clustering/v1/nodes/testing/fixtures.go000066400000000000000000000265711367513235700326530ustar00rootroot00000000000000package 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", } const OperationActionResponse = ` { "action": "2a0ff107-e789-4660-a122-3816c43af703" }` const OperationExpectedActionID = "2a0ff107-e789-4660-a122-3816c43af703" const ActionResponse = ` { "action": "2a0ff107-e789-4660-a122-3816c43af703" }` const ExpectedActionID = "2a0ff107-e789-4660-a122-3816c43af703" 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) }) } func HandleOpsSuccessfully(t *testing.T) { th.Mux.HandleFunc("/v1/nodes/7d85f602-a948-4a30-afd4-e84f47471c15/ops", 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.StatusAccepted) fmt.Fprint(w, OperationActionResponse) }) } func HandleRecoverSuccessfully(t *testing.T) { th.Mux.HandleFunc("/v1/nodes/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-edce3528-864f-41fb-8759-f4707925cc09") w.WriteHeader(http.StatusAccepted) fmt.Fprint(w, ActionResponse) }) } func HandleCheckSuccessfully(t *testing.T) { th.Mux.HandleFunc("/v1/nodes/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-edce3528-864f-41fb-8759-f4707925cc09") w.WriteHeader(http.StatusAccepted) fmt.Fprint(w, ActionResponse) }) } golang-github-gophercloud-gophercloud-0.12.0/openstack/clustering/v1/nodes/testing/requests_test.go000066400000000000000000000073111367513235700337030ustar00rootroot00000000000000package 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) } func TestOpsNode(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleOpsSuccessfully(t) nodeOpts := nodes.OperationOpts{ Operation: nodes.PauseOperation, } actual, err := nodes.Ops(fake.ServiceClient(), "7d85f602-a948-4a30-afd4-e84f47471c15", nodeOpts).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, OperationExpectedActionID, actual) } func TestNodeRecover(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleRecoverSuccessfully(t) recoverOpts := nodes.RecoverOpts{ Operation: nodes.RebuildRecovery, Check: new(bool), } actionID, err := nodes.Recover(fake.ServiceClient(), "edce3528-864f-41fb-8759-f4707925cc09", recoverOpts).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, ExpectedActionID, actionID) } func TestNodeCheck(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleCheckSuccessfully(t) actionID, err := nodes.Check(fake.ServiceClient(), "edce3528-864f-41fb-8759-f4707925cc09").Extract() th.AssertNoErr(t, err) th.AssertEquals(t, ExpectedActionID, actionID) } golang-github-gophercloud-gophercloud-0.12.0/openstack/clustering/v1/nodes/urls.go000066400000000000000000000020501367513235700302740ustar00rootroot00000000000000package nodes import "github.com/gophercloud/gophercloud" var apiVersion = "v1" var apiName = "nodes" func commonURL(client *gophercloud.ServiceClient) string { return client.ServiceURL(apiVersion, apiName) } 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 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) } func opsURL(client *gophercloud.ServiceClient, id string) string { return client.ServiceURL(apiVersion, apiName, id, "ops") } golang-github-gophercloud-gophercloud-0.12.0/openstack/clustering/v1/policies/000077500000000000000000000000001367513235700274625ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/clustering/v1/policies/doc.go000066400000000000000000000035741367513235700305670ustar00rootroot00000000000000/* 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 createOpts := 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, createOpts).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 golang-github-gophercloud-gophercloud-0.12.0/openstack/clustering/v1/policies/requests.go000066400000000000000000000122301367513235700316620ustar00rootroot00000000000000package 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 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 } resp, err := client.Post(policyCreateURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{201}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete makes a request against the API to delete a policy. func Delete(client *gophercloud.ServiceClient, policyID string) (r DeleteResult) { resp, err := client.Delete(policyDeleteURL(client, policyID), &gophercloud.RequestOpts{ OkCodes: []int{204}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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 } resp, err := client.Patch(updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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 } resp, err := client.Post(validateURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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) resp, err := client.Get(url, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } golang-github-gophercloud-gophercloud-0.12.0/openstack/clustering/v1/policies/results.go000066400000000000000000000105371367513235700315200ustar00rootroot00000000000000package 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 } func (r Spec) MarshalJSON() ([]byte, error) { spec := struct { Type string `json:"type"` Version string `json:"version"` Properties map[string]interface{} `json:"properties"` }{ Type: r.Type, Version: r.Version, Properties: r.Properties, } return json.Marshal(spec) } // 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 } golang-github-gophercloud-gophercloud-0.12.0/openstack/clustering/v1/policies/testing/000077500000000000000000000000001367513235700311375ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/clustering/v1/policies/testing/doc.go000066400000000000000000000000521367513235700322300ustar00rootroot00000000000000// clustering_policies_v1 package testing golang-github-gophercloud-gophercloud-0.12.0/openstack/clustering/v1/policies/testing/fixtures.go000066400000000000000000000334701367513235700333460ustar00rootroot00000000000000package 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.go000066400000000000000000000061211367513235700343210ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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) } golang-github-gophercloud-gophercloud-0.12.0/openstack/clustering/v1/policies/urls.go000066400000000000000000000015651367513235700310050ustar00rootroot00000000000000package 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.12.0/openstack/clustering/v1/policytypes/000077500000000000000000000000001367513235700302375ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/clustering/v1/policytypes/doc.go000066400000000000000000000013571367513235700313410ustar00rootroot00000000000000/* 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 golang-github-gophercloud-gophercloud-0.12.0/openstack/clustering/v1/policytypes/requests.go000066400000000000000000000014361367513235700324450ustar00rootroot00000000000000package 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) resp, err := client.Get(url, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } golang-github-gophercloud-gophercloud-0.12.0/openstack/clustering/v1/policytypes/results.go000066400000000000000000000040731367513235700322730ustar00rootroot00000000000000package 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 } golang-github-gophercloud-gophercloud-0.12.0/openstack/clustering/v1/policytypes/testing/000077500000000000000000000000001367513235700317145ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/clustering/v1/policytypes/testing/doc.go000066400000000000000000000000551367513235700330100ustar00rootroot00000000000000// clustering_policytypes_v1 package testing golang-github-gophercloud-gophercloud-0.12.0/openstack/clustering/v1/policytypes/testing/fixtures.go000066400000000000000000000114151367513235700341160ustar00rootroot00000000000000package 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.go000066400000000000000000000021241367513235700350750ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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) } golang-github-gophercloud-gophercloud-0.12.0/openstack/clustering/v1/policytypes/urls.go000066400000000000000000000006141367513235700315540ustar00rootroot00000000000000package 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.12.0/openstack/clustering/v1/profiles/000077500000000000000000000000001367513235700274765ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/clustering/v1/profiles/doc.go000066400000000000000000000043201367513235700305710ustar00rootroot00000000000000/* 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) } Example to Validate a profile serviceClient.Microversion = "1.2" validateOpts := profiles.ValidateOpts{ Spec: profiles.Spec{ Properties: map[string]interface{}{ "flavor": "t2.micro", "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", }, } profile, err := profiles.Validate(serviceClient, validateOpts).Extract() if err != nil { panic(err) } */ package profiles golang-github-gophercloud-gophercloud-0.12.0/openstack/clustering/v1/profiles/requests.go000066400000000000000000000111551367513235700317030ustar00rootroot00000000000000package profiles import ( "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 } resp, err := client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get retrieves detail of a single profile. func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { resp, err := client.Get(getURL(client, id), &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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 } resp, err := client.Patch(updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete deletes the specified profile via profile id. func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { resp, err := client.Delete(deleteURL(client, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // ValidateOptsBuilder allows extensions to add additional parameters to the // Validate request. type ValidateOptsBuilder interface { ToProfileValidateMap() (map[string]interface{}, error) } // ValidateOpts params type ValidateOpts struct { Spec Spec `json:"spec" required:"true"` } // ToProfileValidateMap formats a CreateOpts into a body map. func (opts ValidateOpts) ToProfileValidateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "profile") } // Validate profile. func Validate(client *gophercloud.ServiceClient, opts ValidateOpts) (r ValidateResult) { b, err := opts.ToProfileValidateMap() if err != nil { r.Err = err return } resp, err := client.Post(validateURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } golang-github-gophercloud-gophercloud-0.12.0/openstack/clustering/v1/profiles/results.go000066400000000000000000000074261367513235700315370ustar00rootroot00000000000000package 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 } func (r Spec) MarshalJSON() ([]byte, error) { spec := struct { Type string `json:"type"` Version string `json:"version"` Properties map[string]interface{} `json:"properties"` }{ Type: r.Type, Version: r.Version, Properties: r.Properties, } return json.Marshal(spec) } // 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 } // ValidateResult is the response of a Validate operations. type ValidateResult struct { commonResult } // 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 } golang-github-gophercloud-gophercloud-0.12.0/openstack/clustering/v1/profiles/testing/000077500000000000000000000000001367513235700311535ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/clustering/v1/profiles/testing/doc.go000066400000000000000000000000521367513235700322440ustar00rootroot00000000000000// clustering_profiles_v1 package testing golang-github-gophercloud-gophercloud-0.12.0/openstack/clustering/v1/profiles/testing/fixtures.go000066400000000000000000000300101367513235700333450ustar00rootroot00000000000000package 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", } const ValidateResponse = ` { "profile": { "created_at": "2016-01-03T16:22:23Z", "domain": null, "id": "9e1c6f42-acf5-4688-be2c-8ce954ef0f23", "metadata": {}, "name": "pserver", "project": "42d9e9663331431f97b75e25136307ff", "spec": { "properties": { "flavor": "t2.micro", "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 ExpectedValidate = 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.micro", "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) }) } func HandleValidateSuccessfully(t *testing.T) { th.Mux.HandleFunc("/v1/profiles/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, "OpenStack-API-Version", "clustering 1.2") w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprint(w, ValidateResponse) }) } requests_test.go000066400000000000000000000062111367513235700343350ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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()) } func TestValidateProfile(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleValidateSuccessfully(t) validateOpts := profiles.ValidateOpts{ Spec: profiles.Spec{ Properties: map[string]interface{}{ "flavor": "t2.micro", "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", }, } client := fake.ServiceClient() client.Microversion = "1.2" client.Type = "clustering" profile, err := profiles.Validate(client, validateOpts).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, ExpectedValidate, *profile) } golang-github-gophercloud-gophercloud-0.12.0/openstack/clustering/v1/profiles/urls.go000066400000000000000000000016421367513235700310150ustar00rootroot00000000000000package 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) } func validateURL(client *gophercloud.ServiceClient) string { return client.ServiceURL(apiVersion, apiName, "validate") } golang-github-gophercloud-gophercloud-0.12.0/openstack/clustering/v1/profiletypes/000077500000000000000000000000001367513235700304005ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/clustering/v1/profiletypes/doc.go000066400000000000000000000021021367513235700314670ustar00rootroot00000000000000/* 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 golang-github-gophercloud-gophercloud-0.12.0/openstack/clustering/v1/profiletypes/requests.go000066400000000000000000000016501367513235700326040ustar00rootroot00000000000000package profiletypes import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { resp, err := client.Get(getURL(client, id), &r.Body, &gophercloud.RequestOpts{OkCodes: []int{200}}) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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)} }) } golang-github-gophercloud-gophercloud-0.12.0/openstack/clustering/v1/profiletypes/results.go000066400000000000000000000035741367513235700324410ustar00rootroot00000000000000package 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 List 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 } golang-github-gophercloud-gophercloud-0.12.0/openstack/clustering/v1/profiletypes/testing/000077500000000000000000000000001367513235700320555ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/clustering/v1/profiletypes/testing/doc.go000066400000000000000000000000561367513235700331520ustar00rootroot00000000000000// clustering_profiletypes_v1 package testing fixtures.go000066400000000000000000000162261367513235700342050ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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.go000066400000000000000000000035301367513235700352400ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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) } } golang-github-gophercloud-gophercloud-0.12.0/openstack/clustering/v1/profiletypes/urls.go000066400000000000000000000012571367513235700317210ustar00rootroot00000000000000package 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.12.0/openstack/clustering/v1/receivers/000077500000000000000000000000001367513235700276425ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/clustering/v1/receivers/doc.go000066400000000000000000000032741367513235700307440ustar00rootroot00000000000000/* 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 }) Example to Notify a Receiver receiverID := "6dc6d336e3fc4c0a951b5698cd1236ee" requestID, err := receivers.Notify(serviceClient, receiverID).Extract() if err != nil { panic(err) } */ package receivers golang-github-gophercloud-gophercloud-0.12.0/openstack/clustering/v1/receivers/requests.go000066400000000000000000000111721367513235700320460ustar00rootroot00000000000000package receivers import ( "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 } resp, err := client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201, 202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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 } resp, err := client.Patch(updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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) { resp, err := client.Get(getURL(client, id), &r.Body, &gophercloud.RequestOpts{OkCodes: []int{200}}) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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) { resp, err := client.Delete(deleteURL(client, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Notify Notifies message type receiver func Notify(client *gophercloud.ServiceClient, id string) (r NotifyResult) { resp, err := client.Post(notifyURL(client, id), nil, nil, &gophercloud.RequestOpts{ OkCodes: []int{204}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } golang-github-gophercloud-gophercloud-0.12.0/openstack/clustering/v1/receivers/results.go000066400000000000000000000063231367513235700316760ustar00rootroot00000000000000package 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 } // NotifyResult is the result from a Notify operation. Call its Extract // method to determine if the call succeeded or failed. type NotifyResult struct { commonResult } // 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 } // Extract returns action for notify receivers func (r NotifyResult) Extract() (string, error) { requestID := r.Header.Get("X-Openstack-Request-Id") return requestID, r.Err } golang-github-gophercloud-gophercloud-0.12.0/openstack/clustering/v1/receivers/testing/000077500000000000000000000000001367513235700313175ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/clustering/v1/receivers/testing/doc.go000066400000000000000000000000531367513235700324110ustar00rootroot00000000000000// clustering_receivers_v1 package testing golang-github-gophercloud-gophercloud-0.12.0/openstack/clustering/v1/receivers/testing/fixtures.go000066400000000000000000000152351367513235700335250ustar00rootroot00000000000000package 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} var ExpectedNotifyRequestID = "66a81d68-bf48-4af5-897b-a3bfef7279a8" 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) }) } func HandleNotifySuccessfully(t *testing.T) { th.Mux.HandleFunc("/v1/receivers/6dc6d336e3fc4c0a951b5698cd1236ee/notify", 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", ExpectedNotifyRequestID) w.WriteHeader(http.StatusNoContent) }) } requests_test.go000066400000000000000000000050431367513235700345030ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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()) } func TestNotifyReceivers(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleNotifySuccessfully(t) requestID, err := receivers.Notify(fake.ServiceClient(), "6dc6d336e3fc4c0a951b5698cd1236ee").Extract() th.AssertNoErr(t, err) th.AssertEquals(t, ExpectedNotifyRequestID, requestID) } golang-github-gophercloud-gophercloud-0.12.0/openstack/clustering/v1/receivers/urls.go000066400000000000000000000016571367513235700311670ustar00rootroot00000000000000package 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) } func notifyURL(client *gophercloud.ServiceClient, id string) string { return client.ServiceURL(apiVersion, apiName, id, "notify") } golang-github-gophercloud-gophercloud-0.12.0/openstack/clustering/v1/webhooks/000077500000000000000000000000001367513235700274745ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/clustering/v1/webhooks/doc.go000066400000000000000000000005721367513235700305740ustar00rootroot00000000000000/* 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 golang-github-gophercloud-gophercloud-0.12.0/openstack/clustering/v1/webhooks/requests.go000066400000000000000000000023371367513235700317030ustar00rootroot00000000000000package 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 } resp, err := client.Post(url, nil, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201, 202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } golang-github-gophercloud-gophercloud-0.12.0/openstack/clustering/v1/webhooks/results.go000066400000000000000000000005431367513235700315260ustar00rootroot00000000000000package 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 } golang-github-gophercloud-gophercloud-0.12.0/openstack/clustering/v1/webhooks/testing/000077500000000000000000000000001367513235700311515ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/clustering/v1/webhooks/testing/doc.go000066400000000000000000000000521367513235700322420ustar00rootroot00000000000000// clustering_webhooks_v1 package testing requests_test.go000066400000000000000000000066121367513235700343400ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/clustering/v1/webhooks/testingpackage testing import ( "encoding/json" "fmt" "net/http" "testing" "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") } } golang-github-gophercloud-gophercloud-0.12.0/openstack/clustering/v1/webhooks/urls.go000066400000000000000000000003031367513235700310040ustar00rootroot00000000000000package 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.12.0/openstack/common/000077500000000000000000000000001367513235700244365ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/common/extensions/000077500000000000000000000000001367513235700266355ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/common/extensions/doc.go000066400000000000000000000034571367513235700277420ustar00rootroot00000000000000/* 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 golang-github-gophercloud-gophercloud-0.12.0/openstack/common/extensions/requests.go000077500000000000000000000013411367513235700310410ustar00rootroot00000000000000package 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) { resp, err := c.Get(ExtensionURL(c, alias), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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)} }) } golang-github-gophercloud-gophercloud-0.12.0/openstack/common/extensions/results.go000066400000000000000000000031041367513235700306630ustar00rootroot00000000000000package 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.12.0/openstack/common/extensions/testing/000077500000000000000000000000001367513235700303125ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/common/extensions/testing/doc.go000066400000000000000000000000601367513235700314020ustar00rootroot00000000000000// common extensions unit tests package testing golang-github-gophercloud-gophercloud-0.12.0/openstack/common/extensions/testing/fixtures.go000066400000000000000000000054611367513235700325200ustar00rootroot00000000000000package 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) }) } golang-github-gophercloud-gophercloud-0.12.0/openstack/common/extensions/testing/requests_test.go000066400000000000000000000016421367513235700335560ustar00rootroot00000000000000package 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.12.0/openstack/common/extensions/urls.go000066400000000000000000000006501367513235700301520ustar00rootroot00000000000000package 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.12.0/openstack/compute/000077500000000000000000000000001367513235700246225ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/000077500000000000000000000000001367513235700251515ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/000077500000000000000000000000001367513235700273505ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/aggregates/000077500000000000000000000000001367513235700314615ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/aggregates/doc.go000066400000000000000000000041251367513235700325570ustar00rootroot00000000000000/* Package aggregates manages information about the host aggregates in the OpenStack cloud. Example of Create Aggregate createOpts := aggregates.CreateOpts{ Name: "name", AvailabilityZone: "london", } aggregate, err := aggregates.Create(computeClient, createOpts).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 golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/aggregates/requests.go000066400000000000000000000124131367513235700336640ustar00rootroot00000000000000package 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 } resp, err := client.Post(aggregatesCreateURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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) resp, err := client.Delete(aggregatesDeleteURL(client, v), &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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) resp, err := client.Get(aggregatesGetURL(client, v), &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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 } resp, err := client.Put(aggregatesUpdateURL(client, v), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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 } resp, err := client.Post(aggregatesAddHostURL(client, v), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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 } resp, err := client.Post(aggregatesRemoveHostURL(client, v), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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 } resp, err := client.Post(aggregatesSetMetadataURL(client, v), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/aggregates/results.go000066400000000000000000000054161367513235700335170ustar00rootroot00000000000000package 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 } golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/aggregates/testing/000077500000000000000000000000001367513235700331365ustar00rootroot00000000000000fixtures.go000066400000000000000000000232711367513235700352640ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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.go000066400000000000000000000064761367513235700363350ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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) } golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/aggregates/urls.go000066400000000000000000000021611367513235700327750ustar00rootroot00000000000000package 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") } golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/attachinterfaces/000077500000000000000000000000001367513235700326605ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/attachinterfaces/doc.go000066400000000000000000000025761367513235700337660ustar00rootroot00000000000000/* 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.go000066400000000000000000000061501367513235700350050ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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) { resp, err := client.Get(getInterfaceURL(client, serverID, portID), &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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 } resp, err := client.Post(createInterfaceURL(client, serverID), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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) { resp, err := client.Delete(deleteInterfaceURL(client, serverID, portID), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } results.go000066400000000000000000000045061367513235700346360ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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/000077500000000000000000000000001367513235700342565ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/attachinterfacesdoc.go000066400000000000000000000000571367513235700353540ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/attachinterfaces/testing// attachinterfaces unit tests package testing fixtures.go000066400000000000000000000116101367513235700364550ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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.go000066400000000000000000000046571367513235700375330ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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.go000066400000000000000000000012741367513235700341210ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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) } golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/availabilityzones/000077500000000000000000000000001367513235700331015ustar00rootroot00000000000000doc.go000066400000000000000000000026411367513235700341210ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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.go000066400000000000000000000013431367513235700352250ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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.go000066400000000000000000000040441367513235700350540ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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/000077500000000000000000000000001367513235700344775ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/availabilityzonesdoc.go000066400000000000000000000000571367513235700355750ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/availabilityzones/testing// availabilityzones unittests package testing fixtures.go000066400000000000000000000132051367513235700367000ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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.go000066400000000000000000000017601367513235700377440ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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.go000066400000000000000000000004371367513235700343420ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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") } golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/bootfromvolume/000077500000000000000000000000001367513235700324275ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/bootfromvolume/doc.go000066400000000000000000000104311367513235700335220ustar00rootroot00000000000000/* 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.go000066400000000000000000000105151367513235700345540ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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"` // VolumeType is the volume type of the block device. // This requires Compute API microversion 2.67 or later. VolumeType string `json:"volume_type,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 } resp, err := client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } results.go000066400000000000000000000005331367513235700344010ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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 } golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/bootfromvolume/testing/000077500000000000000000000000001367513235700341045ustar00rootroot00000000000000doc.go000066400000000000000000000000551367513235700351210ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/bootfromvolume/testing// bootfromvolume unit tests package testing fixtures.go000066400000000000000000000156131367513235700362330ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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, }, }, } const ExpectedNewVolumeTypeRequest = ` { "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, "volume_type": "ssd" } ] } } ` var NewVolumeTypeRequest = bootfromvolume.CreateOptsExt{ CreateOptsBuilder: BaseCreateOpts, BlockDevice: []bootfromvolume.BlockDevice{ { UUID: "123456", SourceType: bootfromvolume.SourceImage, DestinationType: bootfromvolume.DestinationVolume, VolumeSize: 10, DeleteOnTermination: true, VolumeType: "ssd", }, }, } requests_test.go000066400000000000000000000026231367513235700372710ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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) } func TestBootFromNewVolumeType(t *testing.T) { actual, err := NewVolumeTypeRequest.ToServerCreateMap() th.AssertNoErr(t, err) th.CheckJSONEquals(t, ExpectedNewVolumeTypeRequest, actual) } golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/bootfromvolume/urls.go000066400000000000000000000002351367513235700337430ustar00rootroot00000000000000package bootfromvolume import "github.com/gophercloud/gophercloud" func createURL(c *gophercloud.ServiceClient) string { return c.ServiceURL("servers") } golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/defsecrules/000077500000000000000000000000001367513235700316545ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/defsecrules/doc.go000066400000000000000000000023611367513235700327520ustar00rootroot00000000000000/* 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 golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/defsecrules/requests.go000066400000000000000000000052301367513235700340560ustar00rootroot00000000000000package 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 } resp, err := client.Post(rootURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get will return details for a particular default rule. func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { resp, err := client.Get(resourceURL(client, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete will permanently delete a rule the project's default security group. func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { resp, err := client.Delete(resourceURL(client, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/defsecrules/results.go000066400000000000000000000036351367513235700337130ustar00rootroot00000000000000package 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 } golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/defsecrules/testing/000077500000000000000000000000001367513235700333315ustar00rootroot00000000000000doc.go000066400000000000000000000000521367513235700343430ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/defsecrules/testing// defsecrules unit tests package testing fixtures.go000066400000000000000000000056651367513235700354660ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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.go000066400000000000000000000053051367513235700365160ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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) } golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/defsecrules/urls.go000066400000000000000000000004641367513235700331740ustar00rootroot00000000000000package 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) } golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/delegate.go000066400000000000000000000013751367513235700314570ustar00rootroot00000000000000package 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) } golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/diagnostics/000077500000000000000000000000001367513235700316575ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/diagnostics/doc.go000066400000000000000000000003771367513235700327620ustar00rootroot00000000000000/* Package diagnostics returns details about a nova instance diagnostics Example of Show Diagnostics diags, err := diagnostics.Get(computeClient, serverId).Extract() if err != nil { panic(err) } fmt.Printf("%+v\n", diags) */ package diagnostics golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/diagnostics/requests.go000066400000000000000000000005051367513235700340610ustar00rootroot00000000000000package diagnostics import ( "github.com/gophercloud/gophercloud" ) // Diagnostics func Get(client *gophercloud.ServiceClient, serverId string) (r serverDiagnosticsResult) { resp, err := client.Get(serverDiagnosticsURL(client, serverId), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/diagnostics/results.go000066400000000000000000000005211367513235700337050ustar00rootroot00000000000000package diagnostics import ( "github.com/gophercloud/gophercloud" ) type serverDiagnosticsResult struct { gophercloud.Result } // Extract interprets any diagnostic response as a map func (r serverDiagnosticsResult) Extract() (map[string]interface{}, error) { var s map[string]interface{} err := r.ExtractInto(&s) return s, err } golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/diagnostics/testing/000077500000000000000000000000001367513235700333345ustar00rootroot00000000000000fixtures.go000066400000000000000000000012001367513235700354460ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/diagnostics/testingpackage testing import ( "net/http" "testing" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) // HandleDiagnosticGetSuccessfully sets up the test server to respond to a diagnostic Get request. func HandleDiagnosticGetSuccessfully(t *testing.T) { th.Mux.HandleFunc("/servers/1234asdf/diagnostics", 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(`{"cpu0_time":173,"memory":524288}`)) }) } requests_test.go000066400000000000000000000011061367513235700365140ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/diagnostics/testingpackage testing import ( "testing" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/diagnostics" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) func TestGetDiagnostics(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleDiagnosticGetSuccessfully(t) expected := map[string]interface{}{"cpu0_time": float64(173), "memory": float64(524288)} res, err := diagnostics.Get(client.ServiceClient(), "1234asdf").Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, expected, res) } golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/diagnostics/urls.go000066400000000000000000000004341367513235700331740ustar00rootroot00000000000000package diagnostics import "github.com/gophercloud/gophercloud" // serverDiagnosticsURL returns the diagnostics url for a nova instance/server func serverDiagnosticsURL(client *gophercloud.ServiceClient, id string) string { return client.ServiceURL("servers", id, "diagnostics") } golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/diskconfig/000077500000000000000000000000001367513235700314705ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/diskconfig/doc.go000066400000000000000000000020751367513235700325700ustar00rootroot00000000000000/* 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 golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/diskconfig/requests.go000066400000000000000000000066631367513235700337050ustar00rootroot00000000000000package 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 } golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/diskconfig/results.go000066400000000000000000000002421367513235700335160ustar00rootroot00000000000000package diskconfig type ServerDiskConfigExt struct { // DiskConfig is the disk configuration of the server. DiskConfig DiskConfig `json:"OS-DCF:diskConfig"` } golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/diskconfig/testing/000077500000000000000000000000001367513235700331455ustar00rootroot00000000000000doc.go000066400000000000000000000000511367513235700341560ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/diskconfig/testing// diskconfig unit tests package testing requests_test.go000066400000000000000000000033331367513235700363310ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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", ImageRef: "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) } golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/doc.go000066400000000000000000000002341367513235700304430ustar00rootroot00000000000000// Package extensions provides information and interaction with the // different extensions available for the OpenStack Compute service. package extensions golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/evacuate/000077500000000000000000000000001367513235700311455ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/evacuate/doc.go000066400000000000000000000006131367513235700322410ustar00rootroot00000000000000/* 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 golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/evacuate/requests.go000066400000000000000000000025131367513235700333500ustar00rootroot00000000000000package evacuate import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions" ) // 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 } resp, err := client.Post(extensions.ActionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/evacuate/results.go000066400000000000000000000011311367513235700331710ustar00rootroot00000000000000package 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 } golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/evacuate/testing/000077500000000000000000000000001367513235700326225ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/evacuate/testing/doc.go000066400000000000000000000000621367513235700337140ustar00rootroot00000000000000// compute_extensions_evacuate_v2 package testing fixtures.go000066400000000000000000000035551367513235700347530ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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.go000066400000000000000000000031711367513235700360060ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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) } extendedserverattributes/000077500000000000000000000000001367513235700344275ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensionsdoc.go000066400000000000000000000034021367513235700355220ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/extendedserverattributes/* Package extendedserverattributes provides the ability to extend a server result with the extended usage information. Example to Get basic 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) Example to get additional fields with microversion 2.3 or later computeClient.Microversion = "2.3" result := servers.Get(computeClient, "d650a0ce-17c3-497d-961a-43c4af80998a") reservationID, err := extendedserverattributes.ExtractReservationID(result.Result) if err != nil { panic(err) } fmt.Printf("%s\n", reservationID) launchIndex, err := extendedserverattributes.ExtractLaunchIndex(result.Result) if err != nil { panic(err) } fmt.Printf("%d\n", launchIndex) ramdiskID, err := extendedserverattributes.ExtractRamdiskID(result.Result) if err != nil { panic(err) } fmt.Printf("%s\n", ramdiskID) kernelID, err := extendedserverattributes.ExtractKernelID(result.Result) if err != nil { panic(err) } fmt.Printf("%s\n", kernelID) hostname, err := extendedserverattributes.ExtractHostname(result.Result) if err != nil { panic(err) } fmt.Printf("%s\n", hostname) rootDeviceName, err := extendedserverattributes.ExtractRootDeviceName(result.Result) if err != nil { panic(err) } fmt.Printf("%s\n", rootDeviceName) userData, err := extendedserverattributes.ExtractUserData(result.Result) if err != nil { panic(err) } fmt.Printf("%s\n", userData) */ package extendedserverattributes results.go000066400000000000000000000033111367513235700364550ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/extendedserverattributespackage extendedserverattributes // ServerAttributesExt represents basic OS-EXT-SRV-ATTR server response fields. // You should use extract methods from microversions.go to retrieve additional // fields. type ServerAttributesExt struct { // Host is the host/hypervisor that the instance is hosted on. Host string `json:"OS-EXT-SRV-ATTR:host"` // InstanceName is the name of the instance. InstanceName string `json:"OS-EXT-SRV-ATTR:instance_name"` // HypervisorHostname is the hostname of the host/hypervisor that the // instance is hosted on. HypervisorHostname string `json:"OS-EXT-SRV-ATTR:hypervisor_hostname"` // ReservationID is the reservation ID of the instance. // This requires microversion 2.3 or later. ReservationID *string `json:"OS-EXT-SRV-ATTR:reservation_id"` // LaunchIndex is the launch index of the instance. // This requires microversion 2.3 or later. LaunchIndex *int `json:"OS-EXT-SRV-ATTR:launch_index"` // RAMDiskID is the ID of the RAM disk image of the instance. // This requires microversion 2.3 or later. RAMDiskID *string `json:"OS-EXT-SRV-ATTR:ramdisk_id"` // KernelID is the ID of the kernel image of the instance. // This requires microversion 2.3 or later. KernelID *string `json:"OS-EXT-SRV-ATTR:kernel_id"` // Hostname is the hostname of the instance. // This requires microversion 2.3 or later. Hostname *string `json:"OS-EXT-SRV-ATTR:hostname"` // RootDeviceName is the name of the root device of the instance. // This requires microversion 2.3 or later. RootDeviceName *string `json:"OS-EXT-SRV-ATTR:root_device_name"` // Userdata is the userdata of the instance. // This requires microversion 2.3 or later. Userdata *string `json:"OS-EXT-SRV-ATTR:user_data"` } testing/000077500000000000000000000000001367513235700361045ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/extendedserverattributesdoc.go000066400000000000000000000000201367513235700371700ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/extendedserverattributes/testingpackage testing fixtures.go000066400000000000000000000020571367513235700403100ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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": "Zm9v", "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.go000066400000000000000000000031031367513235700413420ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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 // Extract basic fields. 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") th.AssertEquals(t, *serverWithAttributesExt.Userdata, "Zm9v") th.AssertEquals(t, *serverWithAttributesExt.ReservationID, "r-ky9gim1l") th.AssertEquals(t, *serverWithAttributesExt.LaunchIndex, 0) th.AssertEquals(t, *serverWithAttributesExt.Hostname, "test00") th.AssertEquals(t, *serverWithAttributesExt.RootDeviceName, "/dev/sda") } golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/extendedstatus/000077500000000000000000000000001367513235700324145ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/extendedstatus/doc.go000066400000000000000000000012151367513235700335070ustar00rootroot00000000000000/* 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.go000066400000000000000000000012421367513235700343640ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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" } } golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/floatingips/000077500000000000000000000000001367513235700316675ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/floatingips/doc.go000066400000000000000000000027621367513235700327720ustar00rootroot00000000000000/* 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 golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/floatingips/requests.go000066400000000000000000000102001367513235700340620ustar00rootroot00000000000000package 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 } resp, err := client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get returns data about a previously created Floating IP. func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { resp, err := client.Get(getURL(client, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete requests the deletion of a previous allocated Floating IP. func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { resp, err := client.Delete(deleteURL(client, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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 } resp, err := client.Post(associateURL(client, serverID), b, nil, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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 } resp, err := client.Post(disassociateURL(client, serverID), b, nil, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/floatingips/results.go000066400000000000000000000056001367513235700337200ustar00rootroot00000000000000package 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 } golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/floatingips/testing/000077500000000000000000000000001367513235700333445ustar00rootroot00000000000000doc.go000066400000000000000000000000521367513235700343560ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/floatingips/testing// floatingips unit tests package testing fixtures.go000066400000000000000000000137451367513235700354770ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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.go000066400000000000000000000054711367513235700365350ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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) } golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/floatingips/urls.go000066400000000000000000000016041367513235700332040ustar00rootroot00000000000000package 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) } golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/hypervisors/000077500000000000000000000000001367513235700317455ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/hypervisors/doc.go000066400000000000000000000033661367513235700330510ustar00rootroot00000000000000/* 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, hypervisorID).Extract() if err != nil { panic(err) } fmt.Printf("%+v\n", hypervisor) Example of Show Hypervisor Details with Compute API microversion greater than 2.53 hypervisorID := "c48f6247-abe4-4a24-824e-ea39e108874f" hypervisor, err := hypervisors.Get(computeClient, hypervisorID).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 Hypervisors 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) Example of Show Hypervisor Uptime with Compute API microversion greater than 2.53 hypervisorID := "c48f6247-abe4-4a24-824e-ea39e108874f" hypervisorUptime, err := hypervisors.GetUptime(computeClient, hypervisorID).Extract() if err != nil { panic(err) } fmt.Printf("%+v\n", hypervisorUptime) */ package hypervisors golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/hypervisors/requests.go000066400000000000000000000027441367513235700341560ustar00rootroot00000000000000package hypervisors import ( "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) { resp, err := client.Get(hypervisorsStatisticsURL(client), &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get makes a request against the API to get details for specific hypervisor. func Get(client *gophercloud.ServiceClient, hypervisorID string) (r HypervisorResult) { resp, err := client.Get(hypervisorsGetURL(client, hypervisorID), &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // GetUptime makes a request against the API to get uptime for specific hypervisor. func GetUptime(client *gophercloud.ServiceClient, hypervisorID string) (r UptimeResult) { resp, err := client.Get(hypervisorsUptimeURL(client, hypervisorID), &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/hypervisors/results.go000066400000000000000000000222471367513235700340040ustar00rootroot00000000000000package hypervisors import ( "encoding/json" "fmt" "strconv" "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 string `json:"-"` DisabledReason string `json:"disabled_reason"` } func (r *Service) UnmarshalJSON(b []byte) error { type tmp Service var s struct { tmp ID interface{} `json:"id"` } err := json.Unmarshal(b, &s) if err != nil { return err } *r = Service(s.tmp) // OpenStack Compute service returns ID in string representation since // 2.53 microversion API (Pike release). switch t := s.ID.(type) { case int: r.ID = strconv.Itoa(t) case float64: r.ID = strconv.Itoa(int(t)) case string: r.ID = t default: return fmt.Errorf("ID has unexpected type: %T", t) } return nil } // 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 string `json:"-"` // 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 ID interface{} `json:"id"` 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) } if len(tmpb) != 0 { 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 has unexpected type: %T", t) } switch t := s.FreeDiskGB.(type) { case int: r.FreeDiskGB = t case float64: r.FreeDiskGB = int(t) default: return fmt.Errorf("Free disk GB has unexpected type: %T", t) } switch t := s.LocalGB.(type) { case int: r.LocalGB = t case float64: r.LocalGB = int(t) default: return fmt.Errorf("Local GB has unexpected type: %T", t) } // OpenStack Compute service returns ID in string representation since // 2.53 microversion API (Pike release). switch t := s.ID.(type) { case int: r.ID = strconv.Itoa(t) case float64: r.ID = strconv.Itoa(int(t)) case string: r.ID = t default: return fmt.Errorf("ID has unexpected type: %T", t) } 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 string `json:"-"` // 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"` } func (r *Uptime) UnmarshalJSON(b []byte) error { type tmp Uptime var s struct { tmp ID interface{} `json:"id"` } err := json.Unmarshal(b, &s) if err != nil { return err } *r = Uptime(s.tmp) // OpenStack Compute service returns ID in string representation since // 2.53 microversion API (Pike release). switch t := s.ID.(type) { case int: r.ID = strconv.Itoa(t) case float64: r.ID = strconv.Itoa(int(t)) case string: r.ID = t default: return fmt.Errorf("ID has unexpected type: %T", t) } return nil } 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 } golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/hypervisors/testing/000077500000000000000000000000001367513235700334225ustar00rootroot00000000000000fixtures.go000066400000000000000000000325501367513235700355500ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/hypervisors/testingpackage testing import ( "fmt" "net/http" "testing" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/hypervisors" "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) // HypervisorListBodyPre253 represents a raw hypervisor list from the Compute // API with microversion older than 2.53. // 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 HypervisorListBodyPre253 = ` { "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 } ] }` // HypervisorListBody represents a raw hypervisor list result with Pike+ release. 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": "c48f6247-abe4-4a24-824e-ea39e108874f", "local_gb": 1028, "local_gb_used": 0, "memory_mb": 8192, "memory_mb_used": 512, "running_vms": 0, "service": { "host": "e6a37ee802d74863ab8b91ade8f12a67", "id": "9c2566e7-7a54-4777-a1ae-c2662f0c407c", "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": "c48f6247-abe4-4a24-824e-ea39e108874f", "local_gb": 1028, "local_gb_used": 0, "memory_mb": 8192, "memory_mb_used": 512, "running_vms": 0, "service": { "host": "e6a37ee802d74863ab8b91ade8f12a67", "id": "9c2566e7-7a54-4777-a1ae-c2662f0c407c", "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 } } ` // HypervisorGetBody represents a raw hypervisor GET result with Pike+ release. 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":"c48f6247-abe4-4a24-824e-ea39e108874f", "local_gb":1028, "local_gb_used":0, "memory_mb":8192, "memory_mb_used":512, "running_vms":0, "service":{ "host":"e6a37ee802d74863ab8b91ade8f12a67", "id":"9c2566e7-7a54-4777-a1ae-c2662f0c407c", "disabled_reason":null }, "vcpus":1, "vcpus_used":0 } } ` // HypervisorGetEmptyCPUInfoBody represents a raw hypervisor GET result with // no cpu_info const HypervisorGetEmptyCPUInfoBody = ` { "hypervisor":{ "cpu_info": "", "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":"c48f6247-abe4-4a24-824e-ea39e108874f", "local_gb":1028, "local_gb_used":0, "memory_mb":8192, "memory_mb_used":512, "running_vms":0, "service":{ "host":"e6a37ee802d74863ab8b91ade8f12a67", "id":"9c2566e7-7a54-4777-a1ae-c2662f0c407c", "disabled_reason":null }, "vcpus":1, "vcpus_used":0 } } ` // HypervisorUptimeBody represents a raw hypervisor uptime request result with // Pike+ release. const HypervisorUptimeBody = ` { "hypervisor": { "hypervisor_hostname": "fake-mini", "id": "c48f6247-abe4-4a24-824e-ea39e108874f", "state": "up", "status": "enabled", "uptime": " 08:32:11 up 93 days, 18:25, 12 users, load average: 0.20, 0.12, 0.14" } } ` var ( HypervisorFakePre253 = 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, } 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: "c48f6247-abe4-4a24-824e-ea39e108874f", LocalGB: 1028, LocalGBUsed: 0, MemoryMB: 8192, MemoryMBUsed: 512, RunningVMs: 0, Service: hypervisors.Service{ Host: "e6a37ee802d74863ab8b91ade8f12a67", ID: "9c2566e7-7a54-4777-a1ae-c2662f0c407c", DisabledReason: "", }, VCPUs: 1, VCPUsUsed: 0, } HypervisorEmptyCPUInfo = hypervisors.Hypervisor{ 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: "c48f6247-abe4-4a24-824e-ea39e108874f", LocalGB: 1028, LocalGBUsed: 0, MemoryMB: 8192, MemoryMBUsed: 512, RunningVMs: 0, Service: hypervisors.Service{ Host: "e6a37ee802d74863ab8b91ade8f12a67", ID: "9c2566e7-7a54-4777-a1ae-c2662f0c407c", 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: "c48f6247-abe4-4a24-824e-ea39e108874f", 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 HandleHypervisorListPre253Successfully(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, HypervisorListBodyPre253) }) } 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) { testhelper.Mux.HandleFunc("/os-hypervisors/"+HypervisorFake.ID, 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 HandleHypervisorGetEmptyCPUInfoSuccessfully(t *testing.T) { testhelper.Mux.HandleFunc("/os-hypervisors/"+HypervisorFake.ID, 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, HypervisorGetEmptyCPUInfoBody) }) } func HandleHypervisorUptimeSuccessfully(t *testing.T) { testhelper.Mux.HandleFunc("/os-hypervisors/"+HypervisorFake.ID+"/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.go000066400000000000000000000076311367513235700366130ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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 TestListHypervisorsPre253(t *testing.T) { testhelper.SetupHTTP() defer testhelper.TeardownHTTP() HandleHypervisorListPre253Successfully(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, HypervisorFakePre253, actual[0]) testhelper.CheckDeepEquals(t, HypervisorFakePre253, actual[1]) return true, nil }) testhelper.AssertNoErr(t, err) if pages != 1 { t.Errorf("Expected 1 page, saw %d", pages) } } func TestListAllHypervisorsPre253(t *testing.T) { testhelper.SetupHTTP() defer testhelper.TeardownHTTP() HandleHypervisorListPre253Successfully(t) allPages, err := hypervisors.List(client.ServiceClient()).AllPages() testhelper.AssertNoErr(t, err) actual, err := hypervisors.ExtractHypervisors(allPages) testhelper.AssertNoErr(t, err) testhelper.CheckDeepEquals(t, HypervisorFakePre253, actual[0]) testhelper.CheckDeepEquals(t, HypervisorFakePre253, actual[1]) } 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 TestGetHypervisorEmptyCPUInfo(t *testing.T) { testhelper.SetupHTTP() defer testhelper.TeardownHTTP() HandleHypervisorGetEmptyCPUInfoSuccessfully(t) expected := HypervisorEmptyCPUInfo 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) } golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/hypervisors/urls.go000066400000000000000000000011321367513235700332560ustar00rootroot00000000000000package 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") } golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/injectnetworkinfo/000077500000000000000000000000001367513235700331125ustar00rootroot00000000000000doc.go000066400000000000000000000007311367513235700341300ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/injectnetworkinfo/* Package injectnetworkinfo provides functionality to inject the network info into a server that has been provisioned by the OpenStack Compute service. This action requires admin privileges and Nova configured with a Xen hypervisor driver. Example to Inject a Network Info into a Server serverID := "47b6b7b7-568d-40e4-868c-d5c41735532e" err := injectnetworkinfo.InjectNetworkInfo(client, id).ExtractErr() if err != nil { panic(err) } */ package injectnetworkinfo requests.go000066400000000000000000000007751367513235700352460ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/injectnetworkinfopackage injectnetworkinfo import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions" ) // InjectNetworkInfo will inject the network info into a server func InjectNetworkInfo(client *gophercloud.ServiceClient, id string) (r InjectNetworkResult) { b := map[string]interface{}{ "injectNetworkInfo": nil, } resp, err := client.Post(extensions.ActionURL(client, id), b, nil, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } results.go000066400000000000000000000004371367513235700350670ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/injectnetworkinfopackage injectnetworkinfo import ( "github.com/gophercloud/gophercloud" ) // InjectNetworkResult is the response of a InjectNetworkInfo operation. Call // its ExtractErr method to determine if the request suceeded or failed. type InjectNetworkResult struct { gophercloud.ErrResult } testing/000077500000000000000000000000001367513235700345105ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/injectnetworkinfofixtures.go000066400000000000000000000007641367513235700367170ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/injectnetworkinfo/testingpackage testing import ( "net/http" "testing" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) func mockInjectNetworkInfoResponse(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, `{"injectNetworkInfo": null}`) w.WriteHeader(http.StatusAccepted) }) } requests_test.go000066400000000000000000000010351367513235700377500ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/injectnetworkinfo/testingpackage testing import ( "testing" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/injectnetworkinfo" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) const serverID = "b16ba811-199d-4ffd-8839-ba96c1185a67" func TestInjectNetworkInfo(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() mockInjectNetworkInfoResponse(t, serverID) err := injectnetworkinfo.InjectNetworkInfo(client.ServiceClient(), serverID).ExtractErr() th.AssertNoErr(t, err) } golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/instanceactions/000077500000000000000000000000001367513235700325355ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/instanceactions/doc.go000066400000000000000000000011471367513235700336340ustar00rootroot00000000000000package instanceactions /* Package instanceactions provides the ability to list or get a server instance-action. Example to List and Get actions: pages, err := instanceactions.List(client, "server-id", nil).AllPages() if err != nil { panic("fail to get actions pages") } actions, err := instanceactions.ExtractInstanceActions(pages) if err != nil { panic("fail to list instance actions") } for _, action := range actions { action, err = instanceactions.Get(client, "server-id", action.RequestID).Extract() if err != nil { panic("fail to get instance action") } fmt.Println(action) } */ request.go000066400000000000000000000045621367513235700345040ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/instanceactionspackage instanceactions import ( "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 { ToInstanceActionsListQuery() (string, error) } // ListOpts represents options used to filter instance action results // in a List request. type ListOpts struct { // Limit is an integer value to limit the results to return. // This requires microversion 2.58 or later. Limit int `q:"limit"` // Marker is the request ID of the last-seen instance action. // This requires microversion 2.58 or later. Marker string `q:"marker"` // ChangesSince filters the response by actions after the given time. // This requires microversion 2.58 or later. ChangesSince *time.Time `q:"changes-since"` // ChangesBefore filters the response by actions before the given time. // This requires microversion 2.66 or later. ChangesBefore *time.Time `q:"changes-before"` } // ToInstanceActionsListQuery formats a ListOpts into a query string. func (opts ListOpts) ToInstanceActionsListQuery() (string, error) { q, err := gophercloud.BuildQueryString(opts) if err != nil { return "", err } params := q.Query() if opts.ChangesSince != nil { params.Add("changes-since", opts.ChangesSince.Format(time.RFC3339)) } if opts.ChangesBefore != nil { params.Add("changes-before", opts.ChangesBefore.Format(time.RFC3339)) } q = &url.URL{RawQuery: params.Encode()} return q.String(), nil } // List makes a request against the API to list the servers actions. func List(client *gophercloud.ServiceClient, id string, opts ListOptsBuilder) pagination.Pager { url := listURL(client, id) if opts != nil { query, err := opts.ToInstanceActionsListQuery() if err != nil { return pagination.Pager{Err: err} } url += query } return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { return InstanceActionPage{pagination.SinglePageBase(r)} }) } // Get makes a request against the API to get a server action. func Get(client *gophercloud.ServiceClient, serverID, requestID string) (r InstanceActionResult) { resp, err := client.Get(instanceActionsURL(client, serverID, requestID), &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } results.go000066400000000000000000000124131367513235700345070ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/instanceactionspackage instanceactions import ( "encoding/json" "time" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // InstanceAction represents an instance action. type InstanceAction struct { // Action is the name of the action. Action string `json:"action"` // InstanceUUID is the UUID of the instance. InstanceUUID string `json:"instance_uuid"` // Message is the related error message for when an action fails. Message string `json:"message"` // Project ID is the ID of the project which initiated the action. ProjectID string `json:"project_id"` // RequestID is the ID generated when performing the action. RequestID string `json:"request_id"` // StartTime is the time the action started. StartTime time.Time `json:"-"` // UserID is the ID of the user which initiated the action. UserID string `json:"user_id"` } // UnmarshalJSON converts our JSON API response into our instance action struct func (i *InstanceAction) UnmarshalJSON(b []byte) error { type tmp InstanceAction var s struct { tmp StartTime gophercloud.JSONRFC3339MilliNoZ `json:"start_time"` } err := json.Unmarshal(b, &s) if err != nil { return err } *i = InstanceAction(s.tmp) i.StartTime = time.Time(s.StartTime) return err } // InstanceActionPage 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 ExtractInstanceActions call. type InstanceActionPage struct { pagination.SinglePageBase } // IsEmpty returns true if an InstanceActionPage contains no instance actions. func (r InstanceActionPage) IsEmpty() (bool, error) { instanceactions, err := ExtractInstanceActions(r) return len(instanceactions) == 0, err } // ExtractInstanceActions interprets a page of results as a slice // of InstanceAction. func ExtractInstanceActions(r pagination.Page) ([]InstanceAction, error) { var resp []InstanceAction err := ExtractInstanceActionsInto(r, &resp) return resp, err } // Event represents an event of instance action. type Event struct { // Event is the name of the event. Event string `json:"event"` // Host is the host of the event. // This requires microversion 2.62 or later. Host *string `json:"host"` // HostID is the host id of the event. // This requires microversion 2.62 or later. HostID *string `json:"hostId"` // Result is the result of the event. Result string `json:"result"` // Traceback is the traceback stack if an error occurred. Traceback string `json:"traceback"` // StartTime is the time the action started. StartTime time.Time `json:"-"` // FinishTime is the time the event finished. FinishTime time.Time `json:"-"` } // UnmarshalJSON converts our JSON API response into our instance action struct. func (e *Event) UnmarshalJSON(b []byte) error { type tmp Event var s struct { tmp StartTime gophercloud.JSONRFC3339MilliNoZ `json:"start_time"` FinishTime gophercloud.JSONRFC3339MilliNoZ `json:"finish_time"` } err := json.Unmarshal(b, &s) if err != nil { return err } *e = Event(s.tmp) e.StartTime = time.Time(s.StartTime) e.FinishTime = time.Time(s.FinishTime) return err } // InstanceActionDetail represents the details of an Action. type InstanceActionDetail struct { // Action is the name of the Action. Action string `json:"action"` // InstanceUUID is the UUID of the instance. InstanceUUID string `json:"instance_uuid"` // Message is the related error message for when an action fails. Message string `json:"message"` // Project ID is the ID of the project which initiated the action. ProjectID string `json:"project_id"` // RequestID is the ID generated when performing the action. RequestID string `json:"request_id"` // UserID is the ID of the user which initiated the action. UserID string `json:"user_id"` // Events is the list of events of the action. // This requires microversion 2.50 or later. Events *[]Event `json:"events"` // UpdatedAt last update date of the action. // This requires microversion 2.58 or later. UpdatedAt *time.Time `json:"-"` // StartTime is the time the action started. StartTime time.Time `json:"-"` } // UnmarshalJSON converts our JSON API response into our instance action struct func (i *InstanceActionDetail) UnmarshalJSON(b []byte) error { type tmp InstanceActionDetail var s struct { tmp UpdatedAt *gophercloud.JSONRFC3339MilliNoZ `json:"updated_at"` StartTime gophercloud.JSONRFC3339MilliNoZ `json:"start_time"` } err := json.Unmarshal(b, &s) if err != nil { return err } *i = InstanceActionDetail(s.tmp) i.UpdatedAt = (*time.Time)(s.UpdatedAt) i.StartTime = time.Time(s.StartTime) return err } // InstanceActionResult is the result handler of Get. type InstanceActionResult struct { gophercloud.Result } // Extract interprets a result as an InstanceActionDetail. func (r InstanceActionResult) Extract() (InstanceActionDetail, error) { var s InstanceActionDetail err := r.ExtractInto(&s) return s, err } func (r InstanceActionResult) ExtractInto(v interface{}) error { return r.Result.ExtractIntoStructPtr(v, "instanceAction") } func ExtractInstanceActionsInto(r pagination.Page, v interface{}) error { return r.(InstanceActionPage).Result.ExtractIntoSlicePtr(v, "instanceActions") } testing/000077500000000000000000000000001367513235700341335ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/instanceactionsdoc.go000066400000000000000000000000561367513235700352300ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/instanceactions/testing// instanceactions unit tests package testing fixtures.go000066400000000000000000000102541367513235700363350ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/instanceactions/testingpackage testing import ( "fmt" "net/http" "testing" "time" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/instanceactions" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) // ListExpected represents an expected repsonse from a List request. var ListExpected = []instanceactions.InstanceAction{ { Action: "stop", InstanceUUID: "fcd19ef2-b593-40b1-90a5-fc31063fa95c", Message: "", ProjectID: "6f70656e737461636b20342065766572", RequestID: "req-f8a59f03-76dc-412f-92c2-21f8612be728", StartTime: time.Date(2018, 04, 25, 1, 26, 29, 000000, time.UTC), UserID: "admin", }, { Action: "create", InstanceUUID: "fcd19ef2-b593-40b1-90a5-fc31063fa95c", Message: "test", ProjectID: "6f70656e737461636b20342065766572", RequestID: "req-50189019-626d-47fb-b944-b8342af09679", StartTime: time.Date(2018, 04, 25, 1, 26, 25, 000000, time.UTC), UserID: "admin", }, } // HandleInstanceActionListSuccessfully sets up the test server to respond to a ListAddresses request. func HandleInstanceActionListSuccessfully(t *testing.T) { th.Mux.HandleFunc("/servers/asdfasdfasdf/os-instance-actions", 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, `{ "instanceActions": [ { "action": "stop", "instance_uuid": "fcd19ef2-b593-40b1-90a5-fc31063fa95c", "message": null, "project_id": "6f70656e737461636b20342065766572", "request_id": "req-f8a59f03-76dc-412f-92c2-21f8612be728", "start_time": "2018-04-25T01:26:29.000000", "user_id": "admin" }, { "action": "create", "instance_uuid": "fcd19ef2-b593-40b1-90a5-fc31063fa95c", "message": "test", "project_id": "6f70656e737461636b20342065766572", "request_id": "req-50189019-626d-47fb-b944-b8342af09679", "start_time": "2018-04-25T01:26:25.000000", "user_id": "admin" } ] }`) }) } var ( expectedUpdateAt = time.Date(2018, 04, 25, 1, 26, 36, 0, time.UTC) expectedEventHost = "compute" expectedEventHostID = "2091634baaccdc4c5a1d57069c833e402921df696b7f970791b12ec6" expectedEvents = []instanceactions.Event{{ Event: "compute_stop_instance", Host: &expectedEventHost, HostID: &expectedEventHostID, Result: "Success", StartTime: time.Date(2018, 04, 25, 1, 26, 36, 0, time.UTC), FinishTime: time.Date(2018, 04, 25, 1, 26, 36, 0, time.UTC), Traceback: "", }} ) // GetExpected represents an expected repsonse from a Get request. var GetExpected = instanceactions.InstanceActionDetail{ Action: "stop", InstanceUUID: "4bf3473b-d550-4b65-9409-292d44ab14a2", Message: "", ProjectID: "6f70656e737461636b20342065766572", RequestID: "req-0d819d5c-1527-4669-bdf0-ffad31b5105b", StartTime: time.Date(2018, 04, 25, 1, 26, 36, 0, time.UTC), UpdatedAt: &expectedUpdateAt, UserID: "admin", Events: &expectedEvents, } // HandleInstanceActionGetSuccessfully sets up the test server to respond to a Get request. func HandleInstanceActionGetSuccessfully(t *testing.T) { th.Mux.HandleFunc("/servers/asdfasdfasdf/os-instance-actions/okzeorkmkfs", 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, `{ "instanceAction": { "action": "stop", "events": [ { "event": "compute_stop_instance", "finish_time": "2018-04-25T01:26:36.00000", "host": "compute", "hostId": "2091634baaccdc4c5a1d57069c833e402921df696b7f970791b12ec6", "result": "Success", "start_time": "2018-04-25T01:26:36.00000", "traceback": null } ], "instance_uuid": "4bf3473b-d550-4b65-9409-292d44ab14a2", "message": null, "project_id": "6f70656e737461636b20342065766572", "request_id": "req-0d819d5c-1527-4669-bdf0-ffad31b5105b", "start_time": "2018-04-25T01:26:36.00000", "updated_at": "2018-04-25T01:26:36.00000", "user_id": "admin" } }`) }) } request_test.go000066400000000000000000000022771367513235700372210ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/instanceactions/testingpackage testing import ( "testing" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/instanceactions" "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() HandleInstanceActionListSuccessfully(t) expected := ListExpected pages := 0 err := instanceactions.List(client.ServiceClient(), "asdfasdfasdf", nil).EachPage(func(page pagination.Page) (bool, error) { pages++ actual, err := instanceactions.ExtractInstanceActions(page) th.AssertNoErr(t, err) if len(actual) != 2 { t.Fatalf("Expected 2 instance actions, got %d", len(actual)) } th.CheckDeepEquals(t, expected, actual) return true, nil }) th.AssertNoErr(t, err) th.CheckEquals(t, 1, pages) } func TestGet(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleInstanceActionGetSuccessfully(t) client := client.ServiceClient() actual, err := instanceactions.Get(client, "asdfasdfasdf", "okzeorkmkfs").Extract() if err != nil { t.Fatalf("Unexpected Get error: %v", err) } th.CheckDeepEquals(t, GetExpected, actual) } golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/instanceactions/urls.go000066400000000000000000000006001367513235700340450ustar00rootroot00000000000000package instanceactions import "github.com/gophercloud/gophercloud" func listURL(client *gophercloud.ServiceClient, id string) string { return client.ServiceURL("servers", id, "os-instance-actions") } func instanceActionsURL(client *gophercloud.ServiceClient, serverID, requestID string) string { return client.ServiceURL("servers", serverID, "os-instance-actions", requestID) } golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/keypairs/000077500000000000000000000000001367513235700311775ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/keypairs/doc.go000066400000000000000000000025551367513235700323020ustar00rootroot00000000000000/* 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 golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/keypairs/requests.go000066400000000000000000000055361367513235700334120ustar00rootroot00000000000000package 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 } resp, err := client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get returns public data about a previously uploaded KeyPair. func Get(client *gophercloud.ServiceClient, name string) (r GetResult) { resp, err := client.Get(getURL(client, name), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete requests the deletion of a previous stored KeyPair from the server. func Delete(client *gophercloud.ServiceClient, name string) (r DeleteResult) { resp, err := client.Delete(deleteURL(client, name), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/keypairs/results.go000066400000000000000000000050301367513235700332250ustar00rootroot00000000000000package 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 } golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/keypairs/testing/000077500000000000000000000000001367513235700326545ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/keypairs/testing/doc.go000066400000000000000000000000471367513235700337510ustar00rootroot00000000000000// keypairs unit tests package testing fixtures.go000066400000000000000000000211321367513235700347740ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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.go000066400000000000000000000037041367513235700360420ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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) } golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/keypairs/urls.go000066400000000000000000000010501367513235700325070ustar00rootroot00000000000000package 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) } golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/limits/000077500000000000000000000000001367513235700306515ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/limits/doc.go000066400000000000000000000004721367513235700317500ustar00rootroot00000000000000/* 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 golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/limits/requests.go000066400000000000000000000017031367513235700330540ustar00rootroot00000000000000package 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 } resp, err := client.Get(url, &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/limits/results.go000066400000000000000000000060521367513235700327040ustar00rootroot00000000000000package 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 } golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/limits/testing/000077500000000000000000000000001367513235700323265ustar00rootroot00000000000000fixtures.go000066400000000000000000000043231367513235700344510ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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.go000066400000000000000000000010071367513235700355060ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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) } golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/limits/urls.go000066400000000000000000000002711367513235700321650ustar00rootroot00000000000000package limits import ( "github.com/gophercloud/gophercloud" ) const resourcePath = "limits" func getURL(c *gophercloud.ServiceClient) string { return c.ServiceURL(resourcePath) } golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/lockunlock/000077500000000000000000000000001367513235700315145ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/lockunlock/doc.go000066400000000000000000000006661367513235700326200ustar00rootroot00000000000000/* 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 golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/lockunlock/requests.go000066400000000000000000000014171367513235700337210ustar00rootroot00000000000000package lockunlock import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions" ) // Lock is the operation responsible for locking a Compute server. func Lock(client *gophercloud.ServiceClient, id string) (r LockResult) { resp, err := client.Post(extensions.ActionURL(client, id), map[string]interface{}{"lock": nil}, nil, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Unlock is the operation responsible for unlocking a Compute server. func Unlock(client *gophercloud.ServiceClient, id string) (r UnlockResult) { resp, err := client.Post(extensions.ActionURL(client, id), map[string]interface{}{"unlock": nil}, nil, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/lockunlock/results.go000066400000000000000000000005431367513235700335460ustar00rootroot00000000000000package 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 } golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/lockunlock/testing/000077500000000000000000000000001367513235700331715ustar00rootroot00000000000000doc.go000066400000000000000000000000511367513235700342020ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/lockunlock/testing// unlocklock unit tests package testing fixtures.go000066400000000000000000000014431367513235700353140ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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.go000066400000000000000000000012511367513235700361670ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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) } golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/migrate/000077500000000000000000000000001367513235700310005ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/migrate/doc.go000066400000000000000000000013241367513235700320740ustar00rootroot00000000000000/* 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 golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/migrate/requests.go000066400000000000000000000037671367513235700332170ustar00rootroot00000000000000package migrate import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions" ) // Migrate will initiate a migration of the instance to another host. func Migrate(client *gophercloud.ServiceClient, id string) (r MigrateResult) { resp, err := client.Post(extensions.ActionURL(client, id), map[string]interface{}{"migrate": nil}, nil, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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 } resp, err := client.Post(extensions.ActionURL(client, id), b, nil, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/migrate/results.go000066400000000000000000000004011367513235700330230ustar00rootroot00000000000000package 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 } golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/migrate/testing/000077500000000000000000000000001367513235700324555ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/migrate/testing/doc.go000066400000000000000000000000631367513235700335500ustar00rootroot00000000000000// compute_extensions_startstop_v2 package testing fixtures.go000066400000000000000000000016371367513235700346050ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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.go000066400000000000000000000017021367513235700356370ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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) } golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/networks/000077500000000000000000000000001367513235700312245ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/networks/doc.go000066400000000000000000000007651367513235700323300ustar00rootroot00000000000000/* 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 golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/networks/requests.go000066400000000000000000000012441367513235700334270ustar00rootroot00000000000000package 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) { resp, err := client.Get(getURL(client, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/networks/results.go000066400000000000000000000072561367513235700332660ustar00rootroot00000000000000package 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 } golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/networks/testing/000077500000000000000000000000001367513235700327015ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/networks/testing/doc.go000066400000000000000000000000471367513235700337760ustar00rootroot00000000000000// networks unit tests package testing fixtures.go000066400000000000000000000142611367513235700350260ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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.go000066400000000000000000000017201367513235700360630ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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) } golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/networks/urls.go000066400000000000000000000005661367513235700325470ustar00rootroot00000000000000package 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) } golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/pauseunpause/000077500000000000000000000000001367513235700320665ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/pauseunpause/doc.go000066400000000000000000000007031367513235700331620ustar00rootroot00000000000000/* 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.go000066400000000000000000000014311367513235700342100ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/pauseunpausepackage pauseunpause import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions" ) // Pause is the operation responsible for pausing a Compute server. func Pause(client *gophercloud.ServiceClient, id string) (r PauseResult) { resp, err := client.Post(extensions.ActionURL(client, id), map[string]interface{}{"pause": nil}, nil, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Unpause is the operation responsible for unpausing a Compute server. func Unpause(client *gophercloud.ServiceClient, id string) (r UnpauseResult) { resp, err := client.Post(extensions.ActionURL(client, id), map[string]interface{}{"unpause": nil}, nil, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/pauseunpause/results.go000066400000000000000000000006751367513235700341260ustar00rootroot00000000000000package 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 } golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/pauseunpause/testing/000077500000000000000000000000001367513235700335435ustar00rootroot00000000000000doc.go000066400000000000000000000000531367513235700345560ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/pauseunpause/testing// pauseunpause unit tests package testing fixtures.go000066400000000000000000000014501367513235700356640ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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.go000066400000000000000000000012661367513235700367320ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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) } golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/quotasets/000077500000000000000000000000001367513235700314005ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/quotasets/doc.go000066400000000000000000000013451367513235700324770ustar00rootroot00000000000000/* 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 golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/quotasets/requests.go000066400000000000000000000074531367513235700336130ustar00rootroot00000000000000package quotasets import ( "github.com/gophercloud/gophercloud" ) // Get returns public data about a previously created QuotaSet. func Get(client *gophercloud.ServiceClient, tenantID string) (r GetResult) { resp, err := client.Get(getURL(client, tenantID), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // GetDetail returns detailed public data about a previously created QuotaSet. func GetDetail(client *gophercloud.ServiceClient, tenantID string) (r GetDetailResult) { resp, err := client.Get(getDetailURL(client, tenantID), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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 } resp, err := client.Put(updateURL(client, tenantID), reqBody, &r.Body, &gophercloud.RequestOpts{OkCodes: []int{200}}) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Resets the quotas for the given tenant to their default values. func Delete(client *gophercloud.ServiceClient, tenantID string) (r DeleteResult) { resp, err := client.Delete(deleteURL(client, tenantID), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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") } golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/quotasets/results.go000066400000000000000000000141601367513235700334320ustar00rootroot00000000000000package 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 } golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/quotasets/testing/000077500000000000000000000000001367513235700330555ustar00rootroot00000000000000doc.go000066400000000000000000000000501367513235700340650ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/quotasets/testing// quotasets unit tests package testing fixtures.go000066400000000000000000000167231367513235700352070ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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" : 9216000, "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": 9216000, "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: 9216000, 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: 9216000}, 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":9216000,"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(9216000), 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.go000066400000000000000000000040661367513235700362450ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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") } } golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/quotasets/urls.go000066400000000000000000000012071367513235700327140ustar00rootroot00000000000000package 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) } golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/remoteconsoles/000077500000000000000000000000001367513235700324115ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/remoteconsoles/doc.go000066400000000000000000000013711367513235700335070ustar00rootroot00000000000000/* Package remoteconsoles provides the ability to create server remote consoles through the Compute API. You need to specify at least "2.6" microversion for the ComputeClient to use that API. Example of Creating a new RemoteConsole computeClient, err := openstack.NewComputeV2(providerClient, endpointOptions) computeClient.Microversion = "2.6" createOpts := remoteconsoles.CreateOpts{ Protocol: remoteconsoles.ConsoleProtocolVNC, Type: remoteconsoles.ConsoleTypeNoVNC, } serverID := "b16ba811-199d-4ffd-8839-ba96c1185a67" remtoteConsole, err := remoteconsoles.Create(computeClient, serverID, createOpts).Extract() if err != nil { panic(err) } fmt.Printf("Console URL: %s\n", remtoteConsole.URL) */ package remoteconsoles requests.go000066400000000000000000000053351367513235700345420ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/remoteconsolespackage remoteconsoles import ( "github.com/gophercloud/gophercloud" ) // ConsoleProtocol represents valid remote console protocol. // It can be used to create a remote console with one of the pre-defined protocol. type ConsoleProtocol string const ( // ConsoleProtocolVNC represents the VNC console protocol. ConsoleProtocolVNC ConsoleProtocol = "vnc" // ConsoleProtocolSPICE represents the SPICE console protocol. ConsoleProtocolSPICE ConsoleProtocol = "spice" // ConsoleProtocolRDP represents the RDP console protocol. ConsoleProtocolRDP ConsoleProtocol = "rdp" // ConsoleProtocolSerial represents the Serial console protocol. ConsoleProtocolSerial ConsoleProtocol = "serial" // ConsoleProtocolMKS represents the MKS console protocol. ConsoleProtocolMKS ConsoleProtocol = "mks" ) // ConsoleType represents valid remote console type. // It can be used to create a remote console with one of the pre-defined type. type ConsoleType string const ( // ConsoleTypeNoVNC represents the VNC console type. ConsoleTypeNoVNC ConsoleType = "novnc" // ConsoleTypeXVPVNC represents the XVP VNC console type. ConsoleTypeXVPVNC ConsoleType = "xvpvnc" // ConsoleTypeRDPHTML5 represents the RDP HTML5 console type. ConsoleTypeRDPHTML5 ConsoleType = "rdp-html5" // ConsoleTypeSPICEHTML5 represents the SPICE HTML5 console type. ConsoleTypeSPICEHTML5 ConsoleType = "spice-html5" // ConsoleTypeSerial represents the Serial console type. ConsoleTypeSerial ConsoleType = "serial" // ConsoleTypeWebMKS represents the Web MKS console type. ConsoleTypeWebMKS ConsoleType = "webmks" ) // CreateOptsBuilder allows to add additional parameters to the Create request. type CreateOptsBuilder interface { ToRemoteConsoleCreateMap() (map[string]interface{}, error) } // CreateOpts specifies parameters to the Create request. type CreateOpts struct { // Protocol specifies the protocol of a new remote console. Protocol ConsoleProtocol `json:"protocol" required:"true"` // Type specifies the type of a new remote console. Type ConsoleType `json:"type" required:"true"` } // ToRemoteConsoleCreateMap builds a request body from the CreateOpts. func (opts CreateOpts) ToRemoteConsoleCreateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "remote_console") } // Create requests the creation of a new remote console on the specified server. func Create(client *gophercloud.ServiceClient, serverID string, opts CreateOptsBuilder) (r CreateResult) { reqBody, err := opts.ToRemoteConsoleCreateMap() if err != nil { r.Err = err return } resp, err := client.Post(createURL(client, serverID), reqBody, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } results.go000066400000000000000000000021461367513235700343650ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/remoteconsolespackage remoteconsoles import "github.com/gophercloud/gophercloud" type commonResult struct { gophercloud.Result } // CreateResult represents the result of a create operation. Call its Extract // method to interpret it as a RemoteConsole. type CreateResult struct { commonResult } // RemoteConsole represents the Compute service remote console object. type RemoteConsole struct { // Protocol contains remote console protocol. // You can use the RemoteConsoleProtocol custom type to unmarshal raw JSON // response into the pre-defined valid console protocol. Protocol string `json:"protocol"` // Type contains remote console type. // You can use the RemoteConsoleType custom type to unmarshal raw JSON // response into the pre-defined valid console type. Type string `json:"type"` // URL can be used to connect to the remote console. URL string `json:"url"` } // Extract interprets any commonResult as a RemoteConsole. func (r commonResult) Extract() (*RemoteConsole, error) { var s struct { RemoteConsole *RemoteConsole `json:"remote_console"` } err := r.ExtractInto(&s) return s.RemoteConsole, err } golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/remoteconsoles/testing/000077500000000000000000000000001367513235700340665ustar00rootroot00000000000000doc.go000066400000000000000000000000201367513235700350730ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/remoteconsoles/testingpackage testing fixtures.go000066400000000000000000000010341367513235700362050ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/remoteconsoles/testingpackage testing // RemoteConsoleCreateRequest represents a request to create a remote console. const RemoteConsoleCreateRequest = ` { "remote_console": { "protocol": "vnc", "type": "novnc" } } ` // RemoteConsoleCreateResult represents a raw server responce to the RemoteConsoleCreateRequest. const RemoteConsoleCreateResult = ` { "remote_console": { "protocol": "vnc", "type": "novnc", "url": "http://192.168.0.4:6080/vnc_auto.html?token=9a2372b9-6a0e-4f71-aca1-56020e6bb677" } } ` requests_test.go000066400000000000000000000025311367513235700372510ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/remoteconsoles/testingpackage testing import ( "fmt" "net/http" "testing" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/remoteconsoles" th "github.com/gophercloud/gophercloud/testhelper" fake "github.com/gophercloud/gophercloud/testhelper/client" ) func TestCreate(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/servers/b16ba811-199d-4ffd-8839-ba96c1185a67/remote-consoles", 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, RemoteConsoleCreateRequest) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, RemoteConsoleCreateResult) }) opts := remoteconsoles.CreateOpts{ Protocol: remoteconsoles.ConsoleProtocolVNC, Type: remoteconsoles.ConsoleTypeNoVNC, } s, err := remoteconsoles.Create(fake.ServiceClient(), "b16ba811-199d-4ffd-8839-ba96c1185a67", opts).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, s.Protocol, string(remoteconsoles.ConsoleProtocolVNC)) th.AssertEquals(t, s.Type, string(remoteconsoles.ConsoleTypeNoVNC)) th.AssertEquals(t, s.URL, "http://192.168.0.4:6080/vnc_auto.html?token=9a2372b9-6a0e-4f71-aca1-56020e6bb677") } golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/remoteconsoles/urls.go000066400000000000000000000005561367513235700337330ustar00rootroot00000000000000package remoteconsoles import "github.com/gophercloud/gophercloud" const ( rootPath = "servers" resourcePath = "remote-consoles" ) func rootURL(c *gophercloud.ServiceClient, serverID string) string { return c.ServiceURL(rootPath, serverID, resourcePath) } func createURL(c *gophercloud.ServiceClient, serverID string) string { return rootURL(c, serverID) } golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/rescueunrescue/000077500000000000000000000000001367513235700324105ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/rescueunrescue/doc.go000066400000000000000000000013621367513235700335060ustar00rootroot00000000000000/* 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.go000066400000000000000000000036061367513235700345400ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/rescueunrescuepackage rescueunrescue import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions" ) // 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 } resp, err := client.Post(extensions.ActionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Unrescue instructs the provider to return the server from RESCUE mode. func Unrescue(client *gophercloud.ServiceClient, id string) (r UnrescueResult) { resp, err := client.Post(extensions.ActionURL(client, id), map[string]interface{}{"unrescue": nil}, nil, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } results.go000066400000000000000000000013111367513235700343550ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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 } golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/rescueunrescue/testing/000077500000000000000000000000001367513235700340655ustar00rootroot00000000000000doc.go000066400000000000000000000000201367513235700350720ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/rescueunrescue/testingpackage testing fixtures.go000066400000000000000000000007271367513235700362140ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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.go000066400000000000000000000026411367513235700372520ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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) } golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/resetnetwork/000077500000000000000000000000001367513235700321045ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/resetnetwork/doc.go000066400000000000000000000006651367513235700332070ustar00rootroot00000000000000/* Package resetnetwork provides functionality to reset the network of a server that has been provisioned by the OpenStack Compute service. This action requires admin privileges and Nova configured with a Xen hypervisor driver. Example to Reset a Network of a Server serverID := "47b6b7b7-568d-40e4-868c-d5c41735532e" err := resetnetwork.ResetNetwork(client, id).ExtractErr() if err != nil { panic(err) } */ package resetnetwork requests.go000066400000000000000000000007311367513235700342300ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/resetnetworkpackage resetnetwork import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions" ) // ResetNetwork will reset the network of a server func ResetNetwork(client *gophercloud.ServiceClient, id string) (r ResetResult) { b := map[string]interface{}{ "resetNetwork": nil, } resp, err := client.Post(extensions.ActionURL(client, id), b, nil, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/resetnetwork/results.go000066400000000000000000000004051367513235700341330ustar00rootroot00000000000000package resetnetwork import ( "github.com/gophercloud/gophercloud" ) // ResetResult is the response of a ResetNetwork operation. Call its ExtractErr // method to determine if the request suceeded or failed. type ResetResult struct { gophercloud.ErrResult } golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/resetnetwork/testing/000077500000000000000000000000001367513235700335615ustar00rootroot00000000000000fixtures.go000066400000000000000000000007521367513235700357060ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/resetnetwork/testingpackage testing import ( "net/http" "testing" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) func mockResetNetworkResponse(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, `{"resetNetwork": null}`) w.WriteHeader(http.StatusAccepted) }) } requests_test.go000066400000000000000000000010041367513235700367360ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/resetnetwork/testingpackage testing import ( "testing" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/resetnetwork" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) const serverID = "b16ba811-199d-4ffd-8839-ba96c1185a67" func TestResetNetwork(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() mockResetNetworkResponse(t, serverID) err := resetnetwork.ResetNetwork(client.ServiceClient(), serverID).ExtractErr() th.AssertNoErr(t, err) } golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/resetstate/000077500000000000000000000000001367513235700315335ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/resetstate/doc.go000066400000000000000000000005361367513235700326330ustar00rootroot00000000000000/* 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 golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/resetstate/requests.go000066400000000000000000000014551367513235700337420ustar00rootroot00000000000000package resetstate import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions" ) // 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} resp, err := client.Post(extensions.ActionURL(client, id), map[string]interface{}{"os-resetState": stateMap}, nil, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/resetstate/results.go000066400000000000000000000004011367513235700335560ustar00rootroot00000000000000package 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 } golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/resetstate/testing/000077500000000000000000000000001367513235700332105ustar00rootroot00000000000000doc.go000066400000000000000000000000201367513235700342150ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/resetstate/testingpackage testing fixtures.go000066400000000000000000000010351367513235700353300ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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.go000066400000000000000000000010161367513235700363700ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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) } golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/schedulerhints/000077500000000000000000000000001367513235700323745ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/schedulerhints/doc.go000066400000000000000000000032641367513235700334750ustar00rootroot00000000000000/* 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.go000066400000000000000000000115461367513235700345260ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/schedulerhintspackage schedulerhints import ( "encoding/json" "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 } // The query needs to be sent as a marshalled string. b, err := json.Marshal(opts.Query) if err != nil { 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"] = string(b) } 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 } golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/schedulerhints/testing/000077500000000000000000000000001367513235700340515ustar00rootroot00000000000000doc.go000066400000000000000000000000551367513235700350660ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/schedulerhints/testing// schedulerhints unit tests package testing requests_test.go000066400000000000000000000070101367513235700372310ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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) } golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/secgroups/000077500000000000000000000000001367513235700313625ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/secgroups/doc.go000066400000000000000000000047361367513235700324700ustar00rootroot00000000000000/* 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 golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/secgroups/requests.go000066400000000000000000000157151367513235700335750ustar00rootroot00000000000000package 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)) } // CreateOpts is the struct responsible for creating a security group. type CreateOpts struct { // the name of your security group. Name string `json:"name" required:"true"` // the description of your security group. Description string `json:"description,omitempty"` } // 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 } resp, err := client.Post(rootURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // UpdateOpts is the struct responsible for updating an existing security group. type UpdateOpts struct { // the name of your security group. Name string `json:"name,omitempty"` // the description of your security group. Description *string `json:"description,omitempty"` } // 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 } resp, err := client.Put(resourceURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get will return details for a particular security group. func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { resp, err := client.Get(resourceURL(client, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete will permanently delete a security group from the project. func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { resp, err := client.Delete(resourceURL(client, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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 } resp, err := client.Post(rootRuleURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // DeleteRule will permanently delete a rule from a security group. func DeleteRule(client *gophercloud.ServiceClient, id string) (r DeleteRuleResult) { resp, err := client.Delete(resourceRuleURL(client, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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) { resp, err := client.Post(serverActionURL(client, serverID), actionMap("add", groupName), nil, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // RemoveServer will disassociate a server from a security group. func RemoveServer(client *gophercloud.ServiceClient, serverID, groupName string) (r RemoveServerResult) { resp, err := client.Post(serverActionURL(client, serverID), actionMap("remove", groupName), nil, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/secgroups/results.go000066400000000000000000000130221367513235700334100ustar00rootroot00000000000000package 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 } golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/secgroups/testing/000077500000000000000000000000001367513235700330375ustar00rootroot00000000000000doc.go000066400000000000000000000000501367513235700340470ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/secgroups/testing// secgroups unit tests package testing fixtures.go000066400000000000000000000162601367513235700351650ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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.go000066400000000000000000000146731367513235700362340ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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) description := "new_desc" opts := secgroups.UpdateOpts{ Name: "new_name", Description: &description, } 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) } golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/secgroups/urls.go000066400000000000000000000014471367513235700327040ustar00rootroot00000000000000package 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") } golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/servergroups/000077500000000000000000000000001367513235700321165ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/servergroups/doc.go000066400000000000000000000023131367513235700332110ustar00rootroot00000000000000/* 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 Create a Server Group with additional microversion 2.64 fields createOpts := servergroups.CreateOpts{ Name: "my_sg", Policy: "anti-affinity", Rules: &servergroups.Rules{ MaxServerPerHost: 3, }, } computeClient.Microversion = "2.64" result := servergroups.Create(computeClient, createOpts) serverGroup, err := result.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.go000066400000000000000000000043621367513235700342460ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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,omitempty"` // Policy specifies the name of a policy. // Requires microversion 2.64 or later. Policy string `json:"policy,omitempty"` // Rules specifies the set of rules. // Requires microversion 2.64 or later. Rules *Rules `json:"rules,omitempty"` } // 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 } resp, err := client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get returns data about a previously created ServerGroup. func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { resp, err := client.Get(getURL(client, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete requests the deletion of a previously allocated ServerGroup. func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { resp, err := client.Delete(deleteURL(client, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/servergroups/results.go000066400000000000000000000057411367513235700341550ustar00rootroot00000000000000package servergroups import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // A ServerGroup creates a policy for instance placement in the cloud. // You should use extract methods from microversions.go to retrieve additional // fields. 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{} // Policy is the policy of a server group. // This requires microversion 2.64 or later. Policy *string `json:"policy"` // Rules are the rules of the server group. // This requires microversion 2.64 or later. Rules *Rules `json:"rules"` } // Rules represents set of rules for a policy. // This requires microversion 2.64 or later. type Rules struct { // MaxServerPerHost specifies how many servers can reside on a single compute host. // It can be used only with the "anti-affinity" policy. MaxServerPerHost int `json:"max_server_per_host"` } // 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 } golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/servergroups/testing/000077500000000000000000000000001367513235700335735ustar00rootroot00000000000000doc.go000066400000000000000000000000531367513235700346060ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/servergroups/testing// servergroups unit tests package testing fixtures.go000066400000000000000000000144221367513235700357170ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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": {} } } ` // GetOutputMicroversion is a sample response to a Get call with microversion set to 2.64 const GetOutputMicroversion = ` { "server_group": { "id": "616fb98f-46ca-475e-917e-2563e5a8cd19", "name": "test", "policies": [ "anti-affinity" ], "policy": "anti-affinity", "rules": { "max_server_per_host": 3 }, "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": {} } } ` // CreateOutputMicroversion is a sample response to a Post call with microversion set to 2.64 const CreateOutputMicroversion = ` { "server_group": { "id": "616fb98f-46ca-475e-917e-2563e5a8cd19", "name": "test", "policies": [ "anti-affinity" ], "policy": "anti-affinity", "rules": { "max_server_per_host": 3 }, "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) }) } // HandleGetMicroversionSuccessfully configures the test server to respond to a Get request // for an existing server group with microversion set to 2.64 func HandleGetMicroversionSuccessfully(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, GetOutputMicroversion) }) } // 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) }) } // HandleCreateMicroversionSuccessfully configures the test server to respond to a Create request // for a new server group with microversion set to 2.64 func HandleCreateMicroversionSuccessfully(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" ], "policy": "anti-affinity", "rules": { "max_server_per_host": 3 } } } `) w.Header().Add("Content-Type", "application/json") fmt.Fprintf(w, CreateOutputMicroversion) }) } // 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.go000066400000000000000000000051431367513235700367600ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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 TestCreateMicroversion(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleCreateMicroversionSuccessfully(t) policy := "anti-affinity" rules := servergroups.Rules{ MaxServerPerHost: 3, } CreatedServerGroup.Policy = &policy CreatedServerGroup.Rules = &rules result := servergroups.Create(client.ServiceClient(), servergroups.CreateOpts{ Name: "test", Policies: []string{"anti-affinity"}, Policy: policy, Rules: &rules, }) actual, err := result.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 TestGetMicroversion(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleGetMicroversionSuccessfully(t) policy := "anti-affinity" rules := servergroups.Rules{ MaxServerPerHost: 3, } FirstServerGroup.Policy = &policy FirstServerGroup.Rules = &rules result := servergroups.Get(client.ServiceClient(), "4d8c3732-a248-40ed-bebc-539a6ffd25c0") // Extract basic fields. actual, err := result.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) } golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/servergroups/urls.go000066400000000000000000000010511367513235700334270ustar00rootroot00000000000000package 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) } golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/serverusage/000077500000000000000000000000001367513235700317035ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/serverusage/doc.go000066400000000000000000000007551367513235700330060ustar00rootroot00000000000000/* 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 golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/serverusage/results.go000066400000000000000000000013731367513235700337370ustar00rootroot00000000000000package 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 } golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/serverusage/testing/000077500000000000000000000000001367513235700333605ustar00rootroot00000000000000doc.go000066400000000000000000000000201367513235700343650ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/serverusage/testingpackage testing fixtures.go000066400000000000000000000012321367513235700354770ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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.go000066400000000000000000000032411367513235700365420ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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") } golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/services/000077500000000000000000000000001367513235700311735ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/services/doc.go000066400000000000000000000012511367513235700322660ustar00rootroot00000000000000/* Package services returns information about the compute services in the OpenStack cloud. Example of Retrieving list of all services opts := services.ListOpts{ Binary: "nova-scheduler", } allPages, err := services.List(computeClient, opts).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 of updating a service opts := services.UpdateOpts{ Status: services.ServiceDisabled, } updated, err := services.Update(client, serviceID, opts).Extract() if err != nil { panic(err) } */ package services golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/services/requests.go000066400000000000000000000046671367513235700334120ustar00rootroot00000000000000package 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 { ToServicesListQuery() (string, error) } // ListOpts represents options to list services. type ListOpts struct { Binary string `q:"binary"` Host string `q:"host"` } // ToServicesListQuery formats a ListOpts into a query string. func (opts ListOpts) ToServicesListQuery() (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.ToServicesListQuery() 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)} }) } type ServiceStatus string const ( // ServiceEnabled is used to mark a service as being enabled. ServiceEnabled ServiceStatus = "enabled" // ServiceDisabled is used to mark a service as being disabled. ServiceDisabled ServiceStatus = "disabled" ) // UpdateOpts specifies the base attributes that may be updated on a service. type UpdateOpts struct { // Status represents the new service status. One of enabled or disabled. Status ServiceStatus `json:"status,omitempty"` // DisabledReason represents the reason for disabling a service. DisabledReason string `json:"disabled_reason,omitempty"` // ForcedDown is a manual override to tell nova that the service in question // has been fenced manually by the operations team. ForcedDown bool `json:"forced_down,omitempty"` } // ToServiceUpdateMap formats an UpdateOpts structure into a request body. func (opts UpdateOpts) ToServiceUpdateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "") } // Update requests that various attributes of the indicated service be changed. func Update(client *gophercloud.ServiceClient, id string, opts UpdateOpts) (r UpdateResult) { b, err := opts.ToServiceUpdateMap() if err != nil { r.Err = err return } resp, err := client.Put(updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/services/results.go000066400000000000000000000050221367513235700332220ustar00rootroot00000000000000package services import ( "encoding/json" "fmt" "strconv" "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"` // Whether or not service was forced down manually. ForcedDown bool `json:"forced_down"` // The name of the host. Host string `json:"host"` // The id of the service. ID string `json:"-"` // 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 ID interface{} `json:"id"` 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) // OpenStack Compute service returns ID in string representation since // 2.53 microversion API (Pike release). switch t := s.ID.(type) { case int: r.ID = strconv.Itoa(t) case float64: r.ID = strconv.Itoa(int(t)) case string: r.ID = t default: return fmt.Errorf("ID has unexpected type: %T", t) } return nil } type serviceResult struct { gophercloud.Result } // Extract interprets any UpdateResult as a service, if possible. func (r serviceResult) Extract() (*Service, error) { var s struct { Service Service `json:"service"` } err := r.ExtractInto(&s) return &s.Service, err } // UpdateResult is the response from an Update operation. Call its Extract // method to interpret it as a Server. type UpdateResult struct { serviceResult } // 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 } golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/services/testing/000077500000000000000000000000001367513235700326505ustar00rootroot00000000000000fixtures.go000066400000000000000000000211531367513235700347730ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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" ) // ServiceListBodyPre253 represents a raw service list from the Compute API // with microversion older than 2.53. const ServiceListBodyPre253 = ` { "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" } ] } ` var ( // FirstFakeServicePre253 represents the first service from the // ServiceListBodyPre253. FirstFakeServicePre253 = 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", } // SecondFakeServicePre253 represents the second service from the // ServiceListBodyPre253. SecondFakeServicePre253 = 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", } // ThirdFakeServicePre253 represents the third service from the // ServiceListBodyPre253. ThirdFakeServicePre253 = 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", } // FourthFakeServicePre253 represents the fourth service from the // ServiceListBodyPre253. FourthFakeServicePre253 = 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", } ) // ServiceListBody represents a raw service list result with Pike+ release. const ServiceListBody = ` { "services": [ { "id": "4c720fa0-02c3-4834-8279-9eecf9edb6cb", "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": "1fdfec3e-ee03-4e36-b99b-71cf2967b70c", "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": "bd0b2e30-809e-4160-bd3d-f23ca30e9b68", "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": "fe41c476-33e2-4ac3-ad21-3ffaf1b9c644", "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" } ] } ` var ( // FirstFakeService represents the first service from the ServiceListBody. FirstFakeService = services.Service{ Binary: "nova-scheduler", DisabledReason: "test1", Host: "host1", ID: "4c720fa0-02c3-4834-8279-9eecf9edb6cb", State: "up", Status: "disabled", UpdatedAt: time.Date(2012, 10, 29, 13, 42, 2, 0, time.UTC), Zone: "internal", } // SecondFakeService represents the second service from the ServiceListBody. SecondFakeService = services.Service{ Binary: "nova-compute", DisabledReason: "test2", Host: "host1", ID: "1fdfec3e-ee03-4e36-b99b-71cf2967b70c", State: "up", Status: "disabled", UpdatedAt: time.Date(2012, 10, 29, 13, 42, 5, 0, time.UTC), Zone: "nova", } // ThirdFakeService represents the third service from the ServiceListBody. ThirdFakeService = services.Service{ Binary: "nova-scheduler", DisabledReason: "", Host: "host2", ID: "bd0b2e30-809e-4160-bd3d-f23ca30e9b68", State: "down", Status: "enabled", UpdatedAt: time.Date(2012, 9, 19, 6, 55, 34, 0, time.UTC), Zone: "internal", } // FourthFakeService represents the fourth service from the ServiceListBody. FourthFakeService = services.Service{ Binary: "nova-compute", DisabledReason: "test4", Host: "host2", ID: "fe41c476-33e2-4ac3-ad21-3ffaf1b9c644", State: "down", Status: "disabled", UpdatedAt: time.Date(2012, 9, 18, 8, 3, 38, 0, time.UTC), Zone: "nova", } ) // ServiceUpdate represents a raw service from the Compute service update API const ServiceUpdate = ` { "service": { "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" } } ` //FakeServiceUpdateBody represents the updated service var FakeServiceUpdateBody = services.Service{ Binary: "nova-scheduler", DisabledReason: "test1", ForcedDown: false, Host: "host1", ID: "1", State: "up", Status: "disabled", UpdatedAt: time.Date(2012, 10, 29, 13, 42, 2, 0, time.UTC), Zone: "internal", } // HandleListPre253Successfully configures the test server to respond to a List // request to a Compute server API pre 2.53 microversion release. func HandleListPre253Successfully(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, ServiceListBodyPre253) }) } // HandleListSuccessfully configures the test server to respond to a List // request to a Compute server with Pike+ release. 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) }) } // HandleUpdateSuccessfully configures the test server to respond to a Update // request to a Compute server with Pike+ release. func HandleUpdateSuccessfully(t *testing.T) { th.Mux.HandleFunc("/os-services/fake-service-id", 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, `{"status": "disabled"}`) fmt.Fprintf(w, ServiceUpdate) }) } requests_test.go000066400000000000000000000045371367513235700360430ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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 TestListServicesPre253(t *testing.T) { testhelper.SetupHTTP() defer testhelper.TeardownHTTP() HandleListPre253Successfully(t) pages := 0 err := services.List(client.ServiceClient(), nil).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, FirstFakeServicePre253, actual[0]) testhelper.CheckDeepEquals(t, SecondFakeServicePre253, actual[1]) testhelper.CheckDeepEquals(t, ThirdFakeServicePre253, actual[2]) testhelper.CheckDeepEquals(t, FourthFakeServicePre253, actual[3]) return true, nil }) testhelper.AssertNoErr(t, err) if pages != 1 { t.Errorf("Expected 1 page, saw %d", pages) } } func TestListServices(t *testing.T) { testhelper.SetupHTTP() defer testhelper.TeardownHTTP() HandleListSuccessfully(t) pages := 0 opts := services.ListOpts{ Binary: "fake-binary", Host: "host123", } err := services.List(client.ServiceClient(), opts).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) } } func TestUpdateService(t *testing.T) { testhelper.SetupHTTP() defer testhelper.TeardownHTTP() HandleUpdateSuccessfully(t) client := client.ServiceClient() actual, err := services.Update(client, "fake-service-id", services.UpdateOpts{Status: services.ServiceDisabled}).Extract() if err != nil { t.Fatalf("Unexpected Update error: %v", err) } testhelper.CheckDeepEquals(t, FakeServiceUpdateBody, *actual) } golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/services/urls.go000066400000000000000000000004051367513235700325060ustar00rootroot00000000000000package services import "github.com/gophercloud/gophercloud" func listURL(c *gophercloud.ServiceClient) string { return c.ServiceURL("os-services") } func updateURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL("os-services", id) } golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/shelveunshelve/000077500000000000000000000000001367513235700324105ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/shelveunshelve/doc.go000066400000000000000000000011201367513235700334760ustar00rootroot00000000000000/* Package shelveunshelve provides functionality to start and stop servers that have been provisioned by the OpenStack Compute service. Example to Shelve, Shelve-offload and Unshelve a Server serverID := "47b6b7b7-568d-40e4-868c-d5c41735532e" err := shelveunshelve.Shelve(computeClient, serverID).ExtractErr() if err != nil { panic(err) } err := shelveunshelve.ShelveOffload(computeClient, serverID).ExtractErr() if err != nil { panic(err) } err := shelveunshelve.Unshelve(computeClient, serverID, nil).ExtractErr() if err != nil { panic(err) } */ package shelveunshelve requests.go000066400000000000000000000040241367513235700345330ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/shelveunshelvepackage shelveunshelve import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions" ) // Shelve is the operation responsible for shelving a Compute server. func Shelve(client *gophercloud.ServiceClient, id string) (r ShelveResult) { resp, err := client.Post(extensions.ActionURL(client, id), map[string]interface{}{"shelve": nil}, nil, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // ShelveOffload is the operation responsible for Shelve-Offload a Compute server. func ShelveOffload(client *gophercloud.ServiceClient, id string) (r ShelveOffloadResult) { resp, err := client.Post(extensions.ActionURL(client, id), map[string]interface{}{"shelveOffload": nil}, nil, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // UnshelveOptsBuilder allows extensions to add additional parameters to the // Unshelve request. type UnshelveOptsBuilder interface { ToUnshelveMap() (map[string]interface{}, error) } // UnshelveOpts specifies parameters of shelve-offload action. type UnshelveOpts struct { // Sets the availability zone to unshelve a server // Available only after nova 2.77 AvailabilityZone string `json:"availability_zone,omitempty"` } func (opts UnshelveOpts) ToUnshelveMap() (map[string]interface{}, error) { // Key 'availabilty_zone' is required if the unshelve action is an object // i.e {"unshelve": {}} will be rejected b, err := gophercloud.BuildRequestBody(opts, "unshelve") if err != nil { return nil, err } if _, ok := b["unshelve"].(map[string]interface{})["availability_zone"]; !ok { b["unshelve"] = nil } return b, err } // Unshelve is the operation responsible for unshelve a Compute server. func Unshelve(client *gophercloud.ServiceClient, id string, opts UnshelveOptsBuilder) (r UnshelveResult) { b, err := opts.ToUnshelveMap() if err != nil { r.Err = err return } resp, err := client.Post(extensions.ActionURL(client, id), b, nil, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } results.go000066400000000000000000000012111367513235700343540ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/shelveunshelvepackage shelveunshelve import "github.com/gophercloud/gophercloud" // ShelveResult is the response from a Shelve operation. Call its ExtractErr // method to determine if the request succeeded or failed. type ShelveResult struct { gophercloud.ErrResult } // ShelveOffloadResult is the response from a Shelve operation. Call its ExtractErr // method to determine if the request succeeded or failed. type ShelveOffloadResult struct { gophercloud.ErrResult } // UnshelveResult is the response from Stop operation. Call its ExtractErr // method to determine if the request succeeded or failed. type UnshelveResult struct { gophercloud.ErrResult } golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/shelveunshelve/testing/000077500000000000000000000000001367513235700340655ustar00rootroot00000000000000fixtures.go000066400000000000000000000030671367513235700362140ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/shelveunshelve/testingpackage testing import ( "fmt" "net/http" "testing" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) func mockShelveServerResponse(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, `{"shelve": null}`) w.WriteHeader(http.StatusAccepted) }) } func mockShelveOffloadServerResponse(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, `{"shelveOffload": null}`) w.WriteHeader(http.StatusAccepted) }) } func mockUnshelveServerResponseWithAvailabilityZone(t *testing.T, id string, az 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(`{ "unshelve": { "availability_zone": "%s" } }`, az)) w.WriteHeader(http.StatusAccepted) }) } func mockUnshelveServerResponseNoAvailabilityZone(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, `{"unshelve": null}`) w.WriteHeader(http.StatusAccepted) }) } requests_test.go000066400000000000000000000026701367513235700372540ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/shelveunshelve/testingpackage testing import ( "testing" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/shelveunshelve" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) const serverID = "{serverId}" const availabilityZone = "test-zone" func TestShelve(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() mockShelveServerResponse(t, serverID) err := shelveunshelve.Shelve(client.ServiceClient(), serverID).ExtractErr() th.AssertNoErr(t, err) } func TestShelveOffload(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() mockShelveOffloadServerResponse(t, serverID) err := shelveunshelve.ShelveOffload(client.ServiceClient(), serverID).ExtractErr() th.AssertNoErr(t, err) } func TestUnshelveNoAvailabilityZone(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() unshelveOpts := shelveunshelve.UnshelveOpts{} mockUnshelveServerResponseNoAvailabilityZone(t, serverID) err := shelveunshelve.Unshelve(client.ServiceClient(), serverID, unshelveOpts).ExtractErr() th.AssertNoErr(t, err) } func TestUnshelveWithAvailabilityZone(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() unshelveOpts := shelveunshelve.UnshelveOpts{ AvailabilityZone: availabilityZone, } mockUnshelveServerResponseWithAvailabilityZone(t, serverID, availabilityZone) err := shelveunshelve.Unshelve(client.ServiceClient(), serverID, unshelveOpts).ExtractErr() th.AssertNoErr(t, err) } golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/startstop/000077500000000000000000000000001367513235700314135ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/startstop/doc.go000066400000000000000000000006601367513235700325110ustar00rootroot00000000000000/* 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 golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/startstop/requests.go000066400000000000000000000014201367513235700336120ustar00rootroot00000000000000package startstop import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions" ) // Start is the operation responsible for starting a Compute server. func Start(client *gophercloud.ServiceClient, id string) (r StartResult) { resp, err := client.Post(extensions.ActionURL(client, id), map[string]interface{}{"os-start": nil}, nil, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Stop is the operation responsible for stopping a Compute server. func Stop(client *gophercloud.ServiceClient, id string) (r StopResult) { resp, err := client.Post(extensions.ActionURL(client, id), map[string]interface{}{"os-stop": nil}, nil, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/startstop/results.go000066400000000000000000000006561367513235700334520ustar00rootroot00000000000000package 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 } golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/startstop/testing/000077500000000000000000000000001367513235700330705ustar00rootroot00000000000000doc.go000066400000000000000000000000501367513235700341000ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/startstop/testing// startstop unit tests package testing fixtures.go000066400000000000000000000014501367513235700352110ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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.go000066400000000000000000000012441367513235700362530ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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) } golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/suspendresume/000077500000000000000000000000001367513235700322525ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/suspendresume/doc.go000066400000000000000000000007141367513235700333500ustar00rootroot00000000000000/* 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.go000066400000000000000000000014431367513235700343770ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/suspendresumepackage suspendresume import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions" ) // Suspend is the operation responsible for suspending a Compute server. func Suspend(client *gophercloud.ServiceClient, id string) (r SuspendResult) { resp, err := client.Post(extensions.ActionURL(client, id), map[string]interface{}{"suspend": nil}, nil, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Resume is the operation responsible for resuming a Compute server. func Resume(client *gophercloud.ServiceClient, id string) (r UnsuspendResult) { resp, err := client.Post(extensions.ActionURL(client, id), map[string]interface{}{"resume": nil}, nil, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } results.go000066400000000000000000000007121367513235700342230ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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 } golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/suspendresume/testing/000077500000000000000000000000001367513235700337275ustar00rootroot00000000000000doc.go000066400000000000000000000000541367513235700347430ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/suspendresume/testing// suspendresume unit tests package testing fixtures.go000066400000000000000000000014521367513235700360520ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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.go000066400000000000000000000012741367513235700371150ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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) } golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/tags/000077500000000000000000000000001367513235700303065ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/tags/doc.go000066400000000000000000000027461367513235700314130ustar00rootroot00000000000000/* Package tags manages Tags on Compute V2 servers. This extension is available since 2.26 Compute V2 API microversion. Example to List all server Tags client.Microversion = "2.26" serverTags, err := tags.List(client, serverID).Extract() if err != nil { log.Fatal(err) } fmt.Printf("Tags: %v\n", serverTags) Example to Check if the specific Tag exists on a server client.Microversion = "2.26" exists, err := tags.Check(client, serverID, tag).Extract() if err != nil { log.Fatal(err) } if exists { log.Printf("Tag %s is set\n", tag) } else { log.Printf("Tag %s is not set\n", tag) } Example to Replace all Tags on a server client.Microversion = "2.26" newTags, err := tags.ReplaceAll(client, serverID, tags.ReplaceAllOpts{Tags: []string{"foo", "bar"}}).Extract() if err != nil { log.Fatal(err) } fmt.Printf("New tags: %v\n", newTags) Example to Add a new Tag on a server client.Microversion = "2.26" err := tags.Add(client, serverID, "foo").ExtractErr() if err != nil { log.Fatal(err) } Example to Delete a Tag on a server client.Microversion = "2.26" err := tags.Delete(client, serverID, "foo").ExtractErr() if err != nil { log.Fatal(err) } Example to Delete all Tags on a server client.Microversion = "2.26" err := tags.DeleteAll(client, serverID).ExtractErr() if err != nil { log.Fatal(err) } */ package tags golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/tags/requests.go000066400000000000000000000051451367513235700325150ustar00rootroot00000000000000package tags import "github.com/gophercloud/gophercloud" // List all tags on a server. func List(client *gophercloud.ServiceClient, serverID string) (r ListResult) { url := listURL(client, serverID) resp, err := client.Get(url, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Check if a tag exists on a server. func Check(client *gophercloud.ServiceClient, serverID, tag string) (r CheckResult) { url := checkURL(client, serverID, tag) resp, err := client.Get(url, nil, &gophercloud.RequestOpts{ OkCodes: []int{204}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // ReplaceAllOptsBuilder allows to add additional parameters to the ReplaceAll request. type ReplaceAllOptsBuilder interface { ToTagsReplaceAllMap() (map[string]interface{}, error) } // ReplaceAllOpts provides options used to replace Tags on a server. type ReplaceAllOpts struct { Tags []string `json:"tags" required:"true"` } // ToTagsReplaceAllMap formats a ReplaceALlOpts into the body of the ReplaceAll request. func (opts ReplaceAllOpts) ToTagsReplaceAllMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "") } // ReplaceAll replaces all Tags on a server. func ReplaceAll(client *gophercloud.ServiceClient, serverID string, opts ReplaceAllOptsBuilder) (r ReplaceAllResult) { b, err := opts.ToTagsReplaceAllMap() url := replaceAllURL(client, serverID) if err != nil { r.Err = err return } resp, err := client.Put(url, &b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Add adds a new Tag on a server. func Add(client *gophercloud.ServiceClient, serverID, tag string) (r AddResult) { url := addURL(client, serverID, tag) resp, err := client.Put(url, nil, nil, &gophercloud.RequestOpts{ OkCodes: []int{201, 204}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete removes a tag from a server. func Delete(client *gophercloud.ServiceClient, serverID, tag string) (r DeleteResult) { url := deleteURL(client, serverID, tag) resp, err := client.Delete(url, &gophercloud.RequestOpts{ OkCodes: []int{204}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // DeleteAll removes all tag from a server. func DeleteAll(client *gophercloud.ServiceClient, serverID string) (r DeleteResult) { url := deleteAllURL(client, serverID) resp, err := client.Delete(url, &gophercloud.RequestOpts{ OkCodes: []int{204}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/tags/results.go000066400000000000000000000017461367513235700323460ustar00rootroot00000000000000package tags import "github.com/gophercloud/gophercloud" type commonResult struct { gophercloud.Result } // Extract is a function that accepts a result and extracts a tags resource. func (r commonResult) Extract() ([]string, error) { var s struct { Tags []string `json:"tags"` } err := r.ExtractInto(&s) return s.Tags, err } type ListResult struct { commonResult } // CheckResult is the result from the Check operation. type CheckResult struct { gophercloud.Result } func (r CheckResult) Extract() (bool, error) { exists := r.Err == nil if r.Err != nil { if _, ok := r.Err.(gophercloud.ErrDefault404); ok { r.Err = nil } } return exists, r.Err } // ReplaceAllResult is the result from the ReplaceAll operation. type ReplaceAllResult struct { commonResult } // AddResult is the result from the Add operation. type AddResult struct { gophercloud.ErrResult } // DeleteResult is the result from the Delete operation. type DeleteResult struct { gophercloud.ErrResult } golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/tags/testing/000077500000000000000000000000001367513235700317635ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/tags/testing/doc.go000066400000000000000000000000431367513235700330540ustar00rootroot00000000000000// tags unit tests package testing fixtures.go000066400000000000000000000006431367513235700341070ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/tags/testingpackage testing // TagsListResponse represents a raw tags response. const TagsListResponse = ` { "tags": ["foo", "bar", "baz"] } ` // TagsReplaceAllRequest represents a raw tags Replace request. const TagsReplaceAllRequest = ` { "tags": ["tag1", "tag2", "tag3"] } ` // TagsReplaceAllResponse represents a raw tags Replace response. const TagsReplaceAllResponse = ` { "tags": ["tag1", "tag2", "tag3"] } ` requests_test.go000066400000000000000000000103541367513235700351500ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/tags/testingpackage testing import ( "fmt" "net/http" "testing" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/tags" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) func TestList(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/servers/uuid1/tags", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, http.MethodGet) th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) _, err := fmt.Fprintf(w, TagsListResponse) th.AssertNoErr(t, err) }) expected := []string{"foo", "bar", "baz"} actual, err := tags.List(client.ServiceClient(), "uuid1").Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, expected, actual) } func TestCheckOk(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/servers/uuid1/tags/foo", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, http.MethodGet) th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusNoContent) }) exists, err := tags.Check(client.ServiceClient(), "uuid1", "foo").Extract() th.AssertNoErr(t, err) th.AssertEquals(t, true, exists) } func TestCheckFail(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/servers/uuid1/tags/bar", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, http.MethodGet) th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusNotFound) }) exists, err := tags.Check(client.ServiceClient(), "uuid1", "bar").Extract() th.AssertNoErr(t, err) th.AssertEquals(t, false, exists) } func TestReplaceAll(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/servers/uuid1/tags", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, http.MethodPut) th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) _, err := fmt.Fprintf(w, TagsReplaceAllResponse) th.AssertNoErr(t, err) }) expected := []string{"tag1", "tag2", "tag3"} actual, err := tags.ReplaceAll(client.ServiceClient(), "uuid1", tags.ReplaceAllOpts{Tags: []string{"tag1", "tag2", "tag3"}}).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, expected, actual) } func TestAddCreated(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/servers/uuid1/tags/foo", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, http.MethodPut) th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) }) err := tags.Add(client.ServiceClient(), "uuid1", "foo").ExtractErr() th.AssertNoErr(t, err) } func TestAddExists(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/servers/uuid1/tags/foo", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, http.MethodPut) th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusNoContent) }) err := tags.Add(client.ServiceClient(), "uuid1", "foo").ExtractErr() th.AssertNoErr(t, err) } func TestDelete(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/servers/uuid1/tags/foo", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, http.MethodDelete) th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusNoContent) }) err := tags.Delete(client.ServiceClient(), "uuid1", "foo").ExtractErr() th.AssertNoErr(t, err) } func TestDeleteAll(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/servers/uuid1/tags", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, http.MethodDelete) th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusNoContent) }) err := tags.DeleteAll(client.ServiceClient(), "uuid1").ExtractErr() th.AssertNoErr(t, err) } golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/tags/urls.go000066400000000000000000000020571367513235700316260ustar00rootroot00000000000000package tags import "github.com/gophercloud/gophercloud" const ( rootResourcePath = "servers" resourcePath = "tags" ) func rootURL(c *gophercloud.ServiceClient, serverID string) string { return c.ServiceURL(rootResourcePath, serverID, resourcePath) } func resourceURL(c *gophercloud.ServiceClient, serverID, tag string) string { return c.ServiceURL(rootResourcePath, serverID, resourcePath, tag) } func listURL(c *gophercloud.ServiceClient, serverID string) string { return rootURL(c, serverID) } func checkURL(c *gophercloud.ServiceClient, serverID, tag string) string { return resourceURL(c, serverID, tag) } func replaceAllURL(c *gophercloud.ServiceClient, serverID string) string { return rootURL(c, serverID) } func addURL(c *gophercloud.ServiceClient, serverID, tag string) string { return resourceURL(c, serverID, tag) } func deleteURL(c *gophercloud.ServiceClient, serverID, tag string) string { return resourceURL(c, serverID, tag) } func deleteAllURL(c *gophercloud.ServiceClient, serverID string) string { return rootURL(c, serverID) } golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/tenantnetworks/000077500000000000000000000000001367513235700324365ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/tenantnetworks/doc.go000066400000000000000000000011621367513235700335320ustar00rootroot00000000000000/* 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.go000066400000000000000000000012531367513235700345620ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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) { resp, err := client.Get(getURL(client, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } results.go000066400000000000000000000026361367513235700344160ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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 } golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/tenantnetworks/testing/000077500000000000000000000000001367513235700341135ustar00rootroot00000000000000doc.go000066400000000000000000000000551367513235700351300ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/tenantnetworks/testing// tenantnetworks unit tests package testing fixtures.go000066400000000000000000000043431367513235700362400ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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.go000066400000000000000000000017501367513235700373000ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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) } golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/tenantnetworks/urls.go000066400000000000000000000006031367513235700337510ustar00rootroot00000000000000package 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) } golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/testing/000077500000000000000000000000001367513235700310255ustar00rootroot00000000000000delegate_test.go000066400000000000000000000031521367513235700341070ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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.") } golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/testing/doc.go000066400000000000000000000000511367513235700321150ustar00rootroot00000000000000// extensions unit tests package testing golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/testing/fixtures.go000066400000000000000000000026061367513235700332310ustar00rootroot00000000000000package 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." } } `) }) } golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/urls.go000066400000000000000000000002741367513235700306670ustar00rootroot00000000000000package extensions import "github.com/gophercloud/gophercloud" func ActionURL(client *gophercloud.ServiceClient, id string) string { return client.ServiceURL("servers", id, "action") } golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/usage/000077500000000000000000000000001367513235700304545ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/usage/doc.go000066400000000000000000000026121367513235700315510ustar00rootroot00000000000000/* 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 golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/usage/requests.go000066400000000000000000000075561367513235700326730ustar00rootroot00000000000000package 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"` // Limit limits the amount of results returned by the API. // This requires the client to be set to microversion 2.40 or later. Limit int `q:"limit"` // Marker instructs the API call where to start listing from. // This requires the client to be set to microversion 2.40 or later. Marker string `q:"marker"` } // 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) { q, err := gophercloud.BuildQueryString(opts) if err != nil { return "", err } params := q.Query() 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"` // Limit limits the amount of results returned by the API. // This requires the client to be set to microversion 2.40 or later. Limit int `q:"limit"` // Marker instructs the API call where to start listing from. // This requires the client to be set to microversion 2.40 or later. Marker string `q:"marker"` } // 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) { q, err := gophercloud.BuildQueryString(opts) if err != nil { return "", err } params := q.Query() 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}} }) } golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/usage/results.go000066400000000000000000000125001367513235700325020ustar00rootroot00000000000000package 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) } golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/usage/testing/000077500000000000000000000000001367513235700321315ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/usage/testing/doc.go000066400000000000000000000000621367513235700332230ustar00rootroot00000000000000// simple tenant usage unit tests package testing fixtures.go000066400000000000000000000241031367513235700342520ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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.go000066400000000000000000000023551367513235700353200ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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) } golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/usage/urls.go000066400000000000000000000005301367513235700317660ustar00rootroot00000000000000package 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) } golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/volumeattach/000077500000000000000000000000001367513235700320445ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/volumeattach/doc.go000066400000000000000000000012771367513235700331470ustar00rootroot00000000000000/* 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.go000066400000000000000000000044421367513235700341730ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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 } resp, err := client.Post(createURL(client, serverID), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get returns public data about a previously created VolumeAttachment. func Get(client *gophercloud.ServiceClient, serverID, attachmentID string) (r GetResult) { resp, err := client.Get(getURL(client, serverID, attachmentID), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete requests the deletion of a previous stored VolumeAttachment from // the server. func Delete(client *gophercloud.ServiceClient, serverID, attachmentID string) (r DeleteResult) { resp, err := client.Delete(deleteURL(client, serverID, attachmentID), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/volumeattach/results.go000066400000000000000000000043551367513235700341030ustar00rootroot00000000000000package 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 } golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/volumeattach/testing/000077500000000000000000000000001367513235700335215ustar00rootroot00000000000000doc.go000066400000000000000000000000531367513235700345340ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/volumeattach/testing// volumeattach unit tests package testing fixtures.go000066400000000000000000000063441367513235700356510ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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.go000066400000000000000000000057371367513235700367170ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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) } golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/extensions/volumeattach/urls.go000066400000000000000000000013011367513235700333530ustar00rootroot00000000000000package 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.12.0/openstack/compute/v2/flavors/000077500000000000000000000000001367513235700266255ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/flavors/doc.go000066400000000000000000000061121367513235700277210ustar00rootroot00000000000000/* 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 golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/flavors/requests.go000066400000000000000000000254341367513235700310370ustar00rootroot00000000000000package 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 } resp, err := client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get retrieves details of a single flavor. Use Extract to convert its // result into a Flavor. func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { resp, err := client.Get(getURL(client, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete deletes the specified flavor ID. func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { resp, err := client.Delete(deleteURL(client, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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 } resp, err := client.Post(accessActionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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 } resp, err := client.Post(accessActionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // ExtraSpecs requests all the extra-specs for the given flavor ID. func ListExtraSpecs(client *gophercloud.ServiceClient, flavorID string) (r ListExtraSpecsResult) { resp, err := client.Get(extraSpecsListURL(client, flavorID), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } func GetExtraSpec(client *gophercloud.ServiceClient, flavorID string, key string) (r GetExtraSpecResult) { resp, err := client.Get(extraSpecsGetURL(client, flavorID, key), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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 } resp, err := client.Post(extraSpecsCreateURL(client, flavorID), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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 } resp, err := client.Put(extraSpecUpdateURL(client, flavorID, key), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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) { resp, err := client.Delete(extraSpecDeleteURL(client, flavorID, key), &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/flavors/results.go000066400000000000000000000147351367513235700306670ustar00rootroot00000000000000package 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.12.0/openstack/compute/v2/flavors/testing/000077500000000000000000000000001367513235700303025ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/flavors/testing/doc.go000066400000000000000000000000461367513235700313760ustar00rootroot00000000000000// flavors unit tests package testing golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/flavors/testing/fixtures.go000066400000000000000000000065711367513235700325130ustar00rootroot00000000000000package 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) }) } golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/flavors/testing/requests_test.go000066400000000000000000000226201367513235700335450ustar00rootroot00000000000000package 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": 9216000, "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: 9216000, 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.12.0/openstack/compute/v2/flavors/urls.go000066400000000000000000000027261367513235700301500ustar00rootroot00000000000000package 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.12.0/openstack/compute/v2/images/000077500000000000000000000000001367513235700264165ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/images/doc.go000066400000000000000000000013601367513235700275120ustar00rootroot00000000000000/* 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 golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/images/requests.go000066400000000000000000000041171367513235700306230ustar00rootroot00000000000000package 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) { resp, err := client.Get(getURL(client, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete deletes the specified image ID. func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { resp, err := client.Delete(deleteURL(client, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/images/results.go000066400000000000000000000047431367513235700304560ustar00rootroot00000000000000package 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.12.0/openstack/compute/v2/images/testing/000077500000000000000000000000001367513235700300735ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/images/testing/doc.go000066400000000000000000000000451367513235700311660ustar00rootroot00000000000000// images unit tests package testing golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/images/testing/requests_test.go000066400000000000000000000133201367513235700333330ustar00rootroot00000000000000package 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.12.0/openstack/compute/v2/images/urls.go000066400000000000000000000006131367513235700277320ustar00rootroot00000000000000package 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.12.0/openstack/compute/v2/servers/000077500000000000000000000000001367513235700266425ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/servers/doc.go000066400000000000000000000045431367513235700277440ustar00rootroot00000000000000/* 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 golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/servers/errors.go000066400000000000000000000045361367513235700305150ustar00rootroot00000000000000package 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) } golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/servers/requests.go000066400000000000000000000632651367513235700310600ustar00rootroot00000000000000package servers import ( "encoding/base64" "encoding/json" "fmt" "github.com/gophercloud/gophercloud" "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"` // This requires the client to be set to microversion 2.26 or later. // Tags filters on specific server tags. All tags must be present for the server. Tags string `q:"tags"` // This requires the client to be set to microversion 2.26 or later. // TagsAny filters on specific server tags. At least one of the tags must be present for the server. TagsAny string `q:"tags-any"` // This requires the client to be set to microversion 2.26 or later. // NotTags filters on specific server tags. All tags must be absent for the server. NotTags string `q:"not-tags"` // This requires the client to be set to microversion 2.26 or later. // NotTagsAny filters on specific server tags. At least one of the tags must be absent for the server. NotTagsAny string `q:"not-tags-any"` } // 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 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"` // FlavorRef is the ID or full URL to the flavor that describes the server's specs. FlavorRef string `json:"flavorRef"` // 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. // Starting with microversion 2.37 networks can also be an "auto" or "none" // string. Networks interface{} `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 specifies an IPv6 address for the instance. AccessIPv6 string `json:"accessIPv6,omitempty"` // Min specifies Minimum number of servers to launch. Min int `json:"min_count,omitempty"` // Max specifies Maximum number of servers to launch. Max int `json:"max_count,omitempty"` // ServiceClient will allow calls to be made to retrieve an image or // flavor ID by name. ServiceClient *gophercloud.ServiceClient `json:"-"` // Tags allows a server to be tagged with single-word metadata. // Requires microversion 2.52 or later. Tags []string `json:"tags,omitempty"` } // ToServerCreateMap assembles a request body based on the contents of a // CreateOpts. func (opts CreateOpts) ToServerCreateMap() (map[string]interface{}, error) { 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 } switch v := opts.Networks.(type) { case []Network: if len(v) > 0 { networks := make([]map[string]interface{}, len(v)) for i, net := range v { 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 } case string: if v == "auto" || v == "none" { b["networks"] = v } else { return nil, fmt.Errorf(`networks must be a slice of Network struct or a string with "auto" or "none" values, current value is %q`, v) } } if opts.Min != 0 { b["min_count"] = opts.Min } if opts.Max != 0 { b["max_count"] = opts.Max } 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 } resp, err := client.Post(listURL(client), reqBody, &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete requests that a server previously provisioned be removed from your // account. func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { resp, err := client.Delete(deleteURL(client, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // ForceDelete forces the deletion of a server. func ForceDelete(client *gophercloud.ServiceClient, id string) (r ActionResult) { resp, err := client.Post(actionURL(client, id), map[string]interface{}{"forceDelete": ""}, nil, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get requests details on a single server, by ID. func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { resp, err := client.Get(getURL(client, id), &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 203}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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 } resp, err := client.Put(updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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, }, } resp, err := client.Post(actionURL(client, id), b, nil, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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 restored 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 } resp, err := client.Post(actionURL(client, id), b, nil, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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"` // ImageRef is the ID of the image you want your server to be provisioned on. ImageRef string `json:"imageRef"` // 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 } 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 } resp, err := client.Post(actionURL(client, id), b, &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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 } resp, err := client.Post(actionURL(client, id), b, nil, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // ConfirmResize confirms a previous resize operation on a server. // See Resize() for more details. func ConfirmResize(client *gophercloud.ServiceClient, id string) (r ActionResult) { resp, err := client.Post(actionURL(client, id), map[string]interface{}{"confirmResize": nil}, nil, &gophercloud.RequestOpts{ OkCodes: []int{201, 202, 204}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // RevertResize cancels a previous resize operation on a server. // See Resize() for more details. func RevertResize(client *gophercloud.ServiceClient, id string) (r ActionResult) { resp, err := client.Post(actionURL(client, id), map[string]interface{}{"revertResize": nil}, nil, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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 } resp, err := client.Put(metadataURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Metadata requests all the metadata for the given server ID. func Metadata(client *gophercloud.ServiceClient, id string) (r GetMetadataResult) { resp, err := client.Get(metadataURL(client, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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 } resp, err := client.Post(metadataURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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 } resp, err := client.Put(metadatumURL(client, id, key), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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) { resp, err := client.Get(metadatumURL(client, id, key), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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) { resp, err := client.Delete(metadatumURL(client, id, key), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // GetPassword makes a request against the nova API to get the encrypted // administrative password. func GetPassword(client *gophercloud.ServiceClient, serverId string) (r GetPasswordResult) { resp, err := client.Get(passwordURL(client, serverId), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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 } resp, err := client.Post(actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/servers/results.go000066400000000000000000000306741367513235700307040ustar00rootroot00000000000000package 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"` // AttachedVolumes includes the volume attachments of this instance AttachedVolumes []AttachedVolume `json:"os-extended-volumes:volumes_attached"` // Fault contains failure information about a server. Fault Fault `json:"fault"` // Tags is a slice/list of string tags in a server. // The requires microversion 2.26 or later. Tags *[]string `json:"tags"` } type AttachedVolume struct { ID string `json:"id"` } 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.12.0/openstack/compute/v2/servers/testing/000077500000000000000000000000001367513235700303175ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/servers/testing/doc.go000066400000000000000000000000461367513235700314130ustar00rootroot00000000000000// servers unit tests package testing golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/servers/testing/fixtures.go000066400000000000000000001106631367513235700325260ustar00rootroot00000000000000package 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": [ { "id": "2bdbc40f-a277-45d4-94ac-d9881c777d33" } ], "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" }` const ServerWithTagsCreateRequest = ` { "server": { "name": "derp", "imageRef": "f90f6034-2570-4974-8351-6b49732ef2eb", "flavorRef": "1", "tags": ["foo", "bar"] } }` // SingleServerWithTagsBody is the canned body of a Get request on an existing server with tags. const SingleServerWithTagsBody = ` { "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": {}, "tags": ["foo", "bar"] } } ` 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{}, AttachedVolumes: []servers.AttachedVolume{ { ID: "2bdbc40f-a277-45d4-94ac-d9881c777d33", }, }, 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{}, AttachedVolumes: []servers.AttachedVolume{}, 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{}, AttachedVolumes: []servers.AttachedVolume{}, 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") } // HandleServerNoNetworkCreationSuccessfully sets up the test server with no // network to respond to a server creation request with a given response. func HandleServerNoNetworkCreationSuccessfully(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", "networks": "none" } }`) w.WriteHeader(http.StatusAccepted) w.Header().Add("Content-Type", "application/json") fmt.Fprintf(w, response) }) } // 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 HandleServersCreationSuccessfully(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", "min_count": 3, "max_count": 3 } }`) w.WriteHeader(http.StatusAccepted) w.Header().Add("Content-Type", "application/json") fmt.Fprintf(w, response) }) } // 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": "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) }) } // HandleServerWithTagsCreationSuccessfully sets up the test server to respond // to a server creation request with a given response. func HandleServerWithTagsCreationSuccessfully(t *testing.T) { 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, ServerWithTagsCreateRequest) w.WriteHeader(http.StatusAccepted) w.Header().Add("Content-Type", "application/json") fmt.Fprintf(w, SingleServerWithTagsBody) }) } golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/servers/testing/requests_test.go000066400000000000000000000363451367513235700335730ustar00rootroot00000000000000package 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 TestCreateServerNoNetwork(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleServerNoNetworkCreationSuccessfully(t, SingleServerBody) actual, err := servers.Create(client.ServiceClient(), servers.CreateOpts{ Name: "derp", ImageRef: "f90f6034-2570-4974-8351-6b49732ef2eb", FlavorRef: "1", Networks: "none", }).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, ServerDerp, *actual) } func TestCreateServers(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleServersCreationSuccessfully(t, SingleServerBody) actual, err := servers.Create(client.ServiceClient(), servers.CreateOpts{ Name: "derp", ImageRef: "f90f6034-2570-4974-8351-6b49732ef2eb", FlavorRef: "1", Min: 3, Max: 3, }).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 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", ImageRef: "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") } } func TestCreateServerWithTags(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleServerWithTagsCreationSuccessfully(t) c := client.ServiceClient() c.Microversion = "2.52" tags := []string{"foo", "bar"} ServerDerpTags := ServerDerp ServerDerpTags.Tags = &tags createOpts := servers.CreateOpts{ Name: "derp", ImageRef: "f90f6034-2570-4974-8351-6b49732ef2eb", FlavorRef: "1", Tags: tags, } res := servers.Create(c, createOpts) th.AssertNoErr(t, res.Err) actualServer, err := res.Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, ServerDerpTags, *actualServer) } golang-github-gophercloud-gophercloud-0.12.0/openstack/compute/v2/servers/testing/results_test.go000066400000000000000000000115251367513235700334120ustar00rootroot00000000000000package 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.AssertNoErr(t, err) 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.12.0/openstack/compute/v2/servers/urls.go000066400000000000000000000027201367513235700301570ustar00rootroot00000000000000package 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.12.0/openstack/compute/v2/servers/util.go000066400000000000000000000010371367513235700301470ustar00rootroot00000000000000package 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.12.0/openstack/container/000077500000000000000000000000001367513235700251305ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/container/v1/000077500000000000000000000000001367513235700254565ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/container/v1/capsules/000077500000000000000000000000001367513235700272755ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/container/v1/capsules/doc.go000066400000000000000000000003271367513235700303730ustar00rootroot00000000000000// 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 golang-github-gophercloud-gophercloud-0.12.0/openstack/container/v1/capsules/errors.go000066400000000000000000000003671367513235700311460ustar00rootroot00000000000000package 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.") } golang-github-gophercloud-gophercloud-0.12.0/openstack/container/v1/capsules/microversions.go000066400000000000000000000047041367513235700325330ustar00rootroot00000000000000package capsules import ( "encoding/json" "time" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // ExtractV132 is a function that accepts a result and extracts a capsule resource. func (r commonResult) ExtractV132() (*CapsuleV132, error) { var s *CapsuleV132 err := r.ExtractInto(&s) return s, err } // Represents a Capsule at microversion v1.32 or greater. type CapsuleV132 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:"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 restart policy RestartPolicy map[string]string `json:"restart_policy"` // The capsule metadata labels MetaLabels map[string]string `json:"labels"` // The capsule IP addresses Addresses map[string][]Address `json:"addresses"` // The container object inside capsule Containers []Container `json:"containers"` // The capsule host Host string `json:"host"` } // ExtractCapsulesV132 accepts a Page struct, specifically a CapsulePage struct, // and extracts the elements into a slice of CapsuleV132 structs. In other words, // a generic collection is mapped into a relevant slice. func ExtractCapsulesV132(r pagination.Page) ([]CapsuleV132, error) { var s struct { Capsules []CapsuleV132 `json:"capsules"` } err := (r.(CapsulePage)).ExtractInto(&s) return s.Capsules, err } func (r *CapsuleV132) UnmarshalJSON(b []byte) error { type tmp CapsuleV132 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 = CapsuleV132(s.tmp) r.CreatedAt = time.Time(s.CreatedAt) r.UpdatedAt = time.Time(s.UpdatedAt) return nil } golang-github-gophercloud-gophercloud-0.12.0/openstack/container/v1/capsules/requests.go000066400000000000000000000065731367513235700315120ustar00rootroot00000000000000package 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) { resp, err := client.Get(getURL(client, id), &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 203}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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 } resp, err := client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{OkCodes: []int{202}}) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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) { resp, err := client.Delete(deleteURL(client, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } golang-github-gophercloud-gophercloud-0.12.0/openstack/container/v1/capsules/results.go000066400000000000000000000213061367513235700313270ustar00rootroot00000000000000package capsules import ( "encoding/json" "fmt" "time" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) type commonResult struct { gophercloud.Result } // ExtractBase is a function that accepts a result and extracts // a base a capsule resource. func (r commonResult) ExtractBase() (*Capsule, error) { var s *Capsule err := r.ExtractInto(&s) return s, err } // Extract is a function that accepts a result and extracts a capsule result. // The result will be returned as an interface{} where it should be able to // be casted as either a Capsule or CapsuleV132. func (r commonResult) Extract() (interface{}, error) { s, err := r.ExtractBase() if err == nil { return s, nil } if _, ok := err.(*json.UnmarshalTypeError); !ok { return s, err } return r.ExtractV132() } // 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) if err != nil { return false, err } if v, ok := is.([]Capsule); ok { return len(v) == 0, nil } if v, ok := is.([]CapsuleV132); ok { return len(v) == 0, nil } return false, fmt.Errorf("Unable to determine Capsule type") } // ExtractCapsulesBase 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 the relevant slice. func ExtractCapsulesBase(r pagination.Page) ([]Capsule, error) { var s struct { Capsules []Capsule `json:"capsules"` } err := (r.(CapsulePage)).ExtractInto(&s) return s.Capsules, err } // ExtractCapsules accepts a Page struct, specifically a CapsulePage struct, // and extracts the elements into an interface. // This interface should be able to be casted as either a Capsule or // CapsuleV132 struct func ExtractCapsules(r pagination.Page) (interface{}, error) { s, err := ExtractCapsulesBase(r) if err == nil { return s, nil } if _, ok := err.(*json.UnmarshalTypeError); !ok { return nil, err } return ExtractCapsulesV132(r) } 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 } golang-github-gophercloud-gophercloud-0.12.0/openstack/container/v1/capsules/template.go000066400000000000000000000013511367513235700314370ustar00rootroot00000000000000package capsules import ( "encoding/json" yaml "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 } golang-github-gophercloud-gophercloud-0.12.0/openstack/container/v1/capsules/testing/000077500000000000000000000000001367513235700307525ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/container/v1/capsules/testing/doc.go000066400000000000000000000000201367513235700320360ustar00rootroot00000000000000package testing golang-github-gophercloud-gophercloud-0.12.0/openstack/container/v1/capsules/testing/fixtures.go000066400000000000000000000436151367513235700331630ustar00rootroot00000000000000package 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" } ] }` const CapsuleV132ListBody = ` { "capsules": [ { "uuid": "cc654059-1a77-47a3-bfcf-715bde5aad9e", "status": "Running", "user_id": "d33b18c384574fd2a3299447aac285f0", "project_id": "6b8ffef2a0ac42ee87887b9cc98bdf68", "cpu": 1, "memory": "1024M", "name": "test", "labels": {"web": "app"}, "created_at": "2018-01-12 09:37:25", "updated_at": "2018-01-12 09:37:25", "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" } ], "restart_policy": { "MaximumRetryCount": "0", "Name": "always" }, "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" } ] }, "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, }, } var ExpectedCapsuleV132 = capsules.CapsuleV132{ 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", }, }, RestartPolicy: map[string]string{ "MaximumRetryCount": "0", "Name": "always", }, MetaLabels: map[string]string{ "web": "app", }, 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", }, }, }, 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) }) } // HandleCapsuleV132ListSuccessfully test setup func HandleCapsuleV132ListSuccessfully(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, CapsuleV132ListBody) }) } 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.go000066400000000000000000000103261367513235700341360ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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") ec := ExpectedCapsule ec.CreatedAt = createdAt ec.UpdatedAt = updatedAt ec.Containers = nil expected := []capsules.Capsule{ec} 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 TestListCapsuleV132(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleCapsuleV132ListSuccessfully(t) createdAt, _ := time.Parse(gophercloud.RFC3339ZNoTNoZ, "2018-01-12 09:37:25") updatedAt, _ := time.Parse(gophercloud.RFC3339ZNoTNoZ, "2018-01-12 09:37:25") ec := ExpectedCapsuleV132 ec.CreatedAt = createdAt ec.UpdatedAt = updatedAt ec.Containers = nil expected := []capsules.CapsuleV132{ec} 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.go000066400000000000000000000014771367513235700341050ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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") } } golang-github-gophercloud-gophercloud-0.12.0/openstack/container/v1/capsules/urls.go000066400000000000000000000011361367513235700306120ustar00rootroot00000000000000package 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.12.0/openstack/containerinfra/000077500000000000000000000000001367513235700261505ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/containerinfra/apiversions/000077500000000000000000000000001367513235700305125ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/containerinfra/apiversions/doc.go000066400000000000000000000002471367513235700316110ustar00rootroot00000000000000// Package apiversions provides information and interaction with the different // API versions for the Container Infra service, code-named Magnum. package apiversions golang-github-gophercloud-gophercloud-0.12.0/openstack/containerinfra/apiversions/errors.go000066400000000000000000000010271367513235700323550ustar00rootroot00000000000000package 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) } golang-github-gophercloud-gophercloud-0.12.0/openstack/containerinfra/apiversions/requests.go000066400000000000000000000012121367513235700327100ustar00rootroot00000000000000package 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) { resp, err := client.Get(getURL(client, v), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } golang-github-gophercloud-gophercloud-0.12.0/openstack/containerinfra/apiversions/results.go000066400000000000000000000034011367513235700325400ustar00rootroot00000000000000package apiversions import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // APIVersion represents an API version for the Container Infra 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"` // Version is the maximum microversion supported. Version string `json:"max_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)} } } golang-github-gophercloud-gophercloud-0.12.0/openstack/containerinfra/apiversions/testing/000077500000000000000000000000001367513235700321675ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/containerinfra/apiversions/testing/doc.go000066400000000000000000000000421367513235700332570ustar00rootroot00000000000000// apiversions_v1 package testing fixtures.go000066400000000000000000000040501367513235700343070ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/containerinfra/apiversions/testingpackage testing import ( "fmt" "net/http" "testing" "github.com/gophercloud/gophercloud/openstack/containerinfra/apiversions" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) const MagnumAPIVersionResponse = ` { "versions":[ { "status":"CURRENT", "min_version":"1.1", "max_version":"1.7", "id":"v1", "links":[ { "href":"http://10.164.180.104:9511/v1/", "rel":"self" } ] } ], "name":"OpenStack Magnum API", "description":"Magnum is an OpenStack project which aims to provide container management." } ` const MagnumAllAPIVersionsResponse = ` { "versions":[ { "status":"CURRENT", "min_version":"1.1", "max_version":"1.7", "id":"v1", "links":[ { "href":"http://10.164.180.104:9511/v1/", "rel":"self" } ] } ], "name":"OpenStack Magnum API", "description":"Magnum is an OpenStack project which aims to provide container management." } ` var MagnumAPIVersion1Result = apiversions.APIVersion{ ID: "v1", Status: "CURRENT", MinVersion: "1.1", Version: "1.7", } var MagnumAllAPIVersionResults = []apiversions.APIVersion{ MagnumAPIVersion1Result, } 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, MagnumAllAPIVersionsResponse) }) } 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, MagnumAPIVersionResponse) }) } requests_test.go000066400000000000000000000015351367513235700353550ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/containerinfra/apiversions/testingpackage testing import ( "fmt" "testing" "github.com/gophercloud/gophercloud/openstack/containerinfra/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) fmt.Println(actual) th.AssertDeepEquals(t, MagnumAllAPIVersionResults, actual) } func TestGetAPIVersion(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() MockGetResponse(t) actual, err := apiversions.Get(client.ServiceClient(), "v1").Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, MagnumAPIVersion1Result, *actual) } golang-github-gophercloud-gophercloud-0.12.0/openstack/containerinfra/apiversions/urls.go000066400000000000000000000010451367513235700320260ustar00rootroot00000000000000package apiversions import ( "strings" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack/utils" ) func getURL(c *gophercloud.ServiceClient, version string) string { baseEndpoint, _ := utils.BaseEndpoint(c.Endpoint) endpoint := strings.TrimRight(baseEndpoint, "/") + "/" + strings.TrimRight(version, "/") + "/" return endpoint } func listURL(c *gophercloud.ServiceClient) string { baseEndpoint, _ := utils.BaseEndpoint(c.Endpoint) endpoint := strings.TrimRight(baseEndpoint, "/") + "/" return endpoint } golang-github-gophercloud-gophercloud-0.12.0/openstack/containerinfra/v1/000077500000000000000000000000001367513235700264765ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/containerinfra/v1/certificates/000077500000000000000000000000001367513235700311435ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/containerinfra/v1/certificates/doc.go000066400000000000000000000016271367513235700322450ustar00rootroot00000000000000// 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 createOpts := certificates.CreateOpts{ BayUUID: "d564b18a-2890-4152-be3d-e05d784ff727", CSR: "-----BEGIN CERTIFICATE REQUEST-----\nMIIEfzCCAmcCAQAwFDESMBAGA1UEAxMJWW91ciBOYW1lMIICIjANBgkqhkiG9w0B\n-----END CERTIFICATE REQUEST-----\n", } response, err := certificates.Create(sc, createOpts).Extract() if err != nil { panic(err) } Example to update certificates err := certificates.Update(client, clusterUUID).ExtractErr() if err != nil { panic(err) } */ package certificates golang-github-gophercloud-gophercloud-0.12.0/openstack/containerinfra/v1/certificates/requests.go000066400000000000000000000034361367513235700333530ustar00rootroot00000000000000package certificates import ( "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) resp, err := client.Get(url, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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 } resp, err := client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{201}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Update will rotate the CA certificate for a cluster func Update(client *gophercloud.ServiceClient, clusterID string) (r UpdateResult) { resp, err := client.Patch(updateURL(client, clusterID), nil, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } golang-github-gophercloud-gophercloud-0.12.0/openstack/containerinfra/v1/certificates/results.go000066400000000000000000000016341367513235700331770ustar00rootroot00000000000000package 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"` } golang-github-gophercloud-gophercloud-0.12.0/openstack/containerinfra/v1/certificates/testing/000077500000000000000000000000001367513235700326205ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/containerinfra/v1/certificates/testing/doc.go000066400000000000000000000000201367513235700337040ustar00rootroot00000000000000package testing fixtures.go000066400000000000000000000073571367513235700347550ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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.go000066400000000000000000000025101367513235700360000ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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) } golang-github-gophercloud-gophercloud-0.12.0/openstack/containerinfra/v1/certificates/urls.go000066400000000000000000000007731367513235700324660ustar00rootroot00000000000000package 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.12.0/openstack/containerinfra/v1/clusters/000077500000000000000000000000001367513235700303425ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/containerinfra/v1/clusters/doc.go000066400000000000000000000043201367513235700314350ustar00rootroot00000000000000/* 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 List Clusters with detailed information allPagesDetail, err := clusters.ListDetail(serviceClient, clusters.ListOpts{}).AllPages() if err != nil { panic(err) } allClustersDetail, err := clusters.ExtractClusters(allPagesDetail) if err != nil { panic(err) } for _, clusterDetail := range allClustersDetail { fmt.Printf("%+v\n", clusterDetail) } 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 golang-github-gophercloud-gophercloud-0.12.0/openstack/containerinfra/v1/clusters/requests.go000066400000000000000000000147551367513235700325600ustar00rootroot00000000000000package clusters import ( "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"` FloatingIPEnabled *bool `json:"floating_ip_enabled,omitempty"` FixedNetwork string `json:"fixed_network,omitempty"` FixedSubnet string `json:"fixed_subnet,omitempty"` MergeLabels *bool `json:"merge_labels,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 } resp, err := client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get retrieves a specific clusters based on its unique ID. func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { resp, err := client.Get(getURL(client, id), &r.Body, &gophercloud.RequestOpts{OkCodes: []int{200}}) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete deletes the specified cluster ID. func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { resp, err := client.Delete(deleteURL(client, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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}} }) } // ListDetail returns a Pager which allows you to iterate over a collection of // clusters with detailed information. // It accepts a ListOptsBuilder, which allows you to sort the returned // collection for greater efficiency. func ListDetail(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { url := listDetailURL(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 interface{} `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) } resp, err := client.Patch(updateURL(client, id), o, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // ResizeOptsBuilder allows extensions to add additional parameters to the // Resize request. type ResizeOptsBuilder interface { ToClusterResizeMap() (map[string]interface{}, error) } // ResizeOpts params type ResizeOpts struct { NodeCount *int `json:"node_count" required:"true"` NodesToRemove []string `json:"nodes_to_remove,omitempty"` NodeGroup string `json:"nodegroup,omitempty"` } // ToClusterResizeMap constructs a request body from ResizeOpts. func (opts ResizeOpts) ToClusterResizeMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "") } // Resize an existing cluster node count. func Resize(client *gophercloud.ServiceClient, id string, opts ResizeOptsBuilder) (r ResizeResult) { b, err := opts.ToClusterResizeMap() if err != nil { r.Err = err return } resp, err := client.Post(resizeURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } golang-github-gophercloud-gophercloud-0.12.0/openstack/containerinfra/v1/clusters/results.go000066400000000000000000000076011367513235700323760ustar00rootroot00000000000000package 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 } // ResizeResult is the response of a Resize operations. type ResizeResult 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 } func (r ResizeResult) 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"` FloatingIPEnabled bool `json:"floating_ip_enabled"` FixedNetwork string `json:"fixed_network"` FixedSubnet string `json:"fixed_subnet"` HealthStatus string `json:"health_status"` HealthStatusReason map[string]interface{} `json:"health_status_reason"` } 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 } golang-github-gophercloud-gophercloud-0.12.0/openstack/containerinfra/v1/clusters/testing/000077500000000000000000000000001367513235700320175ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/containerinfra/v1/clusters/testing/doc.go000066400000000000000000000000201367513235700331030ustar00rootroot00000000000000package testing fixtures.go000066400000000000000000000225641367513235700341510ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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, FloatingIPEnabled: true, FixedNetwork: "private_network", FixedSubnet: "private_subnet", HealthStatus: "HEALTHY", HealthStatusReason: map[string]interface{}{"api": "ok"}, } 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, FloatingIPEnabled: true, FixedNetwork: "private_network", FixedSubnet: "private_subnet", HealthStatus: "HEALTHY", HealthStatusReason: map[string]interface{}{"api": "ok"}, } 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", "floating_ip_enabled": true, "fixed_network": "private_network", "fixed_subnet": "private_subnet", "health_status": "HEALTHY", "health_status_reason": {"api": "ok"} }`, 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", "floating_ip_enabled": true, "fixed_network": "private_network", "fixed_subnet": "private_subnet", "health_status": "HEALTHY", "health_status_reason": {"api": "ok"} }, { "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", "floating_ip_enabled": true, "fixed_network": "private_network", "fixed_subnet": "private_subnet", "health_status": "HEALTHY", "health_status_reason": {"api": "ok"} } ] }`, 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) }) } func HandleListDetailClusterSuccessfully(t *testing.T) { th.Mux.HandleFunc("/v1/clusters/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.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) }) } var ResizeResponse = fmt.Sprintf(` { "uuid": "%s" }`, clusterUUID) func HandleResizeClusterSuccessfully(t *testing.T) { th.Mux.HandleFunc("/v1/clusters/"+clusterUUID+"/actions/resize", 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, ResizeResponse) }) } requests_test.go000066400000000000000000000115571367513235700352120ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/containerinfra/v1/clusters/testingpackage testing import ( "testing" "github.com/gophercloud/gophercloud" "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, FloatingIPEnabled: gophercloud.Enabled, FixedNetwork: "private_network", FixedSubnet: "private_subnet", MergeLabels: gophercloud.Enabled, } 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 TestListDetailClusters(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleListDetailClusterSuccessfully(t) count := 0 sc := fake.ServiceClient() sc.Endpoint = sc.Endpoint + "v1/" clusters.ListDetail(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) } func TestResizeCluster(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleResizeClusterSuccessfully(t) nodeCount := 2 var opts clusters.ResizeOptsBuilder opts = clusters.ResizeOpts{ NodeCount: &nodeCount, } sc := fake.ServiceClient() sc.Endpoint = sc.Endpoint + "v1/" res := clusters.Resize(sc, clusterUUID, 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) } golang-github-gophercloud-gophercloud-0.12.0/openstack/containerinfra/v1/clusters/urls.go000066400000000000000000000020071367513235700316550ustar00rootroot00000000000000package 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 listDetailURL(client *gophercloud.ServiceClient) string { return client.ServiceURL("clusters", "detail") } func updateURL(client *gophercloud.ServiceClient, id string) string { return idURL(client, id) } func resizeURL(client *gophercloud.ServiceClient, id string) string { return client.ServiceURL("clusters", id, "actions/resize") } golang-github-gophercloud-gophercloud-0.12.0/openstack/containerinfra/v1/clustertemplates/000077500000000000000000000000001367513235700320765ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/containerinfra/v1/clustertemplates/doc.go000066400000000000000000000045741367513235700332040ustar00rootroot00000000000000// 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.go000066400000000000000000000135601367513235700342260ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/containerinfra/v1/clustertemplatespackage clustertemplates import ( "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 } resp, err := client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{201}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete deletes the specified cluster ID. func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { resp, err := client.Delete(deleteURL(client, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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) { resp, err := client.Get(getURL(client, id), &r.Body, &gophercloud.RequestOpts{OkCodes: []int{200}}) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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 interface{} `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) } resp, err := client.Patch(updateURL(client, id), o, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } golang-github-gophercloud-gophercloud-0.12.0/openstack/containerinfra/v1/clustertemplates/results.go000066400000000000000000000102331367513235700341250ustar00rootroot00000000000000package 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 } golang-github-gophercloud-gophercloud-0.12.0/openstack/containerinfra/v1/clustertemplates/testing/000077500000000000000000000000001367513235700335535ustar00rootroot00000000000000doc.go000066400000000000000000000000201367513235700345600ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/containerinfra/v1/clustertemplates/testingpackage testing fixtures.go000066400000000000000000000404221367513235700356760ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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.go000066400000000000000000000135501367513235700367410ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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) } golang-github-gophercloud-gophercloud-0.12.0/openstack/containerinfra/v1/clustertemplates/urls.go000066400000000000000000000014161367513235700334140ustar00rootroot00000000000000package 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.12.0/openstack/containerinfra/v1/nodegroups/000077500000000000000000000000001367513235700306635ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/containerinfra/v1/nodegroups/doc.go000066400000000000000000000053321367513235700317620ustar00rootroot00000000000000/* Package nodegroups provides methods for interacting with the Magnum node group API. All node group actions must be performed on a specific cluster, so the cluster UUID/name is required as a parameter in each method. Create a client to use: opts, err := openstack.AuthOptionsFromEnv() if err != nil { panic(err) } provider, err := openstack.AuthenticatedClient(opts) if err != nil { panic(err) } client, err := openstack.NewContainerInfraV1(provider, gophercloud.EndpointOpts{Region: os.Getenv("OS_REGION_NAME")}) if err != nil { panic(err) } client.Microversion = "1.9" Example of Getting a node group: ng, err := nodegroups.Get(client, clusterUUID, nodeGroupUUID).Extract() if err != nil { panic(err) } fmt.Printf("%#v\n", ng) Example of Listing node groups: listOpts := nodegroup.ListOpts{ Role: "worker", } allPages, err := nodegroups.List(client, clusterUUID, listOpts).AllPages() if err != nil { panic(err) } ngs, err := nodegroups.ExtractNodeGroups(allPages) if err != nil { panic(err) } for _, ng := range ngs { fmt.Printf("%#v\n", ng) } Example of Creating a node group: // Labels, node image and node flavor will be inherited from the cluster value if not set. // Role will default to "worker" if not set. // To add a label to the new node group, need to know the cluster labels cluster, err := clusters.Get(client, clusterUUID).Extract() if err != nil { panic(err) } // Add the new label labels := cluster.Labels labels["availability_zone"] = "A" maxNodes := 5 createOpts := nodegroups.CreateOpts{ Name: "new-nodegroup", MinNodeCount: 2, MaxNodeCount: &maxNodes, Labels: labels, } ng, err := nodegroups.Create(client, clusterUUID, createOpts).Extract() if err != nil { panic(err) } fmt.Printf("%#v\n", ng) Example of Updating a node group: // Valid paths are "/min_node_count" and "/max_node_count". // Max node count can be unset with the "remove" op to have // no enforced maximum node count. updateOpts := []nodegroups.UpdateOptsBuilder{ nodegroups.UpdateOpts{ Op: nodegroups.ReplaceOp, Path: "/max_node_count", Value: 10, }, } ng, err = nodegroups.Update(client, clusterUUID, nodeGroupUUID, updateOpts).Extract() if err != nil { panic(err) } fmt.Printf("%#v\n", ng) Example of Deleting a node group: err = nodegroups.Delete(client, clusterUUID, nodeGroupUUID).ExtractErr() if err != nil { panic(err) } */ package nodegroups golang-github-gophercloud-gophercloud-0.12.0/openstack/containerinfra/v1/nodegroups/requests.go000066400000000000000000000135561367513235700330770ustar00rootroot00000000000000package nodegroups import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // Get makes a request to the Magnum API to retrieve a node group // with the given ID/name belonging to the given cluster. // Use the Extract method of the returned GetResult to extract the // node group from the result. func Get(client *gophercloud.ServiceClient, clusterID, nodeGroupID string) (r GetResult) { resp, err := client.Get(getURL(client, clusterID, nodeGroupID), &r.Body, &gophercloud.RequestOpts{OkCodes: []int{200}}) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } type ListOptsBuilder interface { ToNodeGroupsListQuery() (string, error) } // ListOpts is used to filter and sort the node groups of a cluster // when using List. type ListOpts struct { // Pagination marker for large data sets. (UUID field from node group). Marker int `q:"marker"` // Maximum number of resources to return in a single page. Limit int `q:"limit"` // Column to sort results by. Default: id. SortKey string `q:"sort_key"` // Direction to sort. "asc" or "desc". Default: asc. SortDir string `q:"sort_dir"` // List all nodegroups with the specified role. Role string `q:"role"` } func (opts ListOpts) ToNodeGroupsListQuery() (string, error) { q, err := gophercloud.BuildQueryString(opts) return q.String(), err } // List makes a request to the Magnum API to retrieve node groups // belonging to the given cluster. The request can be modified to // filter or sort the list using the options available in ListOpts. // // Use the AllPages method of the returned Pager to ensure that // all node groups are returned (for example when using the Limit // option to limit the number of node groups returned per page). // // Not all node group fields are returned in a list request. // Only the fields UUID, Name, FlavorID, ImageID, // NodeCount, Role, IsDefault, Status and StackID // are returned, all other fields are omitted // and will have their zero value when extracted. func List(client *gophercloud.ServiceClient, clusterID string, opts ListOptsBuilder) pagination.Pager { url := listURL(client, clusterID) if opts != nil { query, err := opts.ToNodeGroupsListQuery() if err != nil { return pagination.Pager{Err: err} } url += query } return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { return NodeGroupPage{pagination.LinkedPageBase{PageResult: r}} }) } type CreateOptsBuilder interface { ToNodeGroupCreateMap() (map[string]interface{}, error) } // CreateOpts is used to set available fields upon node group creation. // // If unset, some fields have defaults or will inherit from the cluster value. type CreateOpts struct { Name string `json:"name" required:"true"` DockerVolumeSize *int `json:"docker_volume_size,omitempty"` // Labels will default to the cluster labels if unset. Labels map[string]string `json:"labels,omitempty"` NodeCount *int `json:"node_count,omitempty"` MinNodeCount int `json:"min_node_count,omitempty"` // MaxNodeCount can be left unset for no maximum node count. MaxNodeCount *int `json:"max_node_count,omitempty"` // Role defaults to "worker" if unset. Role string `json:"role,omitempty"` // Node image ID. Defaults to cluster template image if unset. ImageID string `json:"image_id,omitempty"` // Node machine flavor ID. Defaults to cluster minion flavor if unset. FlavorID string `json:"flavor_id,omitempty"` } func (opts CreateOpts) ToNodeGroupCreateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "") } // Create makes a request to the Magnum API to create a node group // for the the given cluster. // Use the Extract method of the returned CreateResult to extract the // returned node group. func Create(client *gophercloud.ServiceClient, clusterID string, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToNodeGroupCreateMap() if err != nil { r.Err = err return } resp, err := client.Post(createURL(client, clusterID), b, &r.Body, &gophercloud.RequestOpts{OkCodes: []int{202}}) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } type UpdateOptsBuilder interface { ToResourceUpdateMap() (map[string]interface{}, error) } type UpdateOp string const ( AddOp UpdateOp = "add" RemoveOp UpdateOp = "remove" ReplaceOp UpdateOp = "replace" ) // UpdateOpts is used to define the action taken when updating a node group. // // Valid Ops are "add", "remove", "replace" // Valid Paths are "/min_node_count" and "/max_node_count" type UpdateOpts struct { Op UpdateOp `json:"op" required:"true"` Path string `json:"path" required:"true"` Value interface{} `json:"value,omitempty"` } func (opts UpdateOpts) ToResourceUpdateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "") } // Update makes a request to the Magnum API to update a field of // the given node group belonging to the given cluster. More than // one UpdateOpts can be passed at a time. // Use the Extract method of the returned UpdateResult to extract the // updated node group from the result. func Update(client *gophercloud.ServiceClient, clusterID string, nodeGroupID string, opts []UpdateOptsBuilder) (r UpdateResult) { var o []map[string]interface{} for _, opt := range opts { b, err := opt.ToResourceUpdateMap() if err != nil { r.Err = err return } o = append(o, b) } resp, err := client.Patch(updateURL(client, clusterID, nodeGroupID), o, &r.Body, &gophercloud.RequestOpts{OkCodes: []int{202}}) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete makes a request to the Magnum API to delete a node group. func Delete(client *gophercloud.ServiceClient, clusterID, nodeGroupID string) (r DeleteResult) { resp, err := client.Delete(deleteURL(client, clusterID, nodeGroupID), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } golang-github-gophercloud-gophercloud-0.12.0/openstack/containerinfra/v1/nodegroups/results.go000066400000000000000000000056071367513235700327230ustar00rootroot00000000000000package nodegroups import ( "time" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) type commonResult struct { gophercloud.Result } func (r commonResult) Extract() (*NodeGroup, error) { var s NodeGroup err := r.ExtractInto(&s) return &s, err } // GetResult is the response from a Get request. // Use the Extract method to retrieve the NodeGroup itself. type GetResult struct { commonResult } // CreateResult is the response from a Create request. // Use the Extract method to retrieve the created node group. type CreateResult struct { commonResult } // UpdateResult is the response from an Update request. // Use the Extract method to retrieve the updated node group. type UpdateResult struct { commonResult } // DeleteResult is the response from a Delete request. // Use the ExtractErr method to extract the error from the result. type DeleteResult struct { gophercloud.ErrResult } // NodeGroup is the API representation of a Magnum node group. type NodeGroup struct { ID int `json:"id"` UUID string `json:"uuid"` Name string `json:"name"` ClusterID string `json:"cluster_id"` ProjectID string `json:"project_id"` DockerVolumeSize *int `json:"docker_volume_size"` Labels map[string]string `json:"labels"` Links []gophercloud.Link `json:"links"` FlavorID string `json:"flavor_id"` ImageID string `json:"image_id"` NodeAddresses []string `json:"node_addresses"` NodeCount int `json:"node_count"` Role string `json:"role"` MinNodeCount int `json:"min_node_count"` MaxNodeCount *int `json:"max_node_count"` IsDefault bool `json:"is_default"` StackID string `json:"stack_id"` Status string `json:"status"` StatusReason string `json:"status_reason"` Version string `json:"version"` CreatedAt time.Time `json:"created_at"` UpdatedAt time.Time `json:"updated_at"` } type NodeGroupPage struct { pagination.LinkedPageBase } func (r NodeGroupPage) NextPageURL() (string, error) { var s struct { Next string `json:"next"` } err := r.ExtractInto(&s) if err != nil { return "", err } return s.Next, nil } func (r NodeGroupPage) IsEmpty() (bool, error) { s, err := ExtractNodeGroups(r) return len(s) == 0, err } // ExtractNodeGroups takes a Page of node groups as returned from List // or from AllPages and extracts it as a slice of NodeGroups. func ExtractNodeGroups(r pagination.Page) ([]NodeGroup, error) { var s struct { NodeGroups []NodeGroup `json:"nodegroups"` } err := (r.(NodeGroupPage)).ExtractInto(&s) return s.NodeGroups, err } golang-github-gophercloud-gophercloud-0.12.0/openstack/containerinfra/v1/nodegroups/testing/000077500000000000000000000000001367513235700323405ustar00rootroot00000000000000fixtures.go000066400000000000000000000554641367513235700344770ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/containerinfra/v1/nodegroups/testingpackage testing import ( "fmt" "net/http" "testing" "time" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack/containerinfra/v1/nodegroups" th "github.com/gophercloud/gophercloud/testhelper" fake "github.com/gophercloud/gophercloud/testhelper/client" ) const ( clusterUUID = "bda75056-3a57-4ada-b943-658ac27beea0" badClusterUUID = "252e2f37-d83e-4848-be39-eed1b41211ac" nodeGroup1UUID = "b2e581be-2eec-45b8-921a-c85fbc23aaa3" nodeGroup2UUID = "2457febf-520f-4be3-abb9-96b892d7b5a0" badNodeGroupUUID = "4973f3aa-40a2-4857-bf9e-c15faffb08c8" ) var ( nodeGroup1Created, _ = time.Parse(time.RFC3339, "2019-10-18T14:03:37+00:00") nodeGroup1Updated, _ = time.Parse(time.RFC3339, "2019-10-18T14:18:35+00:00") nodeGroup2Created, _ = time.Parse(time.RFC3339, "2019-10-18T14:03:37+00:00") nodeGroup2Updated, _ = time.Parse(time.RFC3339, "2019-10-18T14:18:36+00:00") ) var expectedNodeGroup1 = nodegroups.NodeGroup{ ID: 9, UUID: nodeGroup1UUID, Name: "default-master", ClusterID: clusterUUID, ProjectID: "e91d02d561374de6b49960a27b3f08d0", DockerVolumeSize: nil, Labels: map[string]string{ "kube_tag": "v1.14.7", }, Links: []gophercloud.Link{ { Href: "http://123.456.789.0:9511/v1/clusters/bda75056-3a57-4ada-b943-658ac27beea0/nodegroups/b2e581be-2eec-45b8-921a-c85fbc23aaa3", Rel: "self", }, { Href: "http://123.456.789.0:9511/clusters/bda75056-3a57-4ada-b943-658ac27beea0/nodegroups/b2e581be-2eec-45b8-921a-c85fbc23aaa3", Rel: "bookmark", }, }, FlavorID: "", ImageID: "Fedora-AtomicHost-29-20190820.0.x86_64", NodeAddresses: []string{"172.24.4.19"}, NodeCount: 1, Role: "master", MinNodeCount: 1, MaxNodeCount: nil, IsDefault: true, StackID: "3cd55bb0-1115-4838-8eca-cefc13f7a21b", Status: "UPDATE_COMPLETE", StatusReason: "Stack UPDATE completed successfully", Version: "", CreatedAt: nodeGroup1Created, UpdatedAt: nodeGroup1Updated, } var expectedCreatedNodeGroup = nodegroups.NodeGroup{ UUID: "12542dd8-9588-42a7-a2ff-06f49049920c", Name: "test-ng", ClusterID: clusterUUID, ProjectID: "e91d02d561374de6b49960a27b3f08d0", Labels: map[string]string{ "kube_tag": "v1.14.7", }, Links: []gophercloud.Link{ { Href: "http://123.456.789.0:9511/v1/clusters/bda75056-3a57-4ada-b943-658ac27beea0/nodegroups/12542dd8-9588-42a7-a2ff-06f49049920c", Rel: "self", }, { Href: "http://123.456.789.0:9511/clusters/bda75056-3a57-4ada-b943-658ac27beea0/nodegroups/12542dd8-9588-42a7-a2ff-06f49049920c", Rel: "bookmark", }, }, FlavorID: "m1.small", ImageID: "Fedora-AtomicHost-29-20190820.0.x86_64", NodeCount: 1, MinNodeCount: 1, Role: "worker", } var maxNodesThree = 3 var expectedUpdatedNodeGroup = nodegroups.NodeGroup{ ID: 10, UUID: nodeGroup2UUID, Name: "default-worker", ClusterID: clusterUUID, ProjectID: "e91d02d561374de6b49960a27b3f08d0", Labels: map[string]string{ "kube_tag": "v1.14.7", }, Links: []gophercloud.Link{ { Href: "http://123.456.789.0:9511/v1/clusters/bda75056-3a57-4ada-b943-658ac27beea0/nodegroups/2457febf-520f-4be3-abb9-96b892d7b5a0", Rel: "self", }, { Href: "http://123.456.789.0:9511/clusters/bda75056-3a57-4ada-b943-658ac27beea0/nodegroups/2457febf-520f-4be3-abb9-96b892d7b5a0", Rel: "bookmark", }, }, FlavorID: "m1.small", ImageID: "Fedora-AtomicHost-29-20190820.0.x86_64", NodeAddresses: []string{"172.24.4.17"}, NodeCount: 1, MinNodeCount: 1, MaxNodeCount: &maxNodesThree, IsDefault: true, Role: "worker", StackID: "3cd55bb0-1115-4838-8eca-cefc13f7a21b", Status: "UPDATE_COMPLETE", StatusReason: "Stack UPDATE completed successfully", CreatedAt: nodeGroup2Created, UpdatedAt: nodeGroup2Updated, } func handleGetNodeGroupSuccess(t *testing.T) { th.Mux.HandleFunc("/v1/clusters/"+clusterUUID+"/nodegroups/"+nodeGroup1UUID, func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, http.MethodGet) th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprint(w, nodeGroupGetResponse) }) } func handleGetNodeGroupNotFound(t *testing.T) { th.Mux.HandleFunc("/v1/clusters/"+clusterUUID+"/nodegroups/"+badNodeGroupUUID, func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, http.MethodGet) th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusNotFound) fmt.Fprint(w, nodeGroupGetNotFoundResponse) }) } func handleGetNodeGroupClusterNotFound(t *testing.T) { th.Mux.HandleFunc("/v1/clusters/"+badClusterUUID+"/nodegroups/"+badNodeGroupUUID, func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, http.MethodGet) th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusNotFound) fmt.Fprint(w, nodeGroupGetClusterNotFoundResponse) }) } func handleListNodeGroupsSuccess(t *testing.T) { th.Mux.HandleFunc("/v1/clusters/"+clusterUUID+"/nodegroups", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, http.MethodGet) th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprint(w, nodeGroupListResponse) }) } func handleListNodeGroupsLimitSuccess(t *testing.T) { th.Mux.HandleFunc("/v1/clusters/"+clusterUUID+"/nodegroups", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, http.MethodGet) th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) r.ParseForm() if marker, ok := r.Form["marker"]; !ok { // No marker, this is the first request. th.TestFormValues(t, r, map[string]string{"limit": "1"}) fmt.Fprintf(w, nodeGroupListLimitResponse1, th.Endpoint()) } else { switch marker[0] { case nodeGroup1UUID: // Marker is the UUID of the first node group, return the second. fmt.Fprintf(w, nodeGroupListLimitResponse2, th.Endpoint()) case nodeGroup2UUID: // Marker is the UUID of the second node group, there are no more to return. fmt.Fprint(w, nodeGroupListLimitResponse3) } } }) } func handleListNodeGroupsClusterNotFound(t *testing.T) { th.Mux.HandleFunc("/v1/clusters/"+badClusterUUID+"/nodegroups", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, http.MethodGet) th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusNotFound) fmt.Fprint(w, nodeGroupListClusterNotFoundResponse) }) } func handleCreateNodeGroupSuccess(t *testing.T) { th.Mux.HandleFunc("/v1/clusters/"+clusterUUID+"/nodegroups", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, http.MethodPost) th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusAccepted) fmt.Fprintf(w, nodeGroupCreateResponse) }) } func handleCreateNodeGroupDuplicate(t *testing.T) { th.Mux.HandleFunc("/v1/clusters/"+clusterUUID+"/nodegroups", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, http.MethodPost) th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusConflict) fmt.Fprintf(w, nodeGroupCreateDuplicateResponse) }) } func handleCreateNodeGroupMaster(t *testing.T) { th.Mux.HandleFunc("/v1/clusters/"+clusterUUID+"/nodegroups", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, http.MethodPost) th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusBadRequest) fmt.Fprintf(w, nodeGroupCreateMasterResponse) }) } func handleCreateNodeGroupBadSizes(t *testing.T) { th.Mux.HandleFunc("/v1/clusters/"+clusterUUID+"/nodegroups", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, http.MethodPost) th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusConflict) fmt.Fprintf(w, nodeGroupCreateBadSizesResponse) }) } func handleUpdateNodeGroupSuccess(t *testing.T) { th.Mux.HandleFunc("/v1/clusters/"+clusterUUID+"/nodegroups/"+nodeGroup2UUID, func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, http.MethodPatch) th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusAccepted) fmt.Fprintf(w, nodeGroupUpdateResponse) }) } func handleUpdateNodeGroupInternal(t *testing.T) { th.Mux.HandleFunc("/v1/clusters/"+clusterUUID+"/nodegroups/"+nodeGroup2UUID, func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, http.MethodPatch) th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusBadRequest) fmt.Fprintf(w, nodeGroupUpdateInternalResponse) }) } func handleUpdateNodeGroupBadField(t *testing.T) { th.Mux.HandleFunc("/v1/clusters/"+clusterUUID+"/nodegroups/"+nodeGroup2UUID, func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, http.MethodPatch) th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusBadRequest) fmt.Fprintf(w, nodeGroupUpdateBadFieldResponse) }) } func handleUpdateNodeGroupBadMin(t *testing.T) { th.Mux.HandleFunc("/v1/clusters/"+clusterUUID+"/nodegroups/"+nodeGroup2UUID, func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, http.MethodPatch) th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusConflict) fmt.Fprintf(w, nodeGroupUpdateBadMinResponse) }) } func handleDeleteNodeGroupSuccess(t *testing.T) { th.Mux.HandleFunc("/v1/clusters/"+clusterUUID+"/nodegroups/"+nodeGroup2UUID, func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, http.MethodDelete) th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.WriteHeader(http.StatusNoContent) }) } func handleDeleteNodeGroupNotFound(t *testing.T) { th.Mux.HandleFunc("/v1/clusters/"+clusterUUID+"/nodegroups/"+badNodeGroupUUID, func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, http.MethodDelete) th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusNotFound) fmt.Fprintf(w, nodeGroupDeleteNotFoundResponse) }) } func handleDeleteNodeGroupClusterNotFound(t *testing.T) { th.Mux.HandleFunc("/v1/clusters/"+badClusterUUID+"/nodegroups/"+badNodeGroupUUID, func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, http.MethodDelete) th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusNotFound) fmt.Fprintf(w, nodeGroupDeleteClusterNotFoundResponse) }) } func handleDeleteNodeGroupDefault(t *testing.T) { th.Mux.HandleFunc("/v1/clusters/"+clusterUUID+"/nodegroups/"+nodeGroup2UUID, func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, http.MethodDelete) th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusBadRequest) fmt.Fprintf(w, nodeGroupDeleteDefaultResponse) }) } var nodeGroupGetResponse = fmt.Sprintf(` { "links":[ { "href":"http://123.456.789.0:9511/v1/clusters/bda75056-3a57-4ada-b943-658ac27beea0/nodegroups/b2e581be-2eec-45b8-921a-c85fbc23aaa3", "rel":"self" }, { "href":"http://123.456.789.0:9511/clusters/bda75056-3a57-4ada-b943-658ac27beea0/nodegroups/b2e581be-2eec-45b8-921a-c85fbc23aaa3", "rel":"bookmark" } ], "labels":{ "kube_tag":"v1.14.7" }, "updated_at":"2019-10-18T14:18:35+00:00", "cluster_id":"%s", "min_node_count":1, "id":9, "uuid":"%s", "version":null, "role":"master", "node_count":1, "project_id":"e91d02d561374de6b49960a27b3f08d0", "status":"UPDATE_COMPLETE", "docker_volume_size":null, "max_node_count":null, "is_default":true, "image_id":"Fedora-AtomicHost-29-20190820.0.x86_64", "node_addresses":[ "172.24.4.19" ], "status_reason":"Stack UPDATE completed successfully", "name":"default-master", "stack_id":"3cd55bb0-1115-4838-8eca-cefc13f7a21b", "created_at":"2019-10-18T14:03:37+00:00", "flavor_id":null }`, clusterUUID, nodeGroup1UUID) // nodeGroupGetNotFoundResponse is the returned error when there is a cluster with the requested ID but it does not have the requested node group. var nodeGroupGetNotFoundResponse = fmt.Sprintf(` { "errors":[ { "status":404, "code":"client", "links":[ ], "title":"Nodegroup %s could not be found", "request_id":"" } ] }`, badNodeGroupUUID) // nodeGroupGetClusterNotFoundResponse is the returned error when there is no cluster with the requested ID. var nodeGroupGetClusterNotFoundResponse = fmt.Sprintf(` { "errors":[ { "status":404, "code":"client", "links":[ ], "title":"Cluster %s could not be found", "request_id":"" } ] }`, badClusterUUID) var nodeGroupListResponse = fmt.Sprintf(` { "nodegroups":[ { "status":"UPDATE_COMPLETE", "is_default":true, "uuid":"%s", "max_node_count":null, "stack_id":"3cd55bb0-1115-4838-8eca-cefc13f7a21b", "min_node_count":1, "image_id":"Fedora-AtomicHost-29-20190820.0.x86_64", "role":"master", "flavor_id":null, "node_count":1, "name":"default-master" }, { "status":"UPDATE_COMPLETE", "is_default":true, "uuid":"%s", "max_node_count":null, "stack_id":"3cd55bb0-1115-4838-8eca-cefc13f7a21b", "min_node_count":1, "image_id":"Fedora-AtomicHost-29-20190820.0.x86_64", "role":"worker", "flavor_id":"m1.small", "node_count":1, "name":"default-worker" } ] }`, nodeGroup1UUID, nodeGroup2UUID) // nodeGroupListLimitResponse1 is the first response when requesting the list of node groups with a limit of 1. // It returns the URL for the next page with the marker of the first node group. var nodeGroupListLimitResponse1 = fmt.Sprintf(` { "nodegroups":[ { "status":"UPDATE_COMPLETE", "is_default":true, "name":"default-master", "max_node_count":null, "stack_id":"3cd55bb0-1115-4838-8eca-cefc13f7a21b", "min_node_count":1, "image_id":"Fedora-AtomicHost-29-20190820.0.x86_64", "cluster_id":"bda75056-3a57-4ada-b943-658ac27beea0", "flavor_id":null, "role":"master", "node_count":1, "uuid":"%s" } ], "next":"%%s/v1/clusters/bda75056-3a57-4ada-b943-658ac27beea0/nodegroups?sort_key=id&sort_dir=asc&limit=1&marker=%s" }`, nodeGroup1UUID, nodeGroup1UUID) // nodeGroupListLimitResponse2 is returned when making a request to the URL given by "next" in the first response. var nodeGroupListLimitResponse2 = fmt.Sprintf(` { "nodegroups":[ { "status":"UPDATE_COMPLETE", "is_default":true, "name":"default-worker", "max_node_count":null, "stack_id":"3cd55bb0-1115-4838-8eca-cefc13f7a21b", "min_node_count":1, "image_id":"Fedora-AtomicHost-29-20190820.0.x86_64", "cluster_id":"bda75056-3a57-4ada-b943-658ac27beea0", "flavor_id":"m1.small", "role":"worker", "node_count":1, "uuid":"%s" } ], "next":"%%s/v1/clusters/bda75056-3a57-4ada-b943-658ac27beea0/nodegroups?sort_key=id&sort_dir=asc&limit=1&marker=%s" }`, nodeGroup2UUID, nodeGroup2UUID) // nodeGroupListLimitResponse3 is the response when listing node groups using a marker and all node groups have already been returned. var nodeGroupListLimitResponse3 = `{"nodegroups": []}` // nodeGroupListClusterNotFoundResponse is the error returned when the list operation fails because there is no cluster with the requested ID. var nodeGroupListClusterNotFoundResponse = fmt.Sprintf(` { "errors":[ { "status":404, "code":"client", "links":[ ], "title":"Cluster %s could not be found", "request_id":"" } ] }`, badClusterUUID) var nodeGroupCreateResponse = fmt.Sprintf(` { "uuid":"12542dd8-9588-42a7-a2ff-06f49049920c", "links":[ { "href":"http://123.456.789.0:9511/v1/clusters/bda75056-3a57-4ada-b943-658ac27beea0/nodegroups/12542dd8-9588-42a7-a2ff-06f49049920c", "rel":"self" }, { "href":"http://123.456.789.0:9511/clusters/bda75056-3a57-4ada-b943-658ac27beea0/nodegroups/12542dd8-9588-42a7-a2ff-06f49049920c", "rel":"bookmark" } ], "max_node_count":null, "labels":{ "kube_tag":"v1.14.7" }, "min_node_count":1, "image_id":"Fedora-AtomicHost-29-20190820.0.x86_64", "cluster_id":"%s", "flavor_id":"m1.small", "role":"worker", "node_count":1, "project_id":"e91d02d561374de6b49960a27b3f08d0", "name":"test-ng" }`, clusterUUID) var nodeGroupCreateDuplicateResponse = ` { "errors":[ { "status":409, "code":"client", "links":[ ], "title":"A node group with name default-worker already exists in the cluster kube", "detail":"A node group with name default-worker already exists in the cluster kube.", "request_id":"" } ] }` var nodeGroupCreateMasterResponse = ` { "errors":[ { "status":400, "code":"client", "links":[ ], "title":"Creating master nodegroups is currently not supported", "detail":"Creating master nodegroups is currently not supported.", "request_id":"" } ] }` var nodeGroupCreateBadSizesResponse = ` { "errors":[ { "status":409, "code":"client", "links":[ ], "title":"max_node_count for new-ng is invalid (min_node_count (5) should be less or equal to max_node_count (3))", "detail":"max_node_count for new-ng is invalid (min_node_count (5) should be less or equal to max_node_count (3)).", "request_id":"" } ] }` var nodeGroupUpdateResponse = fmt.Sprintf(` { "links":[ { "href":"http://123.456.789.0:9511/v1/clusters/bda75056-3a57-4ada-b943-658ac27beea0/nodegroups/2457febf-520f-4be3-abb9-96b892d7b5a0", "rel":"self" }, { "href":"http://123.456.789.0:9511/clusters/bda75056-3a57-4ada-b943-658ac27beea0/nodegroups/2457febf-520f-4be3-abb9-96b892d7b5a0", "rel":"bookmark" } ], "labels":{ "kube_tag":"v1.14.7" }, "updated_at":"2019-10-18T14:18:36+00:00", "cluster_id":"%s", "min_node_count":1, "id":10, "uuid":"%s", "version":null, "role":"worker", "node_count":1, "project_id":"e91d02d561374de6b49960a27b3f08d0", "status":"UPDATE_COMPLETE", "docker_volume_size":null, "max_node_count":3, "is_default":true, "image_id":"Fedora-AtomicHost-29-20190820.0.x86_64", "node_addresses":[ "172.24.4.17" ], "status_reason":"Stack UPDATE completed successfully", "name":"default-worker", "stack_id":"3cd55bb0-1115-4838-8eca-cefc13f7a21b", "created_at":"2019-10-18T14:03:37+00:00", "flavor_id":"m1.small" }`, clusterUUID, nodeGroup2UUID) var nodeGroupUpdateInternalResponse = ` { "errors":[ { "status":400, "code":"client", "links":[ ], "title":"'/name' is an internal attribute and can not be updated", "detail":"'/name' is an internal attribute and can not be updated", "request_id":"" } ] }` var nodeGroupUpdateBadFieldResponse = ` { "errors":[ { "status":400, "code":"client", "links":[ ], "title":"Couldn't apply patch '[{'path': '/bad_field', 'value': u'abc123', 'op': u'replace'}]'", "detail":"Couldn't apply patch '[{'path': '/bad_field', 'value': u'abc123', 'op': u'replace'}]'. Reason: can't replace non-existent object 'bad_field'", "request_id":"" } ] }` var nodeGroupUpdateBadMinResponse = ` { "errors":[ { "status":409, "code":"client", "links":[ ], "title":"max_node_count for test-ng is invalid (min_node_count (5) should be less or equal to max_node_count (3))", "detail":"max_node_count for test-ng is invalid (min_node_count (5) should be less or equal to max_node_count (3)).", "request_id":"" } ] }` var nodeGroupDeleteNotFoundResponse = fmt.Sprintf(` { "errors":[ { "status":404, "code":"client", "links":[ ], "title":"Nodegroup %s could not be found", "detail":"Nodegroup %s could not be found.\nTraceback (most recent call last):\n\n File \"/opt/stack/magnum/magnum/conductor/handlers/indirection_api.py\", line 33, in _object_dispatch\n return getattr(target, method)(context, *args, **kwargs)\n\n File \"/usr/local/lib/python2.7/dist-packages/oslo_versionedobjects/base.py\", line 184, in wrapper\n result = fn(cls, context, *args, **kwargs)\n\n File \"/opt/stack/magnum/magnum/objects/nodegroup.py\", line 83, in get\n return cls.get_by_uuid(context, cluster_id, nodegroup_id)\n\n File \"/usr/local/lib/python2.7/dist-packages/oslo_versionedobjects/base.py\", line 184, in wrapper\n result = fn(cls, context, *args, **kwargs)\n\n File \"/opt/stack/magnum/magnum/objects/nodegroup.py\", line 109, in get_by_uuid\n db_nodegroup = cls.dbapi.get_nodegroup_by_uuid(context, cluster, uuid)\n\n File \"/opt/stack/magnum/magnum/db/sqlalchemy/api.py\", line 866, in get_nodegroup_by_uuid\n raise exception.NodeGroupNotFound(nodegroup=nodegroup_uuid)\n\nNodeGroupNotFound: Nodegroup %s could not be found.\n", "request_id":"" } ] }`, badNodeGroupUUID, badNodeGroupUUID, badNodeGroupUUID) var nodeGroupDeleteClusterNotFoundResponse = fmt.Sprintf(` { "errors":[ { "status":404, "code":"client", "links":[ ], "title":"Cluster %s could not be found", "detail":"Cluster %s could not be found.\nTraceback (most recent call last):\n\n File \"/opt/stack/magnum/magnum/conductor/handlers/indirection_api.py\", line 33, in _object_dispatch\n return getattr(target, method)(context, *args, **kwargs)\n\n File \"/usr/local/lib/python2.7/dist-packages/oslo_versionedobjects/base.py\", line 184, in wrapper\n result = fn(cls, context, *args, **kwargs)\n\n File \"/opt/stack/magnum/magnum/objects/cluster.py\", line 198, in get_by_uuid\n db_cluster = cls.dbapi.get_cluster_by_uuid(context, uuid)\n\n File \"/opt/stack/magnum/magnum/db/sqlalchemy/api.py\", line 238, in get_cluster_by_uuid\n raise exception.ClusterNotFound(cluster=cluster_uuid)\n\nClusterNotFound: Cluster %s could not be found.\n", "request_id":"" } ] }`, badClusterUUID, badClusterUUID, badClusterUUID) var nodeGroupDeleteDefaultResponse = ` { "errors":[ { "status":400, "code":"client", "links":[ ], "title":"Deleting a default nodegroup is not supported", "detail":"Deleting a default nodegroup is not supported.", "request_id":"" } ] }` requests_test.go000066400000000000000000000232051367513235700355240ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/containerinfra/v1/nodegroups/testingpackage testing import ( "testing" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack/containerinfra/v1/nodegroups" th "github.com/gophercloud/gophercloud/testhelper" fake "github.com/gophercloud/gophercloud/testhelper/client" ) // TestGetNodeGroupSuccess gets a node group successfully. func TestGetNodeGroupSuccess(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() handleGetNodeGroupSuccess(t) sc := fake.ServiceClient() sc.Endpoint = sc.Endpoint + "v1/" ng, err := nodegroups.Get(sc, clusterUUID, nodeGroup1UUID).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, expectedNodeGroup1, *ng) } // TestGetNodeGroupNotFound tries to get a node group which does not exist. func TestGetNodeGroupNotFound(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() handleGetNodeGroupNotFound(t) sc := fake.ServiceClient() sc.Endpoint = sc.Endpoint + "v1/" _, err := nodegroups.Get(sc, clusterUUID, badNodeGroupUUID).Extract() th.AssertEquals(t, true, err != nil) _, isNotFound := err.(gophercloud.ErrDefault404) th.AssertEquals(t, true, isNotFound) } // TestGetNodeGroupClusterNotFound tries to get a node group in // a cluster which does not exist. func TestGetNodeGroupClusterNotFound(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() handleGetNodeGroupClusterNotFound(t) sc := fake.ServiceClient() sc.Endpoint = sc.Endpoint + "v1/" _, err := nodegroups.Get(sc, badClusterUUID, badNodeGroupUUID).Extract() th.AssertEquals(t, true, err != nil) _, isNotFound := err.(gophercloud.ErrDefault404) th.AssertEquals(t, true, isNotFound) } // TestListNodeGroupsSuccess lists the node groups of a cluster successfully. func TestListNodeGroupsSuccess(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() handleListNodeGroupsSuccess(t) sc := fake.ServiceClient() sc.Endpoint = sc.Endpoint + "v1/" ngPages, err := nodegroups.List(sc, clusterUUID, nodegroups.ListOpts{}).AllPages() th.AssertNoErr(t, err) ngs, err := nodegroups.ExtractNodeGroups(ngPages) th.AssertNoErr(t, err) th.AssertEquals(t, 2, len(ngs)) th.AssertEquals(t, nodeGroup1UUID, ngs[0].UUID) th.AssertEquals(t, nodeGroup2UUID, ngs[1].UUID) } // TestListNodeGroupsLimitSuccess tests listing node groups // with each returned page limited to one node group and // also giving a URL to get the next page. func TestListNodeGroupsLimitSuccess(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() handleListNodeGroupsLimitSuccess(t) sc := fake.ServiceClient() sc.Endpoint = sc.Endpoint + "v1/" listOpts := nodegroups.ListOpts{Limit: 1} ngPages, err := nodegroups.List(sc, clusterUUID, listOpts).AllPages() th.AssertNoErr(t, err) ngs, err := nodegroups.ExtractNodeGroups(ngPages) th.AssertNoErr(t, err) th.AssertEquals(t, 2, len(ngs)) th.AssertEquals(t, nodeGroup1UUID, ngs[0].UUID) th.AssertEquals(t, nodeGroup2UUID, ngs[1].UUID) } // TestListNodeGroupsClusterNotFound tries to list node groups // of a cluster which does not exist. func TestListNodeGroupsClusterNotFound(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() handleListNodeGroupsClusterNotFound(t) sc := fake.ServiceClient() sc.Endpoint = sc.Endpoint + "v1/" _, err := nodegroups.List(sc, clusterUUID, nodegroups.ListOpts{}).AllPages() th.AssertEquals(t, true, err != nil) _, isNotFound := err.(gophercloud.ErrDefault404) th.AssertEquals(t, true, isNotFound) } // TestCreateNodeGroupSuccess creates a node group successfully. func TestCreateNodeGroupSuccess(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() handleCreateNodeGroupSuccess(t) sc := fake.ServiceClient() sc.Endpoint = sc.Endpoint + "v1/" createOpts := nodegroups.CreateOpts{ Name: "test-ng", } ng, err := nodegroups.Create(sc, clusterUUID, createOpts).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, expectedCreatedNodeGroup, *ng) } // TestCreateNodeGroupDuplicate creates a node group with // the same name as an existing one. func TestCreateNodeGroupDuplicate(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() handleCreateNodeGroupDuplicate(t) sc := fake.ServiceClient() sc.Endpoint = sc.Endpoint + "v1/" createOpts := nodegroups.CreateOpts{ Name: "default-worker", } _, err := nodegroups.Create(sc, clusterUUID, createOpts).Extract() th.AssertEquals(t, true, err != nil) _, isNotAccepted := err.(gophercloud.ErrDefault409) th.AssertEquals(t, true, isNotAccepted) } // TestCreateNodeGroupMaster creates a node group with // role=master which is not allowed. func TestCreateNodeGroupMaster(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() handleCreateNodeGroupMaster(t) sc := fake.ServiceClient() sc.Endpoint = sc.Endpoint + "v1/" createOpts := nodegroups.CreateOpts{ Name: "new-ng", Role: "master", } _, err := nodegroups.Create(sc, clusterUUID, createOpts).Extract() th.AssertEquals(t, true, err != nil) _, isBadRequest := err.(gophercloud.ErrDefault400) th.AssertEquals(t, true, isBadRequest) } // TestCreateNodeGroupBadSizes creates a node group with // min_nodes greater than max_nodes. func TestCreateNodeGroupBadSizes(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() handleCreateNodeGroupBadSizes(t) sc := fake.ServiceClient() sc.Endpoint = sc.Endpoint + "v1/" maxNodes := 3 createOpts := nodegroups.CreateOpts{ Name: "default-worker", MinNodeCount: 5, MaxNodeCount: &maxNodes, } _, err := nodegroups.Create(sc, clusterUUID, createOpts).Extract() th.AssertEquals(t, true, err != nil) _, isNotAccepted := err.(gophercloud.ErrDefault409) th.AssertEquals(t, true, isNotAccepted) } // TestUpdateNodeGroupSuccess updates a node group successfully. func TestUpdateNodeGroupSuccess(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() handleUpdateNodeGroupSuccess(t) sc := fake.ServiceClient() sc.Endpoint = sc.Endpoint + "v1/" updateOpts := []nodegroups.UpdateOptsBuilder{ nodegroups.UpdateOpts{ Op: nodegroups.ReplaceOp, Path: "/max_node_count", Value: 3, }, } ng, err := nodegroups.Update(sc, clusterUUID, nodeGroup2UUID, updateOpts).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, expectedUpdatedNodeGroup, *ng) } // TestUpdateNodeGroupInternal tries to update an internal // property of the node group. func TestUpdateNodeGroupInternal(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() handleUpdateNodeGroupInternal(t) sc := fake.ServiceClient() sc.Endpoint = sc.Endpoint + "v1/" updateOpts := []nodegroups.UpdateOptsBuilder{ nodegroups.UpdateOpts{ Op: nodegroups.ReplaceOp, Path: "/name", Value: "newname", }, } _, err := nodegroups.Update(sc, clusterUUID, nodeGroup2UUID, updateOpts).Extract() th.AssertEquals(t, true, err != nil) _, isBadRequest := err.(gophercloud.ErrDefault400) th.AssertEquals(t, true, isBadRequest) } // TestUpdateNodeGroupBadField tries to update a // field of the node group that does not exist. func TestUpdateNodeGroupBadField(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() handleUpdateNodeGroupBadField(t) sc := fake.ServiceClient() sc.Endpoint = sc.Endpoint + "v1/" updateOpts := []nodegroups.UpdateOptsBuilder{ nodegroups.UpdateOpts{ Op: nodegroups.ReplaceOp, Path: "/bad_field", Value: "abc123", }, } _, err := nodegroups.Update(sc, clusterUUID, nodeGroup2UUID, updateOpts).Extract() th.AssertEquals(t, true, err != nil) _, isBadRequest := err.(gophercloud.ErrDefault400) th.AssertEquals(t, true, isBadRequest) } // TestUpdateNodeGroupBadMin tries to set a minimum node count // greater than the current node count func TestUpdateNodeGroupBadMin(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() handleUpdateNodeGroupBadMin(t) sc := fake.ServiceClient() sc.Endpoint = sc.Endpoint + "v1/" updateOpts := []nodegroups.UpdateOptsBuilder{ nodegroups.UpdateOpts{ Op: nodegroups.ReplaceOp, Path: "/min_node_count", Value: 5, }, } _, err := nodegroups.Update(sc, clusterUUID, nodeGroup2UUID, updateOpts).Extract() th.AssertEquals(t, true, err != nil) _, isNotAccepted := err.(gophercloud.ErrDefault409) th.AssertEquals(t, true, isNotAccepted) } // TestDeleteNodeGroupSuccess deletes a node group successfully. func TestDeleteNodeGroupSuccess(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() handleDeleteNodeGroupSuccess(t) sc := fake.ServiceClient() sc.Endpoint = sc.Endpoint + "v1/" err := nodegroups.Delete(sc, clusterUUID, nodeGroup2UUID).ExtractErr() th.AssertNoErr(t, err) } // TestDeleteNodeGroupNotFound tries to delete a node group that does not exist. func TestDeleteNodeGroupNotFound(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() handleDeleteNodeGroupNotFound(t) sc := fake.ServiceClient() sc.Endpoint = sc.Endpoint + "v1/" err := nodegroups.Delete(sc, clusterUUID, badNodeGroupUUID).ExtractErr() _, isNotFound := err.(gophercloud.ErrDefault404) th.AssertEquals(t, true, isNotFound) } // TestDeleteNodeGroupClusterNotFound tries to delete a node group in a cluster that does not exist. func TestDeleteNodeGroupClusterNotFound(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() handleDeleteNodeGroupClusterNotFound(t) sc := fake.ServiceClient() sc.Endpoint = sc.Endpoint + "v1/" err := nodegroups.Delete(sc, badClusterUUID, badNodeGroupUUID).ExtractErr() _, isNotFound := err.(gophercloud.ErrDefault404) th.AssertEquals(t, true, isNotFound) } // TestDeleteNodeGroupDefault tries to delete a protected default node group. func TestDeleteNodeGroupDefault(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() handleDeleteNodeGroupDefault(t) sc := fake.ServiceClient() sc.Endpoint = sc.Endpoint + "v1/" err := nodegroups.Delete(sc, clusterUUID, nodeGroup2UUID).ExtractErr() _, isBadRequest := err.(gophercloud.ErrDefault400) th.AssertEquals(t, true, isBadRequest) } golang-github-gophercloud-gophercloud-0.12.0/openstack/containerinfra/v1/nodegroups/urls.go000066400000000000000000000014471367513235700322050ustar00rootroot00000000000000package nodegroups import ( "github.com/gophercloud/gophercloud" ) func getURL(c *gophercloud.ServiceClient, clusterID, nodeGroupID string) string { return c.ServiceURL("clusters", clusterID, "nodegroups", nodeGroupID) } func listURL(c *gophercloud.ServiceClient, clusterID string) string { return c.ServiceURL("clusters", clusterID, "nodegroups") } func createURL(c *gophercloud.ServiceClient, clusterID string) string { return c.ServiceURL("clusters", clusterID, "nodegroups") } func updateURL(c *gophercloud.ServiceClient, clusterID, nodeGroupID string) string { return c.ServiceURL("clusters", clusterID, "nodegroups", nodeGroupID) } func deleteURL(c *gophercloud.ServiceClient, clusterID, nodeGroupID string) string { return c.ServiceURL("clusters", clusterID, "nodegroups", nodeGroupID) } golang-github-gophercloud-gophercloud-0.12.0/openstack/containerinfra/v1/quotas/000077500000000000000000000000001367513235700300125ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/containerinfra/v1/quotas/doc.go000066400000000000000000000005361367513235700311120ustar00rootroot00000000000000/* Package quotas contains functionality for working with Magnum Quota API. Example to Create a Quota createOpts := quotas.CreateOpts{ ProjectID: "aa5436ab58144c768ca4e9d2e9f5c3b2", Resource: "Cluster", HardLimit: 10, } quota, err := quotas.Create(serviceClient, createOpts).Extract() if err != nil { panic(err) } */ package quotas golang-github-gophercloud-gophercloud-0.12.0/openstack/containerinfra/v1/quotas/requests.go000066400000000000000000000016431367513235700322200ustar00rootroot00000000000000package quotas import ( "github.com/gophercloud/gophercloud" ) // CreateOptsBuilder Builder. type CreateOptsBuilder interface { ToQuotaCreateMap() (map[string]interface{}, error) } // CreateOpts params type CreateOpts struct { ProjectID string `json:"project_id"` Resource string `json:"resource"` HardLimit int `json:"hard_limit"` } // ToQuotaCreateMap constructs a request body from CreateOpts. func (opts CreateOpts) ToQuotaCreateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "") } // Create requests the creation of a new quota. func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToQuotaCreateMap() if err != nil { r.Err = err return } resp, err := client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{201}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } golang-github-gophercloud-gophercloud-0.12.0/openstack/containerinfra/v1/quotas/results.go000066400000000000000000000017611367513235700320470ustar00rootroot00000000000000package quotas import ( "encoding/json" "fmt" "time" "github.com/gophercloud/gophercloud" ) type commonResult struct { gophercloud.Result } // CreateResult is the response of a Create operations. type CreateResult struct { commonResult } // Extract is a function that accepts a result and extracts a quota resource. func (r commonResult) Extract() (*Quotas, error) { var s *Quotas err := r.ExtractInto(&s) return s, err } type Quotas struct { Resource string `json:"resource"` CreatedAt time.Time `json:"created_at"` UpdatedAt time.Time `json:"updated_at"` HardLimit int `json:"hard_limit"` ProjectID string `json:"project_id"` ID string `json:"-"` } func (r *Quotas) UnmarshalJSON(b []byte) error { type tmp Quotas var s struct { tmp ID interface{} `json:"id"` } err := json.Unmarshal(b, &s) if err != nil { return err } *r = Quotas(s.tmp) switch t := s.ID.(type) { case float64: r.ID = fmt.Sprint(t) case string: r.ID = t } return nil } golang-github-gophercloud-gophercloud-0.12.0/openstack/containerinfra/v1/quotas/testing/000077500000000000000000000000001367513235700314675ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/containerinfra/v1/quotas/testing/doc.go000066400000000000000000000000201367513235700325530ustar00rootroot00000000000000package testing golang-github-gophercloud-gophercloud-0.12.0/openstack/containerinfra/v1/quotas/testing/fixtures.go000066400000000000000000000015731367513235700336750ustar00rootroot00000000000000package testing import ( "fmt" "net/http" "testing" th "github.com/gophercloud/gophercloud/testhelper" fake "github.com/gophercloud/gophercloud/testhelper/client" ) const projectID = "aa5436ab58144c768ca4e9d2e9f5c3b2" const requestUUID = "req-781e9bdc-4163-46eb-91c9-786c53188bbb" var CreateResponse = fmt.Sprintf(` { "resource": "Cluster", "created_at": "2017-01-17T17:35:48+00:00", "updated_at": null, "hard_limit": 1, "project_id": "%s", "id": 26 }`, projectID) func HandleCreateQuotaSuccessfully(t *testing.T) { th.Mux.HandleFunc("/v1/quotas", 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.StatusCreated) fmt.Fprint(w, CreateResponse) }) } requests_test.go000066400000000000000000000014341367513235700346530ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/containerinfra/v1/quotas/testingpackage testing import ( "testing" "github.com/gophercloud/gophercloud/openstack/containerinfra/v1/quotas" th "github.com/gophercloud/gophercloud/testhelper" fake "github.com/gophercloud/gophercloud/testhelper/client" ) func TestCreateQuota(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleCreateQuotaSuccessfully(t) opts := quotas.CreateOpts{ ProjectID: "aa5436ab58144c768ca4e9d2e9f5c3b2", Resource: "Cluster", HardLimit: 10, } sc := fake.ServiceClient() sc.Endpoint = sc.Endpoint + "v1/" res := quotas.Create(sc, opts) th.AssertNoErr(t, res.Err) requestID := res.Header.Get("X-OpenStack-Request-Id") th.AssertEquals(t, requestUUID, requestID) quota, err := res.Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, projectID, quota.ProjectID) } golang-github-gophercloud-gophercloud-0.12.0/openstack/containerinfra/v1/quotas/urls.go000066400000000000000000000004221367513235700313240ustar00rootroot00000000000000package quotas import ( "github.com/gophercloud/gophercloud" ) var apiName = "quotas" func commonURL(client *gophercloud.ServiceClient) string { return client.ServiceURL(apiName) } func createURL(client *gophercloud.ServiceClient) string { return commonURL(client) } golang-github-gophercloud-gophercloud-0.12.0/openstack/db/000077500000000000000000000000001367513235700235335ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/db/v1/000077500000000000000000000000001367513235700240615ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/db/v1/configurations/000077500000000000000000000000001367513235700271135ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/db/v1/configurations/doc.go000066400000000000000000000012251367513235700302070ustar00rootroot00000000000000// 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 golang-github-gophercloud-gophercloud-0.12.0/openstack/db/v1/configurations/requests.go000066400000000000000000000163021367513235700313170ustar00rootroot00000000000000package 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 } resp, err := client.Post(baseURL(client), &b, &r.Body, &gophercloud.RequestOpts{OkCodes: []int{200}}) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get will retrieve the details for a specified configuration group. func Get(client *gophercloud.ServiceClient, configID string) (r GetResult) { resp, err := client.Get(resourceURL(client, configID), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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 } resp, err := client.Patch(resourceURL(client, configID), &b, nil, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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 } resp, err := client.Put(resourceURL(client, configID), &b, nil, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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) { resp, err := client.Delete(resourceURL(client, configID), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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) { resp, err := client.Get(getDSParamURL(client, datastoreID, versionID, paramID), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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) { resp, err := client.Get(getGlobalParamURL(client, versionID, paramID), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } golang-github-gophercloud-gophercloud-0.12.0/openstack/db/v1/configurations/results.go000066400000000000000000000066271367513235700311560ustar00rootroot00000000000000package 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 } golang-github-gophercloud-gophercloud-0.12.0/openstack/db/v1/configurations/testing/000077500000000000000000000000001367513235700305705ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/db/v1/configurations/testing/doc.go000066400000000000000000000000501367513235700316570ustar00rootroot00000000000000// db_configurations_v1 package testing golang-github-gophercloud-gophercloud-0.12.0/openstack/db/v1/configurations/testing/fixtures.go000066400000000000000000000071031367513235700327710ustar00rootroot00000000000000package 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), }, } golang-github-gophercloud-gophercloud-0.12.0/openstack/db/v1/configurations/testing/requests_test.go000066400000000000000000000153221367513235700340340ustar00rootroot00000000000000package 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) } golang-github-gophercloud-gophercloud-0.12.0/openstack/db/v1/configurations/urls.go000066400000000000000000000021521367513235700304270ustar00rootroot00000000000000package 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.12.0/openstack/db/v1/databases/000077500000000000000000000000001367513235700260105ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/db/v1/databases/doc.go000066400000000000000000000003611367513235700271040ustar00rootroot00000000000000// 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.12.0/openstack/db/v1/databases/requests.go000066400000000000000000000066561367513235700302270ustar00rootroot00000000000000package 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 } resp, err := client.Post(baseURL(client, instanceID), &b, nil, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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) { resp, err := client.Delete(dbURL(client, instanceID, dbName), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } golang-github-gophercloud-gophercloud-0.12.0/openstack/db/v1/databases/results.go000066400000000000000000000030221367513235700300350ustar00rootroot00000000000000package 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.12.0/openstack/db/v1/databases/testing/000077500000000000000000000000001367513235700274655ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/db/v1/databases/testing/doc.go000066400000000000000000000000431367513235700305560ustar00rootroot00000000000000// db_databases_v1 package testing golang-github-gophercloud-gophercloud-0.12.0/openstack/db/v1/databases/testing/fixtures.go000066400000000000000000000015531367513235700316710ustar00rootroot00000000000000package 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) } golang-github-gophercloud-gophercloud-0.12.0/openstack/db/v1/databases/testing/requests_test.go000066400000000000000000000026621367513235700327340ustar00rootroot00000000000000package 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.12.0/openstack/db/v1/databases/urls.go000066400000000000000000000005271367513235700273300ustar00rootroot00000000000000package 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.12.0/openstack/db/v1/datastores/000077500000000000000000000000001367513235700262325ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/db/v1/datastores/doc.go000066400000000000000000000002241367513235700273240ustar00rootroot00000000000000// Package datastores provides information and interaction with the datastore // API resource in the Rackspace Database service. package datastores golang-github-gophercloud-gophercloud-0.12.0/openstack/db/v1/datastores/requests.go000066400000000000000000000025611367513235700304400ustar00rootroot00000000000000package 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) { resp, err := client.Get(resourceURL(client, datastoreID), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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) { resp, err := client.Get(versionURL(client, datastoreID, versionID), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } golang-github-gophercloud-gophercloud-0.12.0/openstack/db/v1/datastores/results.go000066400000000000000000000051621367513235700302660ustar00rootroot00000000000000package 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.12.0/openstack/db/v1/datastores/testing/000077500000000000000000000000001367513235700277075ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/db/v1/datastores/testing/doc.go000066400000000000000000000000441367513235700310010ustar00rootroot00000000000000// db_datastores_v1 package testing golang-github-gophercloud-gophercloud-0.12.0/openstack/db/v1/datastores/testing/fixtures.go000066400000000000000000000056141367513235700321150ustar00rootroot00000000000000package 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, } golang-github-gophercloud-gophercloud-0.12.0/openstack/db/v1/datastores/testing/requests_test.go000066400000000000000000000037231367513235700331550ustar00rootroot00000000000000package 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.12.0/openstack/db/v1/datastores/urls.go000066400000000000000000000010321367513235700275420ustar00rootroot00000000000000package 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.12.0/openstack/db/v1/flavors/000077500000000000000000000000001367513235700255355ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/db/v1/flavors/doc.go000066400000000000000000000004771367513235700266410ustar00rootroot00000000000000// 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.12.0/openstack/db/v1/flavors/requests.go000066400000000000000000000014321367513235700277370ustar00rootroot00000000000000package 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) { resp, err := client.Get(getURL(client, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } golang-github-gophercloud-gophercloud-0.12.0/openstack/db/v1/flavors/results.go000066400000000000000000000035421367513235700275710ustar00rootroot00000000000000package 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.12.0/openstack/db/v1/flavors/testing/000077500000000000000000000000001367513235700272125ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/db/v1/flavors/testing/doc.go000066400000000000000000000000411367513235700303010ustar00rootroot00000000000000// db_flavors_v1 package testing golang-github-gophercloud-gophercloud-0.12.0/openstack/db/v1/flavors/testing/fixtures.go000066400000000000000000000022511367513235700314120ustar00rootroot00000000000000package 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) } golang-github-gophercloud-gophercloud-0.12.0/openstack/db/v1/flavors/testing/requests_test.go000066400000000000000000000047511367513235700324620ustar00rootroot00000000000000package 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.12.0/openstack/db/v1/flavors/urls.go000066400000000000000000000004151367513235700270510ustar00rootroot00000000000000package 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.12.0/openstack/db/v1/instances/000077500000000000000000000000001367513235700260505ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/db/v1/instances/doc.go000066400000000000000000000004721367513235700271470ustar00rootroot00000000000000// 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.12.0/openstack/db/v1/instances/requests.go000066400000000000000000000212651367513235700302600ustar00rootroot00000000000000package 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 } resp, err := client.Post(baseURL(client), &b, &r.Body, &gophercloud.RequestOpts{OkCodes: []int{200}}) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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) { resp, err := client.Get(resourceURL(client, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete permanently destroys the database instance. func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { resp, err := client.Delete(resourceURL(client, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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) { resp, err := client.Post(userRootURL(client, id), nil, &r.Body, &gophercloud.RequestOpts{OkCodes: []int{200}}) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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) { resp, err := client.Get(userRootURL(client, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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{}{}} resp, err := client.Post(actionURL(client, id), &b, nil, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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}} resp, err := client.Post(actionURL(client, id), &b, nil, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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}}} resp, err := client.Post(actionURL(client, id), &b, nil, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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}} resp, err := client.Put(resourceURL(client, instanceID), &b, nil, &gophercloud.RequestOpts{OkCodes: []int{202}}) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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{}{}} resp, err := client.Put(resourceURL(client, instanceID), &b, nil, &gophercloud.RequestOpts{OkCodes: []int{202}}) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } golang-github-gophercloud-gophercloud-0.12.0/openstack/db/v1/instances/results.go000066400000000000000000000131541367513235700301040ustar00rootroot00000000000000package 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.12.0/openstack/db/v1/instances/testing/000077500000000000000000000000001367513235700275255ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/db/v1/instances/testing/doc.go000066400000000000000000000000431367513235700306160ustar00rootroot00000000000000// db_instances_v1 package testing golang-github-gophercloud-gophercloud-0.12.0/openstack/db/v1/instances/testing/fixtures.go000066400000000000000000000141301367513235700317240ustar00rootroot00000000000000package 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) } golang-github-gophercloud-gophercloud-0.12.0/openstack/db/v1/instances/testing/requests_test.go000066400000000000000000000102211367513235700327620ustar00rootroot00000000000000package 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.12.0/openstack/db/v1/instances/urls.go000066400000000000000000000007541367513235700273720ustar00rootroot00000000000000package 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.12.0/openstack/db/v1/users/000077500000000000000000000000001367513235700252225ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/db/v1/users/doc.go000066400000000000000000000002051367513235700263130ustar00rootroot00000000000000// Package users provides information and interaction with the user API // resource in the OpenStack Database service. package users golang-github-gophercloud-gophercloud-0.12.0/openstack/db/v1/users/requests.go000066400000000000000000000072721367513235700274340ustar00rootroot00000000000000package 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 } resp, err := client.Post(baseURL(client, instanceID), &b, nil, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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) { resp, err := client.Delete(userURL(client, instanceID, userName), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } golang-github-gophercloud-gophercloud-0.12.0/openstack/db/v1/users/results.go000066400000000000000000000026531367513235700272600ustar00rootroot00000000000000package 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.12.0/openstack/db/v1/users/testing/000077500000000000000000000000001367513235700266775ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/db/v1/users/testing/doc.go000066400000000000000000000000371367513235700277730ustar00rootroot00000000000000// db_users_v1 package testing golang-github-gophercloud-gophercloud-0.12.0/openstack/db/v1/users/testing/fixtures.go000066400000000000000000000017031367513235700311000ustar00rootroot00000000000000package 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) } golang-github-gophercloud-gophercloud-0.12.0/openstack/db/v1/users/testing/requests_test.go000066400000000000000000000032621367513235700321430ustar00rootroot00000000000000package 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.12.0/openstack/db/v1/users/urls.go000066400000000000000000000005211367513235700265340ustar00rootroot00000000000000package 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.12.0/openstack/dns/000077500000000000000000000000001367513235700237325ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/dns/v2/000077500000000000000000000000001367513235700242615ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/dns/v2/recordsets/000077500000000000000000000000001367513235700264365ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/dns/v2/recordsets/doc.go000066400000000000000000000022071367513235700275330ustar00rootroot00000000000000/* 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 golang-github-gophercloud-gophercloud-0.12.0/openstack/dns/v2/recordsets/requests.go000066400000000000000000000124111367513235700306370ustar00rootroot00000000000000package 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) { resp, err := client.Get(rrsetURL(client, zoneID, rrsetID), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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 } resp, err := client.Post(baseURL(client, zoneID), &b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{201, 202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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 was actually set, use 0 as a special value to send "null", // even though the result from the API is 0. // // Otherwise, don't send the TTL field. if opts.TTL != nil { ttl := *(opts.TTL) if ttl > 0 { b["ttl"] = 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 } resp, err := client.Put(rrsetURL(client, zoneID, rrsetID), &b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete removes an existing RecordSet. func Delete(client *gophercloud.ServiceClient, zoneID string, rrsetID string) (r DeleteResult) { resp, err := client.Delete(rrsetURL(client, zoneID, rrsetID), &gophercloud.RequestOpts{ OkCodes: []int{202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } golang-github-gophercloud-gophercloud-0.12.0/openstack/dns/v2/recordsets/results.go000066400000000000000000000072641367513235700304770ustar00rootroot00000000000000package 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.12.0/openstack/dns/v2/recordsets/testing/000077500000000000000000000000001367513235700301135ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/dns/v2/recordsets/testing/doc.go000066400000000000000000000000511367513235700312030ustar00rootroot00000000000000// recordsets unit tests package testing golang-github-gophercloud-gophercloud-0.12.0/openstack/dns/v2/recordsets/testing/fixtures.go000066400000000000000000000302311367513235700323120ustar00rootroot00000000000000package 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) }) } golang-github-gophercloud-gophercloud-0.12.0/openstack/dns/v2/recordsets/testing/requests_test.go000066400000000000000000000104671367513235700333640ustar00rootroot00000000000000package 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) var description = "Updated description" ttl := 0 updateOpts := recordsets.UpdateOpts{ TTL: &ttl, Description: &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.12.0/openstack/dns/v2/recordsets/urls.go000066400000000000000000000005161367513235700277540ustar00rootroot00000000000000package 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.12.0/openstack/dns/v2/zones/000077500000000000000000000000001367513235700254175ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/dns/v2/zones/doc.go000066400000000000000000000016321367513235700265150ustar00rootroot00000000000000/* 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.12.0/openstack/dns/v2/zones/requests.go000066400000000000000000000120171367513235700276220ustar00rootroot00000000000000package 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) { resp, err := client.Get(zoneURL(client, zoneID), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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 } resp, err := client.Post(baseURL(client), &b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{201, 202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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 } resp, err := client.Patch(zoneURL(client, zoneID), &b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete implements a zone delete request. func Delete(client *gophercloud.ServiceClient, zoneID string) (r DeleteResult) { resp, err := client.Delete(zoneURL(client, zoneID), &gophercloud.RequestOpts{ OkCodes: []int{202}, JSONResponse: &r.Body, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } golang-github-gophercloud-gophercloud-0.12.0/openstack/dns/v2/zones/results.go000066400000000000000000000102031367513235700274430ustar00rootroot00000000000000package 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.12.0/openstack/dns/v2/zones/testing/000077500000000000000000000000001367513235700270745ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/dns/v2/zones/testing/doc.go000066400000000000000000000000441367513235700301660ustar00rootroot00000000000000// zones unit tests package testing golang-github-gophercloud-gophercloud-0.12.0/openstack/dns/v2/zones/testing/fixtures.go000066400000000000000000000225111367513235700312750ustar00rootroot00000000000000package 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) }) } golang-github-gophercloud-gophercloud-0.12.0/openstack/dns/v2/zones/testing/requests_test.go000066400000000000000000000051611367513235700323400ustar00rootroot00000000000000package 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) var description = "Updated Description" updateOpts := zones.UpdateOpts{ TTL: 600, Description: &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.12.0/openstack/dns/v2/zones/urls.go000066400000000000000000000003741367513235700267370ustar00rootroot00000000000000package 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.12.0/openstack/doc.go000066400000000000000000000007561367513235700242520ustar00rootroot00000000000000/* 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(provider, gophercloud.EndpointOpts{ Region: os.Getenv("OS_REGION_NAME"), }) */ package openstack golang-github-gophercloud-gophercloud-0.12.0/openstack/endpoint_location.go000066400000000000000000000100561367513235700272070ustar00rootroot00000000000000package 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) } } } } // If multiple endpoints were found, use the first result // and disregard the other endpoints. // // This behavior matches the Python library. See GH-1764. if len(endpoints) > 1 { endpoints = endpoints[0:1] } // 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) } } } } // If multiple endpoints were found, use the first result // and disregard the other endpoints. // // This behavior matches the Python library. See GH-1764. if len(endpoints) > 1 { endpoints = endpoints[0:1] } // 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.12.0/openstack/errors.go000066400000000000000000000026021367513235700250110ustar00rootroot00000000000000package openstack import ( "fmt" "github.com/gophercloud/gophercloud" ) // 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) } // 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.12.0/openstack/identity/000077500000000000000000000000001367513235700247775ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/identity/v2/000077500000000000000000000000001367513235700253265ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/identity/v2/extensions/000077500000000000000000000000001367513235700275255ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/identity/v2/extensions/admin/000077500000000000000000000000001367513235700306155ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/identity/v2/extensions/admin/roles/000077500000000000000000000000001367513235700317415ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/identity/v2/extensions/admin/roles/doc.go000066400000000000000000000031031367513235700330320ustar00rootroot00000000000000/* 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.go000066400000000000000000000025341367513235700340700ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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) { resp, err := client.Put(userRoleURL(client, tenantID, userID, roleID), nil, nil, &gophercloud.RequestOpts{ OkCodes: []int{200, 201}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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) { resp, err := client.Delete(userRoleURL(client, tenantID, userID, roleID), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } golang-github-gophercloud-gophercloud-0.12.0/openstack/identity/v2/extensions/admin/roles/results.go000066400000000000000000000022621367513235700337730ustar00rootroot00000000000000package 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 } golang-github-gophercloud-gophercloud-0.12.0/openstack/identity/v2/extensions/admin/roles/testing/000077500000000000000000000000001367513235700334165ustar00rootroot00000000000000doc.go000066400000000000000000000000441367513235700344310ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/identity/v2/extensions/admin/roles/testing// roles unit tests package testing fixtures.go000066400000000000000000000023541367513235700355430ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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.go000066400000000000000000000025301367513235700366000ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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) } golang-github-gophercloud-gophercloud-0.12.0/openstack/identity/v2/extensions/admin/roles/urls.go000066400000000000000000000010111367513235700332460ustar00rootroot00000000000000package 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) } golang-github-gophercloud-gophercloud-0.12.0/openstack/identity/v2/extensions/delegate.go000066400000000000000000000027761367513235700316420ustar00rootroot00000000000000package 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)}, } }) } golang-github-gophercloud-gophercloud-0.12.0/openstack/identity/v2/extensions/doc.go000066400000000000000000000002351367513235700306210ustar00rootroot00000000000000// Package extensions provides information and interaction with the // different extensions available for the OpenStack Identity service. package extensions golang-github-gophercloud-gophercloud-0.12.0/openstack/identity/v2/extensions/testing/000077500000000000000000000000001367513235700312025ustar00rootroot00000000000000delegate_test.go000066400000000000000000000020511367513235700342610ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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) } golang-github-gophercloud-gophercloud-0.12.0/openstack/identity/v2/extensions/testing/doc.go000066400000000000000000000000511367513235700322720ustar00rootroot00000000000000// extensions unit tests package testing golang-github-gophercloud-gophercloud-0.12.0/openstack/identity/v2/extensions/testing/fixtures.go000066400000000000000000000027421367513235700334070ustar00rootroot00000000000000package 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.12.0/openstack/identity/v2/tenants/000077500000000000000000000000001367513235700270025ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/identity/v2/tenants/doc.go000066400000000000000000000025721367513235700301040ustar00rootroot00000000000000/* 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 golang-github-gophercloud-gophercloud-0.12.0/openstack/identity/v2/tenants/requests.go000066400000000000000000000073371367513235700312160ustar00rootroot00000000000000package 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 } resp, err := client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get requests details on a single tenant by ID. func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { resp, err := client.Get(getURL(client, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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 } resp, err := client.Put(updateURL(client, id), &b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete is the operation responsible for permanently deleting a tenant. func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { resp, err := client.Delete(deleteURL(client, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } golang-github-gophercloud-gophercloud-0.12.0/openstack/identity/v2/tenants/results.go000066400000000000000000000044371367513235700310420ustar00rootroot00000000000000package 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 } golang-github-gophercloud-gophercloud-0.12.0/openstack/identity/v2/tenants/testing/000077500000000000000000000000001367513235700304575ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/identity/v2/tenants/testing/doc.go000066400000000000000000000000461367513235700315530ustar00rootroot00000000000000// tenants unit tests package testing golang-github-gophercloud-gophercloud-0.12.0/openstack/identity/v2/tenants/testing/fixtures.go000066400000000000000000000071421367513235700326630ustar00rootroot00000000000000package 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" } } `) }) } golang-github-gophercloud-gophercloud-0.12.0/openstack/identity/v2/tenants/testing/requests_test.go000066400000000000000000000050201367513235700337150ustar00rootroot00000000000000package 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" description := "This is new name" opts := tenants.UpdateOpts{ Name: "new_name", Description: &description, 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.12.0/openstack/identity/v2/tenants/urls.go000066400000000000000000000011701367513235700303150ustar00rootroot00000000000000package 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.12.0/openstack/identity/v2/tokens/000077500000000000000000000000001367513235700266315ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/identity/v2/tokens/doc.go000066400000000000000000000020321367513235700277220ustar00rootroot00000000000000/* 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 golang-github-gophercloud-gophercloud-0.12.0/openstack/identity/v2/tokens/requests.go000066400000000000000000000071701367513235700310400ustar00rootroot00000000000000package 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 } resp, err := client.Post(CreateURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 203}, MoreHeaders: map[string]string{"X-Auth-Token": ""}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get validates and retrieves information for user's token. func Get(client *gophercloud.ServiceClient, token string) (r GetResult) { resp, err := client.Get(GetURL(client, token), &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 203}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } golang-github-gophercloud-gophercloud-0.12.0/openstack/identity/v2/tokens/results.go000066400000000000000000000123261367513235700306650ustar00rootroot00000000000000package 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 } // ExtractTokenID implements the gophercloud.AuthResult interface. The returned // string is the same as the ID field of the Token struct returned from // ExtractToken(). func (r CreateResult) ExtractTokenID() (string, error) { var s struct { Access struct { Token struct { ID string `json:"id"` } `json:"token"` } `json:"access"` } err := r.ExtractInto(&s) return s.Access.Token.ID, err } // 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.12.0/openstack/identity/v2/tokens/testing/000077500000000000000000000000001367513235700303065ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/identity/v2/tokens/testing/doc.go000066400000000000000000000000451367513235700314010ustar00rootroot00000000000000// tokens unit tests package testing golang-github-gophercloud-gophercloud-0.12.0/openstack/identity/v2/tokens/testing/fixtures.go000066400000000000000000000123421367513235700325100ustar00rootroot00000000000000package 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) } golang-github-gophercloud-gophercloud-0.12.0/openstack/identity/v2/tokens/testing/requests_test.go000066400000000000000000000045021367513235700335500ustar00rootroot00000000000000package 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.12.0/openstack/identity/v2/tokens/urls.go000066400000000000000000000006011367513235700301420ustar00rootroot00000000000000package 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.12.0/openstack/identity/v2/users/000077500000000000000000000000001367513235700264675ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/identity/v2/users/doc.go000066400000000000000000000027101367513235700275630ustar00rootroot00000000000000/* 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 golang-github-gophercloud-gophercloud-0.12.0/openstack/identity/v2/users/requests.go000066400000000000000000000077531367513235700307050ustar00rootroot00000000000000package 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 } resp, err := client.Post(rootURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get requests details on a single user, either by ID or Name. func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { resp, err := client.Get(ResourceURL(client, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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 } resp, err := client.Put(ResourceURL(client, id), &b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete is the operation responsible for permanently deleting a User. func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { resp, err := client.Delete(ResourceURL(client, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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)} }) } golang-github-gophercloud-gophercloud-0.12.0/openstack/identity/v2/users/results.go000066400000000000000000000054611367513235700305250ustar00rootroot00000000000000package 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.12.0/openstack/identity/v2/users/testing/000077500000000000000000000000001367513235700301445ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/identity/v2/users/testing/doc.go000066400000000000000000000000441367513235700312360ustar00rootroot00000000000000// users unit tests package testing golang-github-gophercloud-gophercloud-0.12.0/openstack/identity/v2/users/testing/fixtures.go000066400000000000000000000071361367513235700323530ustar00rootroot00000000000000package 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" } ] } `) }) } golang-github-gophercloud-gophercloud-0.12.0/openstack/identity/v2/users/testing/requests_test.go000066400000000000000000000066011367513235700334100ustar00rootroot00000000000000package 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.12.0/openstack/identity/v2/users/urls.go000066400000000000000000000007451367513235700300110ustar00rootroot00000000000000package 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.12.0/openstack/identity/v3/000077500000000000000000000000001367513235700253275ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/identity/v3/applicationcredentials/000077500000000000000000000000001367513235700320505ustar00rootroot00000000000000requests.go000066400000000000000000000120441367513235700341740ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/identity/v3/applicationcredentialspackage applicationcredentials import ( "time" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // ListOptsBuilder allows extensions to add additional parameters to // the List request type ListOptsBuilder interface { ToApplicationCredentialListQuery() (string, error) } // ListOpts provides options to filter the List results. type ListOpts struct { // Name filters the response by an application credential name Name string `q:"name"` } // ToApplicationCredentialListQuery formats a ListOpts into a query string. func (opts ListOpts) ToApplicationCredentialListQuery() (string, error) { q, err := gophercloud.BuildQueryString(opts) return q.String(), err } // List enumerates the ApplicationCredentials to which the current token has access. func List(client *gophercloud.ServiceClient, userID string, opts ListOptsBuilder) pagination.Pager { url := listURL(client, userID) if opts != nil { query, err := opts.ToApplicationCredentialListQuery() if err != nil { return pagination.Pager{Err: err} } url += query } return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { return ApplicationCredentialPage{pagination.LinkedPageBase{PageResult: r}} }) } // Get retrieves details on a single user, by ID. func Get(client *gophercloud.ServiceClient, userID string, id string) (r GetResult) { resp, err := client.Get(getURL(client, userID, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // CreateOptsBuilder allows extensions to add additional parameters to // the Create request. type CreateOptsBuilder interface { ToApplicationCredentialCreateMap() (map[string]interface{}, error) } // CreateOpts provides options used to create an application credential. type CreateOpts struct { // The name of the application credential. Name string `json:"name,omitempty" required:"true"` // A description of the application credential’s purpose. Description string `json:"description,omitempty"` // A flag indicating whether the application credential may be used for creation or destruction of other application credentials or trusts. // Defaults to false Unrestricted bool `json:"unrestricted"` // The secret for the application credential, either generated by the server or provided by the user. // This is only ever shown once in the response to a create request. It is not stored nor ever shown again. // If the secret is lost, a new application credential must be created. Secret string `json:"secret,omitempty"` // A list of one or more roles that this application credential has associated with its project. // A token using this application credential will have these same roles. Roles []Role `json:"roles,omitempty"` // A list of access rules objects. AccessRules []AccessRule `json:"access_rules,omitempty"` // The expiration time of the application credential, if one was specified. ExpiresAt *time.Time `json:"-"` } // ToApplicationCredentialCreateMap formats a CreateOpts into a create request. func (opts CreateOpts) ToApplicationCredentialCreateMap() (map[string]interface{}, error) { parent := "application_credential" b, err := gophercloud.BuildRequestBody(opts, parent) if err != nil { return nil, err } if opts.ExpiresAt != nil { if v, ok := b[parent].(map[string]interface{}); ok { v["expires_at"] = opts.ExpiresAt.Format(gophercloud.RFC3339MilliNoZ) } } return b, nil } // Create creates a new ApplicationCredential. func Create(client *gophercloud.ServiceClient, userID string, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToApplicationCredentialCreateMap() if err != nil { r.Err = err return } resp, err := client.Post(createURL(client, userID), &b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{201}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete deletes an application credential. func Delete(client *gophercloud.ServiceClient, userID string, id string) (r DeleteResult) { resp, err := client.Delete(deleteURL(client, userID, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // ListAccessRules enumerates the AccessRules to which the current user has access. func ListAccessRules(client *gophercloud.ServiceClient, userID string) pagination.Pager { url := listAccessRulesURL(client, userID) return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { return AccessRulePage{pagination.LinkedPageBase{PageResult: r}} }) } // GetAccessRule retrieves details on a single access rule by ID. func GetAccessRule(client *gophercloud.ServiceClient, userID string, id string) (r GetAccessRuleResult) { resp, err := client.Get(getAccessRuleURL(client, userID, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // DeleteAccessRule deletes an access rule. func DeleteAccessRule(client *gophercloud.ServiceClient, userID string, id string) (r DeleteResult) { resp, err := client.Delete(deleteAccessRuleURL(client, userID, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } golang-github-gophercloud-gophercloud-0.12.0/openstack/identity/v3/applicationcredentials/results.go000066400000000000000000000144241367513235700341050ustar00rootroot00000000000000package applicationcredentials import ( "encoding/json" "time" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) type Role struct { // DomainID is the domain ID the role belongs to. DomainID string `json:"domain_id,omitempty"` // ID is the unique ID of the role. ID string `json:"id,omitempty"` // Name is the role name Name string `json:"name,omitempty"` } // ApplicationCredential represents the access rule object type AccessRule struct { // The ID of the access rule ID string `json:"id,omitempty"` // The API path that the application credential is permitted to access Path string `json:"path,omitempty"` // The request method that the application credential is permitted to use for a // given API endpoint Method string `json:"method,omitempty"` // The service type identifier for the service that the application credential // is permitted to access Service string `json:"service,omitempty"` } // ApplicationCredential represents the application credential object type ApplicationCredential struct { // The ID of the application credential. ID string `json:"id"` // The name of the application credential. Name string `json:"name"` // A description of the application credential’s purpose. Description string `json:"description"` // A flag indicating whether the application credential may be used for creation or destruction of other application credentials or trusts. // Defaults to false Unrestricted bool `json:"unrestricted"` // The secret for the application credential, either generated by the server or provided by the user. // This is only ever shown once in the response to a create request. It is not stored nor ever shown again. // If the secret is lost, a new application credential must be created. Secret string `json:"secret"` // The ID of the project the application credential was created for and that authentication requests using this application credential will be scoped to. ProjectID string `json:"project_id"` // A list of one or more roles that this application credential has associated with its project. // A token using this application credential will have these same roles. Roles []Role `json:"roles"` // The expiration time of the application credential, if one was specified. ExpiresAt time.Time `json:"-"` // A list of access rules objects. AccessRules []AccessRule `json:"access_rules,omitempty"` // Links contains referencing links to the application credential. Links map[string]interface{} `json:"links"` } func (r *ApplicationCredential) UnmarshalJSON(b []byte) error { type tmp ApplicationCredential var s struct { tmp ExpiresAt gophercloud.JSONRFC3339MilliNoZ `json:"expires_at"` } err := json.Unmarshal(b, &s) if err != nil { return err } *r = ApplicationCredential(s.tmp) r.ExpiresAt = time.Time(s.ExpiresAt) return nil } type applicationCredentialResult struct { gophercloud.Result } // GetResult is the response from a Get operation. Call its Extract method // to interpret it as an ApplicationCredential. type GetResult struct { applicationCredentialResult } // CreateResult is the response from a Create operation. Call its Extract method // to interpret it as an ApplicationCredential. type CreateResult struct { applicationCredentialResult } // DeleteResult is the response from a Delete operation. Call its ExtractErr to // determine if the request succeeded or failed. type DeleteResult struct { gophercloud.ErrResult } // an ApplicationCredentialPage is a single page of an ApplicationCredential results. type ApplicationCredentialPage struct { pagination.LinkedPageBase } // IsEmpty determines whether or not a an ApplicationCredentialPage contains any results. func (r ApplicationCredentialPage) IsEmpty() (bool, error) { applicationCredentials, err := ExtractApplicationCredentials(r) return len(applicationCredentials) == 0, err } // NextPageURL extracts the "next" link from the links section of the result. func (r ApplicationCredentialPage) 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 } // Extractan ApplicationCredentials returns a slice of ApplicationCredentials contained in a single page of results. func ExtractApplicationCredentials(r pagination.Page) ([]ApplicationCredential, error) { var s struct { ApplicationCredentials []ApplicationCredential `json:"application_credentials"` } err := (r.(ApplicationCredentialPage)).ExtractInto(&s) return s.ApplicationCredentials, err } // Extract interprets any application_credential results as an ApplicationCredential. func (r applicationCredentialResult) Extract() (*ApplicationCredential, error) { var s struct { ApplicationCredential *ApplicationCredential `json:"application_credential"` } err := r.ExtractInto(&s) return s.ApplicationCredential, err } // GetAccessRuleResult is the response from a Get operation. Call its Extract method // to interpret it as an AccessRule. type GetAccessRuleResult struct { gophercloud.Result } // an AccessRulePage is a single page of an AccessRule results. type AccessRulePage struct { pagination.LinkedPageBase } // IsEmpty determines whether or not a an AccessRulePage contains any results. func (r AccessRulePage) IsEmpty() (bool, error) { accessRules, err := ExtractAccessRules(r) return len(accessRules) == 0, err } // NextPageURL extracts the "next" link from the links section of the result. func (r AccessRulePage) 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 } // ExtractAccessRules returns a slice of AccessRules contained in a single page of results. func ExtractAccessRules(r pagination.Page) ([]AccessRule, error) { var s struct { AccessRules []AccessRule `json:"access_rules"` } err := (r.(AccessRulePage)).ExtractInto(&s) return s.AccessRules, err } // Extract interprets any access_rule results as an AccessRule. func (r GetAccessRuleResult) Extract() (*AccessRule, error) { var s struct { AccessRule *AccessRule `json:"access_rule"` } err := r.ExtractInto(&s) return s.AccessRule, err } golang-github-gophercloud-gophercloud-0.12.0/openstack/identity/v3/applicationcredentials/testing/000077500000000000000000000000001367513235700335255ustar00rootroot00000000000000fixtures.go000066400000000000000000000372441367513235700356600ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/identity/v3/applicationcredentials/testingpackage testing import ( "fmt" "net/http" "testing" "time" "github.com/gophercloud/gophercloud/openstack/identity/v3/applicationcredentials" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) const userID = "2844b2a08be147a08ef58317d6471f1f" const applicationCredentialID = "f741662395b249c9b8acdebf1722c5ae" const accessRuleID = "07d719df00f349ef8de77d542edf010c" // ListOutput provides a single page of ApplicationCredential results. const ListOutput = ` { "links": { "self": "https://identity/v3/users/2844b2a08be147a08ef58317d6471f1f/application_credentials", "previous": null, "next": null }, "application_credentials": [ { "links": { "self": "https://identity/v3/users/2844b2a08be147a08ef58317d6471f1f/application_credentials/c4859fb437df4b87a51a8f5adcfb0bc7" }, "description": null, "roles": [ { "id": "31f87923ae4a4d119aa0b85dcdbeed13", "domain_id": null, "name": "compute_viewer" } ], "expires_at": null, "unrestricted": false, "project_id": "53c2b94f63fb4f43a21b92d119ce549f", "id": "c4859fb437df4b87a51a8f5adcfb0bc7", "name": "test1" }, { "links": { "self": "https://identity/v3/users/2844b2a08be147a08ef58317d6471f1f/application_credentials/6b8cc7647da64166a4a3cc0c88ebbabb" }, "description": null, "roles": [ { "id": "31f87923ae4a4d119aa0b85dcdbeed13", "domain_id": null, "name": "compute_viewer" }, { "id": "4494bc5bea1a4105ad7fbba6a7eb9ef4", "domain_id": null, "name": "network_viewer" } ], "expires_at": "2019-03-12T12:12:12.123456", "unrestricted": true, "project_id": "53c2b94f63fb4f43a21b92d119ce549f", "id": "6b8cc7647da64166a4a3cc0c88ebbabb", "name": "test2" } ] } ` // GetOutput provides a Get result. const GetOutput = ` { "application_credential": { "links": { "self": "https://identity/v3/users/2844b2a08be147a08ef58317d6471f1f/application_credentials/f741662395b249c9b8acdebf1722c5ae" }, "description": null, "roles": [ { "id": "31f87923ae4a4d119aa0b85dcdbeed13", "domain_id": null, "name": "compute_viewer" } ], "access_rules": [ { "path": "/v2.0/metrics", "id": "07d719df00f349ef8de77d542edf010c", "service": "monitoring", "method": "GET" } ], "expires_at": null, "unrestricted": false, "project_id": "53c2b94f63fb4f43a21b92d119ce549f", "id": "f741662395b249c9b8acdebf1722c5ae", "name": "test" } } ` // CreateRequest provides the input to a Create request. const CreateRequest = ` { "application_credential": { "secret": "mysecret", "unrestricted": false, "roles": [ { "id": "31f87923ae4a4d119aa0b85dcdbeed13" } ], "access_rules": [ { "path": "/v2.0/metrics", "method": "GET", "service": "monitoring" } ], "name": "test" } } ` const CreateResponse = ` { "application_credential": { "links": { "self": "https://identity/v3/users/2844b2a08be147a08ef58317d6471f1f/application_credentials/f741662395b249c9b8acdebf1722c5ae" }, "description": null, "roles": [ { "id": "31f87923ae4a4d119aa0b85dcdbeed13", "domain_id": null, "name": "compute_viewer" } ], "access_rules": [ { "path": "/v2.0/metrics", "id": "07d719df00f349ef8de77d542edf010c", "service": "monitoring", "method": "GET" } ], "expires_at": null, "secret": "mysecret", "unrestricted": false, "project_id": "53c2b94f63fb4f43a21b92d119ce549f", "id": "f741662395b249c9b8acdebf1722c5ae", "name": "test" } } ` // CreateNoOptionsRequest provides the input to a Create request with no Secret. const CreateNoSecretRequest = ` { "application_credential": { "unrestricted": false, "name": "test1", "roles": [ { "id": "31f87923ae4a4d119aa0b85dcdbeed13" } ] } } ` const CreateNoSecretResponse = ` { "application_credential": { "links": { "self": "https://identity/v3/users/2844b2a08be147a08ef58317d6471f1f/application_credentials/c4859fb437df4b87a51a8f5adcfb0bc7" }, "description": null, "roles": [ { "id": "31f87923ae4a4d119aa0b85dcdbeed13", "domain_id": null, "name": "compute_viewer" } ], "expires_at": null, "secret": "generated_secret", "unrestricted": false, "project_id": "53c2b94f63fb4f43a21b92d119ce549f", "id": "c4859fb437df4b87a51a8f5adcfb0bc7", "name": "test1" } } ` const CreateUnrestrictedRequest = ` { "application_credential": { "unrestricted": true, "roles": [ { "id": "31f87923ae4a4d119aa0b85dcdbeed13" }, { "id": "4494bc5bea1a4105ad7fbba6a7eb9ef4" } ], "expires_at": "2019-03-12T12:12:12.123456", "name": "test2" } } ` const CreateUnrestrictedResponse = ` { "application_credential": { "links": { "self": "https://identity/v3/users/2844b2a08be147a08ef58317d6471f1f/application_credentials/6b8cc7647da64166a4a3cc0c88ebbabb" }, "description": null, "roles": [ { "id": "31f87923ae4a4d119aa0b85dcdbeed13", "domain_id": null, "name": "compute_viewer" }, { "id": "4494bc5bea1a4105ad7fbba6a7eb9ef4", "domain_id": null, "name": "network_viewer" } ], "expires_at": "2019-03-12T12:12:12.123456", "secret": "generated_secret", "unrestricted": true, "project_id": "53c2b94f63fb4f43a21b92d119ce549f", "id": "6b8cc7647da64166a4a3cc0c88ebbabb", "name": "test2" } } ` // ListAccessRulesOutput provides a single page of AccessRules results. const ListAccessRulesOutput = ` { "links": { "self": "https://example.com/identity/v3/users/2844b2a08be147a08ef58317d6471f1f/access_rules", "previous": null, "next": null }, "access_rules": [ { "path": "/v2.0/metrics", "links": { "self": "https://example.com/identity/v3/access_rules/07d719df00f349ef8de77d542edf010c" }, "id": "07d719df00f349ef8de77d542edf010c", "service": "monitoring", "method": "GET" } ] }` // GetAccessRuleOutput provides a Get result. const GetAccessRuleOutput = ` { "access_rule": { "path": "/v2.0/metrics", "links": { "self": "https://example.com/identity/v3/access_rules/07d719df00f349ef8de77d542edf010c" }, "id": "07d719df00f349ef8de77d542edf010c", "service": "monitoring", "method": "GET" } } ` var nilTime time.Time var ApplicationCredential = applicationcredentials.ApplicationCredential{ ID: "f741662395b249c9b8acdebf1722c5ae", Name: "test", Description: "", Unrestricted: false, Secret: "", ProjectID: "53c2b94f63fb4f43a21b92d119ce549f", Roles: []applicationcredentials.Role{ applicationcredentials.Role{ ID: "31f87923ae4a4d119aa0b85dcdbeed13", Name: "compute_viewer", }, }, AccessRules: []applicationcredentials.AccessRule{ { ID: "07d719df00f349ef8de77d542edf010c", Path: "/v2.0/metrics", Method: "GET", Service: "monitoring", }, }, ExpiresAt: nilTime, Links: map[string]interface{}{ "self": "https://identity/v3/users/2844b2a08be147a08ef58317d6471f1f/application_credentials/f741662395b249c9b8acdebf1722c5ae", }, } var ApplicationCredentialNoSecretResponse = applicationcredentials.ApplicationCredential{ ID: "c4859fb437df4b87a51a8f5adcfb0bc7", Name: "test1", Description: "", Unrestricted: false, Secret: "generated_secret", ProjectID: "53c2b94f63fb4f43a21b92d119ce549f", Roles: []applicationcredentials.Role{ applicationcredentials.Role{ ID: "31f87923ae4a4d119aa0b85dcdbeed13", Name: "compute_viewer", }, }, ExpiresAt: nilTime, Links: map[string]interface{}{ "self": "https://identity/v3/users/2844b2a08be147a08ef58317d6471f1f/application_credentials/c4859fb437df4b87a51a8f5adcfb0bc7", }, } var ApplationCredentialExpiresAt = time.Date(2019, 3, 12, 12, 12, 12, 123456000, time.UTC) var UnrestrictedApplicationCredential = applicationcredentials.ApplicationCredential{ ID: "6b8cc7647da64166a4a3cc0c88ebbabb", Name: "test2", Description: "", Unrestricted: true, Secret: "", ProjectID: "53c2b94f63fb4f43a21b92d119ce549f", Roles: []applicationcredentials.Role{ applicationcredentials.Role{ ID: "31f87923ae4a4d119aa0b85dcdbeed13", Name: "compute_viewer", }, applicationcredentials.Role{ ID: "4494bc5bea1a4105ad7fbba6a7eb9ef4", Name: "network_viewer", }, }, ExpiresAt: ApplationCredentialExpiresAt, Links: map[string]interface{}{ "self": "https://identity/v3/users/2844b2a08be147a08ef58317d6471f1f/application_credentials/6b8cc7647da64166a4a3cc0c88ebbabb", }, } var FirstApplicationCredential = applicationcredentials.ApplicationCredential{ ID: "c4859fb437df4b87a51a8f5adcfb0bc7", Name: "test1", Description: "", Unrestricted: false, Secret: "", ProjectID: "53c2b94f63fb4f43a21b92d119ce549f", Roles: []applicationcredentials.Role{ applicationcredentials.Role{ ID: "31f87923ae4a4d119aa0b85dcdbeed13", Name: "compute_viewer", }, }, ExpiresAt: nilTime, Links: map[string]interface{}{ "self": "https://identity/v3/users/2844b2a08be147a08ef58317d6471f1f/application_credentials/c4859fb437df4b87a51a8f5adcfb0bc7", }, } var SecondApplicationCredential = applicationcredentials.ApplicationCredential{ ID: "6b8cc7647da64166a4a3cc0c88ebbabb", Name: "test2", Description: "", Unrestricted: true, Secret: "", ProjectID: "53c2b94f63fb4f43a21b92d119ce549f", Roles: []applicationcredentials.Role{ applicationcredentials.Role{ ID: "31f87923ae4a4d119aa0b85dcdbeed13", Name: "compute_viewer", }, applicationcredentials.Role{ ID: "4494bc5bea1a4105ad7fbba6a7eb9ef4", Name: "network_viewer", }, }, ExpiresAt: ApplationCredentialExpiresAt, Links: map[string]interface{}{ "self": "https://identity/v3/users/2844b2a08be147a08ef58317d6471f1f/application_credentials/6b8cc7647da64166a4a3cc0c88ebbabb", }, } var AccessRule = applicationcredentials.AccessRule{ Path: "/v2.0/metrics", ID: "07d719df00f349ef8de77d542edf010c", Service: "monitoring", Method: "GET", } var ExpectedAccessRulesSlice = []applicationcredentials.AccessRule{ AccessRule, } // ExpectedApplicationCredentialsSlice is the slice of application credentials expected to be returned from ListOutput. var ExpectedApplicationCredentialsSlice = []applicationcredentials.ApplicationCredential{FirstApplicationCredential, SecondApplicationCredential} // HandleListApplicationCredentialsSuccessfully creates an HTTP handler at `/users` on the // test handler mux that responds with a list of two applicationcredentials. func HandleListApplicationCredentialsSuccessfully(t *testing.T) { th.Mux.HandleFunc("/users/2844b2a08be147a08ef58317d6471f1f/application_credentials", 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) }) } // HandleGetApplicationCredentialSuccessfully creates an HTTP handler at `/users` on the // test handler mux that responds with a single application credential. func HandleGetApplicationCredentialSuccessfully(t *testing.T) { th.Mux.HandleFunc("/users/2844b2a08be147a08ef58317d6471f1f/application_credentials/f741662395b249c9b8acdebf1722c5ae", 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) }) } // HandleCreateApplicationCredentialSuccessfully creates an HTTP handler at `/users` on the // test handler mux that tests application credential creation. func HandleCreateApplicationCredentialSuccessfully(t *testing.T) { th.Mux.HandleFunc("/users/2844b2a08be147a08ef58317d6471f1f/application_credentials", 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) }) } // HandleCreateNoOptionsApplicationCredentialSuccessfully creates an HTTP handler at `/users` on the // test handler mux that tests application credential creation. func HandleCreateNoSecretApplicationCredentialSuccessfully(t *testing.T) { th.Mux.HandleFunc("/users/2844b2a08be147a08ef58317d6471f1f/application_credentials", 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, CreateNoSecretRequest) w.WriteHeader(http.StatusCreated) fmt.Fprintf(w, CreateNoSecretResponse) }) } func HandleCreateUnrestrictedApplicationCredentialSuccessfully(t *testing.T) { th.Mux.HandleFunc("/users/2844b2a08be147a08ef58317d6471f1f/application_credentials", 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, CreateUnrestrictedRequest) w.WriteHeader(http.StatusCreated) fmt.Fprintf(w, CreateUnrestrictedResponse) }) } // HandleDeleteApplicationCredentialSuccessfully creates an HTTP handler at `/users` on the // test handler mux that tests application credential deletion. func HandleDeleteApplicationCredentialSuccessfully(t *testing.T) { th.Mux.HandleFunc("/users/2844b2a08be147a08ef58317d6471f1f/application_credentials/f741662395b249c9b8acdebf1722c5ae", 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) }) } // HandleListAccessRulesSuccessfully creates an HTTP handler at `/users` on the // test handler mux that responds with a list of two applicationcredentials. func HandleListAccessRulesSuccessfully(t *testing.T) { th.Mux.HandleFunc("/users/2844b2a08be147a08ef58317d6471f1f/access_rules", 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, ListAccessRulesOutput) }) } // HandleGetAccessRuleSuccessfully creates an HTTP handler at `/users` on the // test handler mux that responds with a single application credential. func HandleGetAccessRuleSuccessfully(t *testing.T) { th.Mux.HandleFunc("/users/2844b2a08be147a08ef58317d6471f1f/access_rules/07d719df00f349ef8de77d542edf010c", 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, GetAccessRuleOutput) }) } // HandleDeleteAccessRuleSuccessfully creates an HTTP handler at `/users` on the // test handler mux that tests application credential deletion. func HandleDeleteAccessRuleSuccessfully(t *testing.T) { th.Mux.HandleFunc("/users/2844b2a08be147a08ef58317d6471f1f/access_rules/07d719df00f349ef8de77d542edf010c", 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.go000066400000000000000000000127051367513235700367140ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/identity/v3/applicationcredentials/testingpackage testing import ( "testing" "github.com/gophercloud/gophercloud/openstack/identity/v3/applicationcredentials" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) func TestListApplicationCredentials(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleListApplicationCredentialsSuccessfully(t) count := 0 err := applicationcredentials.List(client.ServiceClient(), userID, nil).EachPage(func(page pagination.Page) (bool, error) { count++ actual, err := applicationcredentials.ExtractApplicationCredentials(page) th.AssertNoErr(t, err) th.CheckDeepEquals(t, ExpectedApplicationCredentialsSlice, actual) return true, nil }) th.AssertNoErr(t, err) th.CheckEquals(t, count, 1) } func TestListApplicationCredentialsAllPages(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleListApplicationCredentialsSuccessfully(t) allPages, err := applicationcredentials.List(client.ServiceClient(), userID, nil).AllPages() th.AssertNoErr(t, err) actual, err := applicationcredentials.ExtractApplicationCredentials(allPages) th.AssertNoErr(t, err) th.CheckDeepEquals(t, ExpectedApplicationCredentialsSlice, actual) th.AssertDeepEquals(t, ExpectedApplicationCredentialsSlice[0].Roles, []applicationcredentials.Role{{ID: "31f87923ae4a4d119aa0b85dcdbeed13", Name: "compute_viewer"}}) th.AssertDeepEquals(t, ExpectedApplicationCredentialsSlice[1].Roles, []applicationcredentials.Role{applicationcredentials.Role{ID: "31f87923ae4a4d119aa0b85dcdbeed13", Name: "compute_viewer"}, applicationcredentials.Role{ID: "4494bc5bea1a4105ad7fbba6a7eb9ef4", Name: "network_viewer"}}) } func TestGetApplicationCredential(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleGetApplicationCredentialSuccessfully(t) actual, err := applicationcredentials.Get(client.ServiceClient(), userID, applicationCredentialID).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, ApplicationCredential, *actual) } func TestCreateApplicationCredential(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleCreateApplicationCredentialSuccessfully(t) createOpts := applicationcredentials.CreateOpts{ Name: "test", Secret: "mysecret", Roles: []applicationcredentials.Role{ applicationcredentials.Role{ID: "31f87923ae4a4d119aa0b85dcdbeed13"}, }, AccessRules: []applicationcredentials.AccessRule{ { Path: "/v2.0/metrics", Method: "GET", Service: "monitoring", }, }, } ApplicationCredentialResponse := ApplicationCredential ApplicationCredentialResponse.Secret = "mysecret" actual, err := applicationcredentials.Create(client.ServiceClient(), userID, createOpts).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, ApplicationCredentialResponse, *actual) } func TestCreateNoSecretApplicationCredential(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleCreateNoSecretApplicationCredentialSuccessfully(t) createOpts := applicationcredentials.CreateOpts{ Name: "test1", Roles: []applicationcredentials.Role{ applicationcredentials.Role{ID: "31f87923ae4a4d119aa0b85dcdbeed13"}, }, } actual, err := applicationcredentials.Create(client.ServiceClient(), userID, createOpts).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, ApplicationCredentialNoSecretResponse, *actual) } func TestCreateUnrestrictedApplicationCredential(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleCreateUnrestrictedApplicationCredentialSuccessfully(t) createOpts := applicationcredentials.CreateOpts{ Name: "test2", Unrestricted: true, Roles: []applicationcredentials.Role{ applicationcredentials.Role{ID: "31f87923ae4a4d119aa0b85dcdbeed13"}, applicationcredentials.Role{ID: "4494bc5bea1a4105ad7fbba6a7eb9ef4"}, }, ExpiresAt: &ApplationCredentialExpiresAt, } UnrestrictedApplicationCredentialResponse := UnrestrictedApplicationCredential UnrestrictedApplicationCredentialResponse.Secret = "generated_secret" actual, err := applicationcredentials.Create(client.ServiceClient(), userID, createOpts).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, UnrestrictedApplicationCredentialResponse, *actual) } func TestDeleteApplicationCredential(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleDeleteApplicationCredentialSuccessfully(t) res := applicationcredentials.Delete(client.ServiceClient(), userID, applicationCredentialID) th.AssertNoErr(t, res.Err) } func TestListAccessRules(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleListAccessRulesSuccessfully(t) count := 0 err := applicationcredentials.ListAccessRules(client.ServiceClient(), userID).EachPage(func(page pagination.Page) (bool, error) { count++ actual, err := applicationcredentials.ExtractAccessRules(page) th.AssertNoErr(t, err) th.CheckDeepEquals(t, ExpectedAccessRulesSlice, actual) return true, nil }) th.AssertNoErr(t, err) th.CheckEquals(t, count, 1) } func TestGetAccessRule(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleGetAccessRuleSuccessfully(t) actual, err := applicationcredentials.GetAccessRule(client.ServiceClient(), userID, accessRuleID).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, AccessRule, *actual) } func TestDeleteAccessRule(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleDeleteAccessRuleSuccessfully(t) res := applicationcredentials.DeleteAccessRule(client.ServiceClient(), userID, accessRuleID) th.AssertNoErr(t, res.Err) } golang-github-gophercloud-gophercloud-0.12.0/openstack/identity/v3/applicationcredentials/urls.go000066400000000000000000000022011367513235700333570ustar00rootroot00000000000000package applicationcredentials import "github.com/gophercloud/gophercloud" func listURL(client *gophercloud.ServiceClient, userID string) string { return client.ServiceURL("users", userID, "application_credentials") } func getURL(client *gophercloud.ServiceClient, userID string, id string) string { return client.ServiceURL("users", userID, "application_credentials", id) } func createURL(client *gophercloud.ServiceClient, userID string) string { return client.ServiceURL("users", userID, "application_credentials") } func deleteURL(client *gophercloud.ServiceClient, userID string, id string) string { return client.ServiceURL("users", userID, "application_credentials", id) } func listAccessRulesURL(client *gophercloud.ServiceClient, userID string) string { return client.ServiceURL("users", userID, "access_rules") } func getAccessRuleURL(client *gophercloud.ServiceClient, userID string, id string) string { return client.ServiceURL("users", userID, "access_rules", id) } func deleteAccessRuleURL(client *gophercloud.ServiceClient, userID string, id string) string { return client.ServiceURL("users", userID, "access_rules", id) } golang-github-gophercloud-gophercloud-0.12.0/openstack/identity/v3/credentials/000077500000000000000000000000001367513235700276245ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/identity/v3/credentials/requests.go000066400000000000000000000100331367513235700320230ustar00rootroot00000000000000package credentials import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // ListOptsBuilder allows extensions to add additional parameters to // the List request type ListOptsBuilder interface { ToCredentialListQuery() (string, error) } // ListOpts provides options to filter the List results. type ListOpts struct { // UserID filters the response by a credential user_id UserID string `q:"user_id"` // Type filters the response by a credential type Type string `q:"type"` } // ToCredentialListQuery formats a ListOpts into a query string. func (opts ListOpts) ToCredentialListQuery() (string, error) { q, err := gophercloud.BuildQueryString(opts) return q.String(), err } // List enumerates the Credentials 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.ToCredentialListQuery() if err != nil { return pagination.Pager{Err: err} } url += query } return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { return CredentialPage{pagination.LinkedPageBase{PageResult: r}} }) } // Get retrieves details on a single user, by ID. func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { resp, err := client.Get(getURL(client, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // CreateOptsBuilder allows extensions to add additional parameters to // the Create request. type CreateOptsBuilder interface { ToCredentialCreateMap() (map[string]interface{}, error) } // CreateOpts provides options used to create a credential. type CreateOpts struct { // Serialized blob containing the credentials Blob string `json:"blob" required:"true"` // ID of the project. ProjectID string `json:"project_id,omitempty"` // The type of the credential. Type string `json:"type" required:"true"` // ID of the user who owns the credential. UserID string `json:"user_id" required:"true"` } // ToCredentialCreateMap formats a CreateOpts into a create request. func (opts CreateOpts) ToCredentialCreateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "credential") } // Create creates a new Credential. func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToCredentialCreateMap() if err != nil { r.Err = err return } resp, err := client.Post(createURL(client), &b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{201}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete deletes a credential. func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { resp, err := client.Delete(deleteURL(client, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // UpdateOptsBuilder allows extensions to add additional parameters to // the Update request. type UpdateOptsBuilder interface { ToCredentialsUpdateMap() (map[string]interface{}, error) } // UpdateOpts represents parameters to update a credential. type UpdateOpts struct { // Serialized blob containing the credentials. Blob string `json:"blob,omitempty"` // ID of the project. ProjectID string `json:"project_id,omitempty"` // The type of the credential. Type string `json:"type,omitempty"` // ID of the user who owns the credential. UserID string `json:"user_id,omitempty"` } // ToUpdateCreateMap formats a UpdateOpts into an update request. func (opts UpdateOpts) ToCredentialsUpdateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "credential") } // Update modifies the attributes of a Credential. func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToCredentialsUpdateMap() if err != nil { r.Err = err return } resp, err := client.Patch(updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } golang-github-gophercloud-gophercloud-0.12.0/openstack/identity/v3/credentials/results.go000066400000000000000000000050201367513235700316510ustar00rootroot00000000000000package credentials import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // Credential represents the Credential object type Credential struct { // The ID of the credential. ID string `json:"id"` // Serialized Blob Credential. Blob string `json:"blob"` // ID of the user who owns the credential. UserID string `json:"user_id"` // The type of the credential. Type string `json:"type"` // The ID of the project the credential was created for. ProjectID string `json:"project_id"` // Links contains referencing links to the credential. Links map[string]interface{} `json:"links"` } type credentialResult struct { gophercloud.Result } // GetResult is the response from a Get operation. Call its Extract method // to interpret it as a Credential. type GetResult struct { credentialResult } // CreateResult is the response from a Create operation. Call its Extract method // to interpret it as a Credential. type CreateResult struct { credentialResult } // 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 Credential type UpdateResult struct { credentialResult } // a CredentialPage is a single page of a Credential results. type CredentialPage struct { pagination.LinkedPageBase } // IsEmpty determines whether or not a CredentialPage contains any results. func (r CredentialPage) IsEmpty() (bool, error) { credentials, err := ExtractCredentials(r) return len(credentials) == 0, err } // NextPageURL extracts the "next" link from the links section of the result. func (r CredentialPage) 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 } // Extract a Credential returns a slice of Credentials contained in a single page of results. func ExtractCredentials(r pagination.Page) ([]Credential, error) { var s struct { Credentials []Credential `json:"credentials"` } err := (r.(CredentialPage)).ExtractInto(&s) return s.Credentials, err } // Extract interprets any credential results as a Credential. func (r credentialResult) Extract() (*Credential, error) { var s struct { Credential *Credential `json:"credential"` } err := r.ExtractInto(&s) return s.Credential, err } golang-github-gophercloud-gophercloud-0.12.0/openstack/identity/v3/credentials/testing/000077500000000000000000000000001367513235700313015ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/identity/v3/credentials/testing/fixtures.go000066400000000000000000000172761367513235700335160ustar00rootroot00000000000000package testing import ( "fmt" "net/http" "testing" "github.com/gophercloud/gophercloud/openstack/identity/v3/credentials" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) const userID = "bb5476fd12884539b41d5a88f838d773" const credentialID = "3d3367228f9c7665266604462ec60029bcd83ad89614021a80b2eb879c572510" const projectID = "731fc6f265cd486d900f16e84c5cb594" // ListOutput provides a single page of Credential results. const ListOutput = ` { "credentials": [ { "user_id": "bb5476fd12884539b41d5a88f838d773", "links": { "self": "http://identity/v3/credentials/3d3367228f9c7665266604462ec60029bcd83ad89614021a80b2eb879c572510" }, "blob": "{\"access\":\"181920\",\"secret\":\"secretKey\"}", "project_id": "731fc6f265cd486d900f16e84c5cb594", "type": "ec2", "id": "3d3367228f9c7665266604462ec60029bcd83ad89614021a80b2eb879c572510" }, { "user_id": "6f556708d04b4ea6bc72d7df2296b71a", "links": { "self": "http://identity/v3/credentials/2441494e52ab6d594a34d74586075cb299489bdd1e9389e3ab06467a4f460609" }, "blob": "{\"access\":\"7da79ff0aa364e1396f067e352b9b79a\",\"secret\":\"7a18d68ba8834b799d396f3ff6f1e98c\"}", "project_id": "1a1d14690f3c4ec5bf5f321c5fde3c16", "type": "ec2", "id": "2441494e52ab6d594a34d74586075cb299489bdd1e9389e3ab06467a4f460609" } ], "links": { "self": "http://identity/v3/credentials", "previous": null, "next": null } } ` // GetOutput provides a Get result. const GetOutput = ` { "credential": { "user_id": "bb5476fd12884539b41d5a88f838d773", "links": { "self": "http://identity/v3/credentials/3d3367228f9c7665266604462ec60029bcd83ad89614021a80b2eb879c572510" }, "blob": "{\"access\":\"181920\",\"secret\":\"secretKey\"}", "project_id": "731fc6f265cd486d900f16e84c5cb594", "type": "ec2", "id": "3d3367228f9c7665266604462ec60029bcd83ad89614021a80b2eb879c572510" } } ` // CreateRequest provides the input to a Create request. const CreateRequest = ` { "credential": { "blob": "{\"access\":\"181920\",\"secret\":\"secretKey\"}", "project_id": "731fc6f265cd486d900f16e84c5cb594", "type": "ec2", "user_id": "bb5476fd12884539b41d5a88f838d773" } } ` // UpdateRequest provides the input to as Update request. const UpdateRequest = ` { "credential": { "blob": "{\"access\":\"181920\",\"secret\":\"secretKey\"}", "project_id": "731fc6f265cd486d900f16e84c5cb594", "type": "ec2", "user_id": "bb5476fd12884539b41d5a88f838d773" } } ` // UpdateOutput provides an update result. const UpdateOutput = ` { "credential": { "user_id": "bb5476fd12884539b41d5a88f838d773", "links": { "self": "http://identity/v3/credentials/2441494e52ab6d594a34d74586075cb299489bdd1e9389e3ab06467a4f460609" }, "blob": "{\"access\":\"181920\",\"secret\":\"secretKey\"}", "project_id": "731fc6f265cd486d900f16e84c5cb594", "type": "ec2", "id": "2441494e52ab6d594a34d74586075cb299489bdd1e9389e3ab06467a4f460609" } } ` var Credential = credentials.Credential{ ID: credentialID, ProjectID: projectID, Type: "ec2", UserID: userID, Blob: "{\"access\":\"181920\",\"secret\":\"secretKey\"}", Links: map[string]interface{}{ "self": "http://identity/v3/credentials/3d3367228f9c7665266604462ec60029bcd83ad89614021a80b2eb879c572510", }, } var FirstCredential = credentials.Credential{ ID: credentialID, ProjectID: projectID, Type: "ec2", UserID: userID, Blob: "{\"access\":\"181920\",\"secret\":\"secretKey\"}", Links: map[string]interface{}{ "self": "http://identity/v3/credentials/3d3367228f9c7665266604462ec60029bcd83ad89614021a80b2eb879c572510", }, } var SecondCredential = credentials.Credential{ ID: "2441494e52ab6d594a34d74586075cb299489bdd1e9389e3ab06467a4f460609", ProjectID: "1a1d14690f3c4ec5bf5f321c5fde3c16", Type: "ec2", UserID: "6f556708d04b4ea6bc72d7df2296b71a", Blob: "{\"access\":\"7da79ff0aa364e1396f067e352b9b79a\",\"secret\":\"7a18d68ba8834b799d396f3ff6f1e98c\"}", Links: map[string]interface{}{ "self": "http://identity/v3/credentials/2441494e52ab6d594a34d74586075cb299489bdd1e9389e3ab06467a4f460609", }, } // SecondCredentialUpdated is how SecondCredential should look after an Update. var SecondCredentialUpdated = credentials.Credential{ ID: "2441494e52ab6d594a34d74586075cb299489bdd1e9389e3ab06467a4f460609", ProjectID: projectID, Type: "ec2", UserID: userID, Blob: "{\"access\":\"181920\",\"secret\":\"secretKey\"}", Links: map[string]interface{}{ "self": "http://identity/v3/credentials/2441494e52ab6d594a34d74586075cb299489bdd1e9389e3ab06467a4f460609", }, } // ExpectedCredentialsSlice is the slice of credentials expected to be returned from ListOutput. var ExpectedCredentialsSlice = []credentials.Credential{FirstCredential, SecondCredential} // HandleListCredentialsSuccessfully creates an HTTP handler at `/credentials` on the // test handler mux that responds with a list of two credentials. func HandleListCredentialsSuccessfully(t *testing.T) { th.Mux.HandleFunc("/credentials", 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) }) } // HandleGetCredentialSuccessfully creates an HTTP handler at `/credentials` on the // test handler mux that responds with a single credential. func HandleGetCredentialSuccessfully(t *testing.T) { th.Mux.HandleFunc("/credentials/3d3367228f9c7665266604462ec60029bcd83ad89614021a80b2eb879c572510", 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) }) } // HandleCreateCredentialSuccessfully creates an HTTP handler at `/credentials` on the // test handler mux that tests credential creation. func HandleCreateCredentialSuccessfully(t *testing.T) { th.Mux.HandleFunc("/credentials", 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) }) } // HandleDeleteCredentialSuccessfully creates an HTTP handler at `/credentials` on the // test handler mux that tests credential deletion. func HandleDeleteCredentialSuccessfully(t *testing.T) { th.Mux.HandleFunc("/credentials/3d3367228f9c7665266604462ec60029bcd83ad89614021a80b2eb879c572510", 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) }) } // HandleUpdateCredentialsSuccessfully creates an HTTP handler at `/credentials` on the // test handler mux that tests credentials update. func HandleUpdateCredentialSuccessfully(t *testing.T) { th.Mux.HandleFunc("/credentials/2441494e52ab6d594a34d74586075cb299489bdd1e9389e3ab06467a4f460609", 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.go000066400000000000000000000055671367513235700345000ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/identity/v3/credentials/testingpackage testing import ( "testing" "github.com/gophercloud/gophercloud/openstack/identity/v3/credentials" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) func TestListCredentials(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleListCredentialsSuccessfully(t) count := 0 err := credentials.List(client.ServiceClient(), nil).EachPage(func(page pagination.Page) (bool, error) { count++ actual, err := credentials.ExtractCredentials(page) th.AssertNoErr(t, err) th.CheckDeepEquals(t, ExpectedCredentialsSlice, actual) return true, nil }) th.AssertNoErr(t, err) th.CheckEquals(t, count, 1) } func TestListCredentialsAllPages(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleListCredentialsSuccessfully(t) allPages, err := credentials.List(client.ServiceClient(), nil).AllPages() th.AssertNoErr(t, err) actual, err := credentials.ExtractCredentials(allPages) th.AssertNoErr(t, err) th.CheckDeepEquals(t, ExpectedCredentialsSlice, actual) th.AssertDeepEquals(t, ExpectedCredentialsSlice[0].Blob, "{\"access\":\"181920\",\"secret\":\"secretKey\"}") th.AssertDeepEquals(t, ExpectedCredentialsSlice[1].Blob, "{\"access\":\"7da79ff0aa364e1396f067e352b9b79a\",\"secret\":\"7a18d68ba8834b799d396f3ff6f1e98c\"}") } func TestGetCredential(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleGetCredentialSuccessfully(t) actual, err := credentials.Get(client.ServiceClient(), credentialID).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, Credential, *actual) } func TestCreateCredential(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleCreateCredentialSuccessfully(t) createOpts := credentials.CreateOpts{ ProjectID: projectID, Type: "ec2", UserID: userID, Blob: "{\"access\":\"181920\",\"secret\":\"secretKey\"}", } CredentialResponse := Credential actual, err := credentials.Create(client.ServiceClient(), createOpts).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, CredentialResponse, *actual) } func TestDeleteCredential(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleDeleteCredentialSuccessfully(t) res := credentials.Delete(client.ServiceClient(), credentialID) th.AssertNoErr(t, res.Err) } func TestUpdateCredential(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleUpdateCredentialSuccessfully(t) updateOpts := credentials.UpdateOpts{ ProjectID: "731fc6f265cd486d900f16e84c5cb594", Type: "ec2", UserID: "bb5476fd12884539b41d5a88f838d773", Blob: "{\"access\":\"181920\",\"secret\":\"secretKey\"}", } actual, err := credentials.Update(client.ServiceClient(), "2441494e52ab6d594a34d74586075cb299489bdd1e9389e3ab06467a4f460609", updateOpts).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, SecondCredentialUpdated, *actual) } golang-github-gophercloud-gophercloud-0.12.0/openstack/identity/v3/credentials/urls.go000066400000000000000000000012501367513235700311360ustar00rootroot00000000000000package credentials import "github.com/gophercloud/gophercloud" func listURL(client *gophercloud.ServiceClient) string { return client.ServiceURL("credentials") } func getURL(client *gophercloud.ServiceClient, credentialID string) string { return client.ServiceURL("credentials", credentialID) } func createURL(client *gophercloud.ServiceClient) string { return client.ServiceURL("credentials") } func deleteURL(client *gophercloud.ServiceClient, credentialID string) string { return client.ServiceURL("credentials", credentialID) } func updateURL(client *gophercloud.ServiceClient, credentialID string) string { return client.ServiceURL("credentials", credentialID) } golang-github-gophercloud-gophercloud-0.12.0/openstack/identity/v3/domains/000077500000000000000000000000001367513235700267615ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/identity/v3/domains/doc.go000066400000000000000000000021771367513235700300640ustar00rootroot00000000000000/* 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 golang-github-gophercloud-gophercloud-0.12.0/openstack/identity/v3/domains/requests.go000066400000000000000000000075771367513235700312030ustar00rootroot00000000000000package 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) { resp, err := client.Get(getURL(client, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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 } resp, err := client.Post(createURL(client), &b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{201}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete deletes a domain. func Delete(client *gophercloud.ServiceClient, domainID string) (r DeleteResult) { resp, err := client.Delete(deleteURL(client, domainID), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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 } resp, err := client.Patch(updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } golang-github-gophercloud-gophercloud-0.12.0/openstack/identity/v3/domains/results.go000066400000000000000000000045471367513235700310230ustar00rootroot00000000000000package 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 } golang-github-gophercloud-gophercloud-0.12.0/openstack/identity/v3/domains/testing/000077500000000000000000000000001367513235700304365ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/identity/v3/domains/testing/fixtures.go000066400000000000000000000122421367513235700326370ustar00rootroot00000000000000package 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) }) } golang-github-gophercloud-gophercloud-0.12.0/openstack/identity/v3/domains/testing/requests_test.go000066400000000000000000000042571367513235700337070ustar00rootroot00000000000000package 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) var description = "Staging Domain" updateOpts := domains.UpdateOpts{ Description: &description, } actual, err := domains.Update(client.ServiceClient(), "9fe1d3", updateOpts).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, SecondDomainUpdated, *actual) } golang-github-gophercloud-gophercloud-0.12.0/openstack/identity/v3/domains/urls.go000066400000000000000000000011701367513235700302740ustar00rootroot00000000000000package 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.12.0/openstack/identity/v3/endpoints/000077500000000000000000000000001367513235700273325ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/identity/v3/endpoints/doc.go000066400000000000000000000027501367513235700304320ustar00rootroot00000000000000/* 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 golang-github-gophercloud-gophercloud-0.12.0/openstack/identity/v3/endpoints/requests.go000066400000000000000000000112201367513235700315300ustar00rootroot00000000000000package 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 } resp, err := client.Post(listURL(client), &b, &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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 string `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 } resp, err := client.Patch(endpointURL(client, endpointID), &b, &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete removes an endpoint from the service catalog. func Delete(client *gophercloud.ServiceClient, endpointID string) (r DeleteResult) { resp, err := client.Delete(endpointURL(client, endpointID), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } golang-github-gophercloud-gophercloud-0.12.0/openstack/identity/v3/endpoints/results.go000066400000000000000000000042221367513235700313620ustar00rootroot00000000000000package 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 } golang-github-gophercloud-gophercloud-0.12.0/openstack/identity/v3/endpoints/testing/000077500000000000000000000000001367513235700310075ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/identity/v3/endpoints/testing/doc.go000066400000000000000000000000501367513235700320760ustar00rootroot00000000000000// endpoints unit tests package testing requests_test.go000066400000000000000000000123511367513235700341730ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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) } golang-github-gophercloud-gophercloud-0.12.0/openstack/identity/v3/endpoints/urls.go000066400000000000000000000004501367513235700306450ustar00rootroot00000000000000package 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.12.0/openstack/identity/v3/extensions/000077500000000000000000000000001367513235700275265ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/identity/v3/extensions/ec2credentials/000077500000000000000000000000001367513235700324155ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/identity/v3/extensions/ec2credentials/doc.go000066400000000000000000000010041367513235700335040ustar00rootroot00000000000000/* Package ec2credentials provides information and interaction with the EC2 credentials API resource for the OpenStack Identity service. For more information, see: https://docs.openstack.org/api-ref/identity/v2-ext/ Example to Create an EC2 credential createOpts := ec2credentials.CreateOpts{ // project ID of the EC2 credential scope TenantID: projectID, } credential, err := ec2credentials.Create(identityClient, userID, createOpts).Extract() if err != nil { panic(err) } */ package ec2credentials requests.go000066400000000000000000000034451367513235700345460ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/identity/v3/extensions/ec2credentialspackage ec2credentials import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // List enumerates the Credentials to which the current token has access. func List(client *gophercloud.ServiceClient, userID string) pagination.Pager { url := listURL(client, userID) return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { return CredentialPage{pagination.LinkedPageBase{PageResult: r}} }) } // Get retrieves details on a single EC2 credential by ID. func Get(client *gophercloud.ServiceClient, userID string, id string) (r GetResult) { resp, err := client.Get(getURL(client, userID, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // CreateOpts provides options used to create an EC2 credential. type CreateOpts struct { // TenantID is the project ID scope of the EC2 credential. TenantID string `json:"tenant_id" required:"true"` } // ToCredentialCreateMap formats a CreateOpts into a create request. func (opts CreateOpts) ToCredentialCreateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "") } // Create creates a new EC2 Credential. func Create(client *gophercloud.ServiceClient, userID string, opts CreateOpts) (r CreateResult) { b, err := opts.ToCredentialCreateMap() if err != nil { r.Err = err return } resp, err := client.Post(createURL(client, userID), &b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{201}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete deletes an EC2 credential. func Delete(client *gophercloud.ServiceClient, userID string, id string) (r DeleteResult) { resp, err := client.Delete(deleteURL(client, userID, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } results.go000066400000000000000000000047661367513235700344030ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/identity/v3/extensions/ec2credentialspackage ec2credentials import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // Credential represents the application credential object type Credential struct { // UserID contains a User ID of the EC2 credential owner. UserID string `json:"user_id"` // TenantID contains an EC2 credential project scope. TenantID string `json:"tenant_id"` // Access contains an EC2 credential access UUID. Access string `json:"access"` // Secret contains an EC2 credential secret UUID. Secret string `json:"secret"` // TrustID contains an EC2 credential trust ID scope. TrustID string `json:"trust_id"` // Links contains referencing links to the application credential. Links map[string]interface{} `json:"links"` } type credentialResult struct { gophercloud.Result } // GetResult is the response from a Get operation. Call its Extract method // to interpret it as an Credential. type GetResult struct { credentialResult } // CreateResult is the response from a Create operation. Call its Extract method // to interpret it as an Credential. type CreateResult struct { credentialResult } // DeleteResult is the response from a Delete operation. Call its ExtractErr to // determine if the request succeeded or failed. type DeleteResult struct { gophercloud.ErrResult } // an CredentialPage is a single page of an Credential results. type CredentialPage struct { pagination.LinkedPageBase } // IsEmpty determines whether or not a an CredentialPage contains any results. func (r CredentialPage) IsEmpty() (bool, error) { ec2Credentials, err := ExtractCredentials(r) return len(ec2Credentials) == 0, err } // NextPageURL extracts the "next" link from the links section of the result. func (r CredentialPage) 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 } // Extractan Credentials returns a slice of Credentials contained in a single page of results. func ExtractCredentials(r pagination.Page) ([]Credential, error) { var s struct { Credentials []Credential `json:"credentials"` } err := (r.(CredentialPage)).ExtractInto(&s) return s.Credentials, err } // Extract interprets any Credential results as a Credential. func (r credentialResult) Extract() (*Credential, error) { var s struct { Credential *Credential `json:"credential"` } err := r.ExtractInto(&s) return s.Credential, err } testing/000077500000000000000000000000001367513235700340135ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/identity/v3/extensions/ec2credentialsfixtures.go000066400000000000000000000132361367513235700362200ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/identity/v3/extensions/ec2credentials/testingpackage testing import ( "fmt" "net/http" "testing" "github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/ec2credentials" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) const userID = "2844b2a08be147a08ef58317d6471f1f" const credentialID = "f741662395b249c9b8acdebf1722c5ae" // ListOutput provides a single page of EC2Credential results. const ListOutput = ` { "credentials": [ { "user_id": "2844b2a08be147a08ef58317d6471f1f", "links": { "self": "http://identity:5000/v3/users/2844b2a08be147a08ef58317d6471f1f/credentials/OS-EC2/f741662395b249c9b8acdebf1722c5ae" }, "tenant_id": "6238dee2fec940a6bf31e49e9faf995a", "access": "f741662395b249c9b8acdebf1722c5ae", "secret": "6a61eb0296034c89b49cc51dde9b40aa", "trust_id": null }, { "user_id": "2844b2a08be147a08ef58317d6471f1f", "links": { "self": "http://identity:5000/v3/users/2844b2a08be147a08ef58317d6471f1f/credentials/OS-EC2/ad6fc85fc2df49e6b5c23d5b5bdff980" }, "tenant_id": "6238dee2fec940a6bf31e49e9faf995a", "access": "ad6fc85fc2df49e6b5c23d5b5bdff980", "secret": "eb233f680a204097ac329ebe8dba6d32", "trust_id": null } ], "links": { "self": "http://identity:5000/v3/users/2844b2a08be147a08ef58317d6471f1f/credentials/OS-EC2", "previous": null, "next": null } } ` // GetOutput provides a Get result. const GetOutput = ` { "credential": { "user_id": "2844b2a08be147a08ef58317d6471f1f", "links": { "self": "http://identity:5000/v3/users/2844b2a08be147a08ef58317d6471f1f/credentials/OS-EC2/f741662395b249c9b8acdebf1722c5ae" }, "tenant_id": "6238dee2fec940a6bf31e49e9faf995a", "access": "f741662395b249c9b8acdebf1722c5ae", "secret": "6a61eb0296034c89b49cc51dde9b40aa", "trust_id": null } } ` // CreateRequest provides the input to a Create request. const CreateRequest = ` { "tenant_id": "6238dee2fec940a6bf31e49e9faf995a" } ` const CreateResponse = ` { "credential": { "user_id": "2844b2a08be147a08ef58317d6471f1f", "links": { "self": "http://identity:5000/v3/users/2844b2a08be147a08ef58317d6471f1f/credentials/OS-EC2/f741662395b249c9b8acdebf1722c5ae" }, "tenant_id": "6238dee2fec940a6bf31e49e9faf995a", "access": "f741662395b249c9b8acdebf1722c5ae", "secret": "6a61eb0296034c89b49cc51dde9b40aa", "trust_id": null } } ` var EC2Credential = ec2credentials.Credential{ UserID: "2844b2a08be147a08ef58317d6471f1f", TenantID: "6238dee2fec940a6bf31e49e9faf995a", Access: "f741662395b249c9b8acdebf1722c5ae", Secret: "6a61eb0296034c89b49cc51dde9b40aa", TrustID: "", Links: map[string]interface{}{ "self": "http://identity:5000/v3/users/2844b2a08be147a08ef58317d6471f1f/credentials/OS-EC2/f741662395b249c9b8acdebf1722c5ae", }, } var SecondEC2Credential = ec2credentials.Credential{ UserID: "2844b2a08be147a08ef58317d6471f1f", TenantID: "6238dee2fec940a6bf31e49e9faf995a", Access: "ad6fc85fc2df49e6b5c23d5b5bdff980", Secret: "eb233f680a204097ac329ebe8dba6d32", TrustID: "", Links: map[string]interface{}{ "self": "http://identity:5000/v3/users/2844b2a08be147a08ef58317d6471f1f/credentials/OS-EC2/ad6fc85fc2df49e6b5c23d5b5bdff980", }, } // ExpectedEC2CredentialsSlice is the slice of application credentials expected to be returned from ListOutput. var ExpectedEC2CredentialsSlice = []ec2credentials.Credential{EC2Credential, SecondEC2Credential} // HandleListEC2CredentialsSuccessfully creates an HTTP handler at `/users` on the // test handler mux that responds with a list of two applicationcredentials. func HandleListEC2CredentialsSuccessfully(t *testing.T) { th.Mux.HandleFunc("/users/2844b2a08be147a08ef58317d6471f1f/credentials/OS-EC2", 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) }) } // HandleGetEC2CredentialSuccessfully creates an HTTP handler at `/users` on the // test handler mux that responds with a single application credential. func HandleGetEC2CredentialSuccessfully(t *testing.T) { th.Mux.HandleFunc("/users/2844b2a08be147a08ef58317d6471f1f/credentials/OS-EC2/f741662395b249c9b8acdebf1722c5ae", 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) }) } // HandleCreateEC2CredentialSuccessfully creates an HTTP handler at `/users` on the // test handler mux that tests application credential creation. func HandleCreateEC2CredentialSuccessfully(t *testing.T) { th.Mux.HandleFunc("/users/2844b2a08be147a08ef58317d6471f1f/credentials/OS-EC2", 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) }) } // HandleDeleteEC2CredentialSuccessfully creates an HTTP handler at `/users` on the // test handler mux that tests application credential deletion. func HandleDeleteEC2CredentialSuccessfully(t *testing.T) { th.Mux.HandleFunc("/users/2844b2a08be147a08ef58317d6471f1f/credentials/OS-EC2/f741662395b249c9b8acdebf1722c5ae", 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.go000066400000000000000000000040251367513235700372550ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/identity/v3/extensions/ec2credentials/testingpackage testing import ( "testing" "github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/ec2credentials" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) func TestListEC2Credentials(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleListEC2CredentialsSuccessfully(t) count := 0 err := ec2credentials.List(client.ServiceClient(), userID).EachPage(func(page pagination.Page) (bool, error) { count++ actual, err := ec2credentials.ExtractCredentials(page) th.AssertNoErr(t, err) th.CheckDeepEquals(t, ExpectedEC2CredentialsSlice, actual) return true, nil }) th.AssertNoErr(t, err) th.CheckEquals(t, count, 1) } func TestListEC2CredentialsAllPages(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleListEC2CredentialsSuccessfully(t) allPages, err := ec2credentials.List(client.ServiceClient(), userID).AllPages() th.AssertNoErr(t, err) actual, err := ec2credentials.ExtractCredentials(allPages) th.AssertNoErr(t, err) th.CheckDeepEquals(t, ExpectedEC2CredentialsSlice, actual) } func TestGetEC2Credential(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleGetEC2CredentialSuccessfully(t) actual, err := ec2credentials.Get(client.ServiceClient(), userID, credentialID).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, EC2Credential, *actual) } func TestCreateEC2Credential(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleCreateEC2CredentialSuccessfully(t) createOpts := ec2credentials.CreateOpts{ TenantID: "6238dee2fec940a6bf31e49e9faf995a", } actual, err := ec2credentials.Create(client.ServiceClient(), userID, createOpts).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, EC2Credential, *actual) } func TestDeleteEC2Credential(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleDeleteEC2CredentialSuccessfully(t) res := ec2credentials.Delete(client.ServiceClient(), userID, credentialID) th.AssertNoErr(t, res.Err) } golang-github-gophercloud-gophercloud-0.12.0/openstack/identity/v3/extensions/ec2credentials/urls.go000066400000000000000000000012411367513235700337270ustar00rootroot00000000000000package ec2credentials import "github.com/gophercloud/gophercloud" func listURL(client *gophercloud.ServiceClient, userID string) string { return client.ServiceURL("users", userID, "credentials", "OS-EC2") } func getURL(client *gophercloud.ServiceClient, userID string, id string) string { return client.ServiceURL("users", userID, "credentials", "OS-EC2", id) } func createURL(client *gophercloud.ServiceClient, userID string) string { return client.ServiceURL("users", userID, "credentials", "OS-EC2") } func deleteURL(client *gophercloud.ServiceClient, userID string, id string) string { return client.ServiceURL("users", userID, "credentials", "OS-EC2", id) } golang-github-gophercloud-gophercloud-0.12.0/openstack/identity/v3/extensions/ec2tokens/000077500000000000000000000000001367513235700314235ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/identity/v3/extensions/ec2tokens/doc.go000066400000000000000000000020401367513235700325130ustar00rootroot00000000000000/* Package tokens provides information and interaction with the EC2 token API resource for the OpenStack Identity service. For more information, see: https://docs.openstack.org/api-ref/identity/v2-ext/ Example to Create a Token From an EC2 access and secret keys var authOptions tokens.AuthOptionsBuilder authOptions = &ec2tokens.AuthOptions{ Access: "a7f1e798b7c2417cba4a02de97dc3cdc", Secret: "18f4f6761ada4e3795fa5273c30349b9", } token, err := ec2tokens.Create(identityClient, authOptions).ExtractToken() if err != nil { panic(err) } Example to auth a client using EC2 access and secret keys client, err := openstack.NewClient("http://localhost:5000/v3") if err != nil { panic(err) } var authOptions tokens.AuthOptionsBuilder authOptions = &ec2tokens.AuthOptions{ Access: "a7f1e798b7c2417cba4a02de97dc3cdc", Secret: "18f4f6761ada4e3795fa5273c30349b9", AllowReauth: true, } err = openstack.AuthenticateV3(client, authOptions, gophercloud.EndpointOpts{}) if err != nil { panic(err) } */ package ec2tokens golang-github-gophercloud-gophercloud-0.12.0/openstack/identity/v3/extensions/ec2tokens/requests.go000066400000000000000000000317331367513235700336340ustar00rootroot00000000000000package ec2tokens import ( "crypto/hmac" "crypto/sha1" "crypto/sha256" "encoding/hex" "fmt" "math/rand" "net/url" "sort" "strings" "time" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack/identity/v3/tokens" ) const ( // EC2CredentialsAwsRequestV4 is a constant, used to generate AWS // Credential V4. EC2CredentialsAwsRequestV4 = "aws4_request" // EC2CredentialsHmacSha1V2 is a HMAC SHA1 signature method. Used to // generate AWS Credential V2. EC2CredentialsHmacSha1V2 = "HmacSHA1" // EC2CredentialsHmacSha256V2 is a HMAC SHA256 signature method. Used // to generate AWS Credential V2. EC2CredentialsHmacSha256V2 = "HmacSHA256" // EC2CredentialsAwsHmacV4 is an AWS signature V4 signing method. // More details: // https://docs.aws.amazon.com/general/latest/gr/signature-version-4.html EC2CredentialsAwsHmacV4 = "AWS4-HMAC-SHA256" // EC2CredentialsTimestampFormatV4 is an AWS signature V4 timestamp // format. EC2CredentialsTimestampFormatV4 = "20060102T150405Z" // EC2CredentialsDateFormatV4 is an AWS signature V4 date format. EC2CredentialsDateFormatV4 = "20060102" ) // AuthOptions represents options for authenticating a user using EC2 credentials. type AuthOptions struct { // Access is the EC2 Credential Access ID. Access string `json:"access" required:"true"` // Secret is the EC2 Credential Secret, used to calculate signature. // Not used, when a Signature is is. Secret string `json:"-"` // Host is a HTTP request Host header. Used to calculate an AWS // signature V2. For signature V4 set the Host inside Headers map. // Optional. Host string `json:"host"` // Path is a HTTP request path. Optional. Path string `json:"path"` // Verb is a HTTP request method. Optional. Verb string `json:"verb"` // Headers is a map of HTTP request headers. Optional. Headers map[string]string `json:"headers"` // Region is a region name to calculate an AWS signature V4. Optional. Region string `json:"-"` // Service is a service name to calculate an AWS signature V4. Optional. Service string `json:"-"` // Params is a map of GET method parameters. Optional. Params map[string]string `json:"params"` // AllowReauth allows Gophercloud to re-authenticate automatically // if/when your token expires. AllowReauth bool `json:"-"` // Signature can be either a []byte (encoded to base64 automatically) or // a string. You can set the singature explicitly, when you already know // it. In this case default Params won't be automatically set. Optional. Signature interface{} `json:"signature"` // BodyHash is a HTTP request body sha256 hash. When nil and Signature // is not set, a random hash is generated. Optional. BodyHash *string `json:"body_hash"` // Timestamp is a timestamp to calculate a V4 signature. Optional. Timestamp *time.Time `json:"-"` // Token is a []byte string (encoded to base64 automatically) which was // signed by an EC2 secret key. Used by S3 tokens for validation only. // Token must be set with a Signature. If a Signature is not provided, // a Token will be generated automatically along with a Signature. Token []byte `json:"token,omitempty"` } // EC2CredentialsBuildCanonicalQueryStringV2 builds a canonical query string // for an AWS signature V2. // https://github.com/openstack/python-keystoneclient/blob/stable/train/keystoneclient/contrib/ec2/utils.py#L133 func EC2CredentialsBuildCanonicalQueryStringV2(params map[string]string) string { var keys []string for k := range params { keys = append(keys, k) } sort.Strings(keys) var pairs []string for _, k := range keys { pairs = append(pairs, fmt.Sprintf("%s=%s", k, url.QueryEscape(params[k]))) } return strings.Join(pairs, "&") } // EC2CredentialsBuildStringToSignV2 builds a string to sign an AWS signature // V2. // https://github.com/openstack/python-keystoneclient/blob/stable/train/keystoneclient/contrib/ec2/utils.py#L148 func EC2CredentialsBuildStringToSignV2(opts AuthOptions) []byte { stringToSign := strings.Join([]string{ opts.Verb, opts.Host, opts.Path, }, "\n") return []byte(strings.Join([]string{ stringToSign, EC2CredentialsBuildCanonicalQueryStringV2(opts.Params), }, "\n")) } // EC2CredentialsBuildCanonicalQueryStringV2 builds a canonical query string // for an AWS signature V4. // https://github.com/openstack/python-keystoneclient/blob/stable/train/keystoneclient/contrib/ec2/utils.py#L244 func EC2CredentialsBuildCanonicalQueryStringV4(verb string, params map[string]string) string { if verb == "POST" { return "" } return EC2CredentialsBuildCanonicalQueryStringV2(params) } // EC2CredentialsBuildCanonicalHeadersV4 builds a canonical string based on // "headers" map and "signedHeaders" string parameters. // https://github.com/openstack/python-keystoneclient/blob/stable/train/keystoneclient/contrib/ec2/utils.py#L216 func EC2CredentialsBuildCanonicalHeadersV4(headers map[string]string, signedHeaders string) string { headersLower := make(map[string]string, len(headers)) for k, v := range headers { headersLower[strings.ToLower(k)] = v } var headersList []string for _, h := range strings.Split(signedHeaders, ";") { if v, ok := headersLower[h]; ok { headersList = append(headersList, h+":"+v) } } return strings.Join(headersList, "\n") + "\n" } // EC2CredentialsBuildSignatureKeyV4 builds a HMAC 256 signature key based on // input parameters. // https://github.com/openstack/python-keystoneclient/blob/stable/train/keystoneclient/contrib/ec2/utils.py#L169 func EC2CredentialsBuildSignatureKeyV4(secret, region, service string, date time.Time) []byte { kDate := sumHMAC256([]byte("AWS4"+secret), []byte(date.Format(EC2CredentialsDateFormatV4))) kRegion := sumHMAC256(kDate, []byte(region)) kService := sumHMAC256(kRegion, []byte(service)) return sumHMAC256(kService, []byte(EC2CredentialsAwsRequestV4)) } // EC2CredentialsBuildStringToSignV4 builds an AWS v4 signature string to sign // based on input parameters. // https://github.com/openstack/python-keystoneclient/blob/stable/train/keystoneclient/contrib/ec2/utils.py#L251 func EC2CredentialsBuildStringToSignV4(opts AuthOptions, signedHeaders string, bodyHash string, date time.Time) []byte { scope := strings.Join([]string{ date.Format(EC2CredentialsDateFormatV4), opts.Region, opts.Service, EC2CredentialsAwsRequestV4, }, "/") canonicalRequest := strings.Join([]string{ opts.Verb, opts.Path, EC2CredentialsBuildCanonicalQueryStringV4(opts.Verb, opts.Params), EC2CredentialsBuildCanonicalHeadersV4(opts.Headers, signedHeaders), signedHeaders, bodyHash, }, "\n") hash := sha256.Sum256([]byte(canonicalRequest)) return []byte(strings.Join([]string{ EC2CredentialsAwsHmacV4, date.Format(EC2CredentialsTimestampFormatV4), scope, hex.EncodeToString(hash[:]), }, "\n")) } // EC2CredentialsBuildSignatureV4 builds an AWS v4 signature based on input // parameters. // https://github.com/openstack/python-keystoneclient/blob/stable/train/keystoneclient/contrib/ec2/utils.py#L285..L286 func EC2CredentialsBuildSignatureV4(key []byte, stringToSign []byte) string { return hex.EncodeToString(sumHMAC256(key, stringToSign)) } // EC2CredentialsBuildAuthorizationHeaderV4 builds an AWS v4 Authorization // header based on auth parameters, date and signature func EC2CredentialsBuildAuthorizationHeaderV4(opts AuthOptions, signedHeaders string, signature string, date time.Time) string { return fmt.Sprintf("%s Credential=%s/%s/%s/%s/%s, SignedHeaders=%s, Signature=%s", EC2CredentialsAwsHmacV4, opts.Access, date.Format(EC2CredentialsDateFormatV4), opts.Region, opts.Service, EC2CredentialsAwsRequestV4, signedHeaders, signature) } // ToTokenV3ScopeMap is a dummy method to satisfy tokens.AuthOptionsBuilder // interface. func (opts *AuthOptions) ToTokenV3ScopeMap() (map[string]interface{}, error) { return nil, nil } // ToTokenV3HeadersMap allows AuthOptions to satisfy the AuthOptionsBuilder // interface in the v3 tokens package. func (opts *AuthOptions) ToTokenV3HeadersMap(map[string]interface{}) (map[string]string, error) { return nil, nil } // CanReauth is a method method to satisfy tokens.AuthOptionsBuilder interface func (opts *AuthOptions) CanReauth() bool { return opts.AllowReauth } // ToTokenV3CreateMap formats an AuthOptions into a create request. func (opts *AuthOptions) ToTokenV3CreateMap(map[string]interface{}) (map[string]interface{}, error) { b, err := gophercloud.BuildRequestBody(opts, "credentials") if err != nil { return nil, err } if opts.Signature != nil { return b, nil } // calculate signature, when it is not set c, _ := b["credentials"].(map[string]interface{}) h := interfaceToMap(c, "headers") p := interfaceToMap(c, "params") // detect and process a signature v2 if v, ok := p["SignatureVersion"]; ok && v == "2" { if _, ok := c["body_hash"]; ok { delete(c, "body_hash") } if _, ok := c["headers"]; ok { delete(c, "headers") } if v, ok := p["SignatureMethod"]; ok { // params is a map of strings strToSign := EC2CredentialsBuildStringToSignV2(*opts) switch v { case EC2CredentialsHmacSha1V2: // keystone uses this method only when HmacSHA256 is not available on the server side // https://github.com/openstack/python-keystoneclient/blob/stable/train/keystoneclient/contrib/ec2/utils.py#L151..L156 c["signature"] = sumHMAC1([]byte(opts.Secret), strToSign) return b, nil case EC2CredentialsHmacSha256V2: c["signature"] = sumHMAC256([]byte(opts.Secret), strToSign) return b, nil } return nil, fmt.Errorf("unsupported signature method: %s", v) } return nil, fmt.Errorf("signature method must be provided") } else if ok { return nil, fmt.Errorf("unsupported signature version: %s", v) } // it is not a signature v2, but a signature v4 date := time.Now().UTC() if opts.Timestamp != nil { date = *opts.Timestamp } if v, _ := c["body_hash"]; v == nil { // when body_hash is not set, generate a random one c["body_hash"] = randomBodyHash() } signedHeaders, _ := h["X-Amz-SignedHeaders"] stringToSign := EC2CredentialsBuildStringToSignV4(*opts, signedHeaders, c["body_hash"].(string), date) key := EC2CredentialsBuildSignatureKeyV4(opts.Secret, opts.Region, opts.Service, date) c["signature"] = EC2CredentialsBuildSignatureV4(key, stringToSign) h["X-Amz-Date"] = date.Format(EC2CredentialsTimestampFormatV4) h["Authorization"] = EC2CredentialsBuildAuthorizationHeaderV4(*opts, signedHeaders, c["signature"].(string), date) // token is only used for S3 tokens validation and will be removed when using EC2 validation c["token"] = stringToSign return b, nil } // Create authenticates and either generates a new token from EC2 credentials func Create(c *gophercloud.ServiceClient, opts tokens.AuthOptionsBuilder) (r tokens.CreateResult) { b, err := opts.ToTokenV3CreateMap(nil) if err != nil { r.Err = err return } // delete "token" element, since it is used in s3tokens deleteBodyElements(b, "token") resp, err := c.Post(ec2tokensURL(c), b, &r.Body, &gophercloud.RequestOpts{ MoreHeaders: map[string]string{"X-Auth-Token": ""}, OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // ValidateS3Token authenticates an S3 request using EC2 credentials. Doesn't // generate a new token ID, but returns a tokens.CreateResult. func ValidateS3Token(c *gophercloud.ServiceClient, opts tokens.AuthOptionsBuilder) (r tokens.CreateResult) { b, err := opts.ToTokenV3CreateMap(nil) if err != nil { r.Err = err return } // delete unused element, since it is used in ec2tokens only deleteBodyElements(b, "body_hash", "headers", "host", "params", "path", "verb") resp, err := c.Post(s3tokensURL(c), b, &r.Body, &gophercloud.RequestOpts{ MoreHeaders: map[string]string{"X-Auth-Token": ""}, OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // The following are small helper functions used to help build the signature. // sumHMAC1 is a func to implement the HMAC SHA1 signature method. func sumHMAC1(key []byte, data []byte) []byte { hash := hmac.New(sha1.New, key) hash.Write(data) return hash.Sum(nil) } // sumHMAC256 is a func to implement the HMAC SHA256 signature method. func sumHMAC256(key []byte, data []byte) []byte { hash := hmac.New(sha256.New, key) hash.Write(data) return hash.Sum(nil) } // randomBodyHash is a func to generate a random sha256 hexdigest. func randomBodyHash() string { h := make([]byte, 64) rand.Read(h) return hex.EncodeToString(h) } // interfaceToMap is a func used to represent a "credentials" map element as a // "map[string]string" func interfaceToMap(c map[string]interface{}, key string) map[string]string { // convert map[string]interface{} to map[string]string m := make(map[string]string) if v, _ := c[key].(map[string]interface{}); v != nil { for k, v := range v { m[k] = v.(string) } } c[key] = m return m } // deleteBodyElements deletes map body elements func deleteBodyElements(b map[string]interface{}, elements ...string) { if c, ok := b["credentials"].(map[string]interface{}); ok { for _, k := range elements { if _, ok := c[k]; ok { delete(c, k) } } } } golang-github-gophercloud-gophercloud-0.12.0/openstack/identity/v3/extensions/ec2tokens/testing/000077500000000000000000000000001367513235700331005ustar00rootroot00000000000000requests_test.go000066400000000000000000000216171367513235700362710ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/identity/v3/extensions/ec2tokens/testingpackage testing import ( "encoding/hex" "fmt" "net/http" "testing" "time" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/ec2tokens" "github.com/gophercloud/gophercloud/openstack/identity/v3/tokens" tokens_testing "github.com/gophercloud/gophercloud/openstack/identity/v3/tokens/testing" "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 ec2tokens.AuthOptions, requestJSON string) { testhelper.SetupHTTP() defer testhelper.TeardownHTTP() client := gophercloud.ServiceClient{ ProviderClient: &gophercloud.ProviderClient{}, Endpoint: testhelper.Endpoint(), } testhelper.Mux.HandleFunc("/ec2tokens", 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.StatusOK) fmt.Fprintf(w, tokens_testing.TokenOutput) }) expected := &tokens.Token{ ExpiresAt: time.Date(2017, 6, 3, 2, 19, 49, 0, time.UTC), } actual, err := ec2tokens.Create(&client, &options).Extract() testhelper.AssertNoErr(t, err) testhelper.CheckDeepEquals(t, expected, actual) } func TestCreateV2(t *testing.T) { credentials := ec2tokens.AuthOptions{ Access: "a7f1e798b7c2417cba4a02de97dc3cdc", Host: "localhost", Path: "/", Secret: "18f4f6761ada4e3795fa5273c30349b9", Verb: "GET", // this should be removed from JSON request BodyHash: new(string), // this should be removed from JSON request Headers: map[string]string{ "Foo": "Bar", }, Params: map[string]string{ "Action": "Test", "SignatureMethod": "HmacSHA256", "SignatureVersion": "2", }, } authTokenPost(t, credentials, `{ "credentials": { "access": "a7f1e798b7c2417cba4a02de97dc3cdc", "host": "localhost", "params": { "Action": "Test", "SignatureMethod": "HmacSHA256", "SignatureVersion": "2" }, "path": "/", "signature": "Up+MbVbbrvdR5FRkUz+n3nc+VW6xieuN50wh6ONEJ4w=", "verb": "GET" } }`) } func TestCreateV4(t *testing.T) { bodyHash := "foo" credentials := ec2tokens.AuthOptions{ Access: "a7f1e798b7c2417cba4a02de97dc3cdc", BodyHash: &bodyHash, Timestamp: new(time.Time), Region: "region1", Service: "ec2", Path: "/", Secret: "18f4f6761ada4e3795fa5273c30349b9", Verb: "GET", Headers: map[string]string{ "Host": "localhost", }, Params: map[string]string{ "Action": "Test", }, } authTokenPost(t, credentials, `{ "credentials": { "access": "a7f1e798b7c2417cba4a02de97dc3cdc", "body_hash": "foo", "host": "", "headers": { "Host": "localhost", "Authorization": "AWS4-HMAC-SHA256 Credential=a7f1e798b7c2417cba4a02de97dc3cdc/00010101/region1/ec2/aws4_request, SignedHeaders=, Signature=f36f79118f75d7d6ec86ead9a61679cbdcf94c0cbfe5e9cf2407e8406aa82028", "X-Amz-Date": "00010101T000000Z" }, "params": { "Action": "Test" }, "path": "/", "signature": "f36f79118f75d7d6ec86ead9a61679cbdcf94c0cbfe5e9cf2407e8406aa82028", "verb": "GET" } }`) } func TestCreateV4Empty(t *testing.T) { credentials := ec2tokens.AuthOptions{ Access: "a7f1e798b7c2417cba4a02de97dc3cdc", Secret: "18f4f6761ada4e3795fa5273c30349b9", BodyHash: new(string), Timestamp: new(time.Time), } authTokenPost(t, credentials, `{ "credentials": { "access": "a7f1e798b7c2417cba4a02de97dc3cdc", "body_hash": "", "host": "", "headers": { "Authorization": "AWS4-HMAC-SHA256 Credential=a7f1e798b7c2417cba4a02de97dc3cdc/00010101///aws4_request, SignedHeaders=, Signature=140a31abf1efe93a607dcac6cd8f66887b86d2bc8f712c290d9aa06edf428608", "X-Amz-Date": "00010101T000000Z" }, "params": {}, "path": "", "signature": "140a31abf1efe93a607dcac6cd8f66887b86d2bc8f712c290d9aa06edf428608", "verb": "" } }`) } func TestCreateV4Headers(t *testing.T) { credentials := ec2tokens.AuthOptions{ Access: "a7f1e798b7c2417cba4a02de97dc3cdc", BodyHash: new(string), Timestamp: new(time.Time), Region: "region1", Service: "ec2", Path: "/", Secret: "18f4f6761ada4e3795fa5273c30349b9", Verb: "GET", Headers: map[string]string{ "Foo": "Bar", "Host": "localhost", }, Params: map[string]string{ "Action": "Test", }, } authTokenPost(t, credentials, `{ "credentials": { "access": "a7f1e798b7c2417cba4a02de97dc3cdc", "body_hash": "", "host": "", "headers": { "Foo": "Bar", "Host": "localhost", "Authorization": "AWS4-HMAC-SHA256 Credential=a7f1e798b7c2417cba4a02de97dc3cdc/00010101/region1/ec2/aws4_request, SignedHeaders=, Signature=f5cd6995be98e5576a130b30cca277375f10439217ea82169aa8386e83965611", "X-Amz-Date": "00010101T000000Z" }, "params": { "Action": "Test" }, "path": "/", "signature": "f5cd6995be98e5576a130b30cca277375f10439217ea82169aa8386e83965611", "verb": "GET" } }`) } func TestCreateV4WithSignature(t *testing.T) { credentials := ec2tokens.AuthOptions{ Access: "a7f1e798b7c2417cba4a02de97dc3cdc", BodyHash: new(string), Path: "/", Signature: "f5cd6995be98e5576a130b30cca277375f10439217ea82169aa8386e83965611", Verb: "GET", Headers: map[string]string{ "Foo": "Bar", "Host": "localhost", "Authorization": "AWS4-HMAC-SHA256 Credential=a7f1e798b7c2417cba4a02de97dc3cdc/00010101/region1/ec2/aws4_request, SignedHeaders=, Signature=f5cd6995be98e5576a130b30cca277375f10439217ea82169aa8386e83965611", "X-Amz-Date": "00010101T000000Z", }, Params: map[string]string{ "Action": "Test", }, } authTokenPost(t, credentials, `{ "credentials": { "access": "a7f1e798b7c2417cba4a02de97dc3cdc", "body_hash": "", "host": "", "headers": { "Foo": "Bar", "Host": "localhost", "Authorization": "AWS4-HMAC-SHA256 Credential=a7f1e798b7c2417cba4a02de97dc3cdc/00010101/region1/ec2/aws4_request, SignedHeaders=, Signature=f5cd6995be98e5576a130b30cca277375f10439217ea82169aa8386e83965611", "X-Amz-Date": "00010101T000000Z" }, "params": { "Action": "Test" }, "path": "/", "signature": "f5cd6995be98e5576a130b30cca277375f10439217ea82169aa8386e83965611", "verb": "GET" } }`) } func TestEC2CredentialsBuildCanonicalQueryStringV2(t *testing.T) { params := map[string]string{ "Action": "foo", "Value": "bar", } expected := "Action=foo&Value=bar" testhelper.CheckEquals(t, expected, ec2tokens.EC2CredentialsBuildCanonicalQueryStringV2(params)) } func TestEC2CredentialsBuildStringToSignV2(t *testing.T) { opts := ec2tokens.AuthOptions{ Verb: "GET", Host: "localhost", Path: "/", Params: map[string]string{ "Action": "foo", "Value": "bar", }, } expected := []byte("GET\nlocalhost\n/\nAction=foo&Value=bar") testhelper.CheckDeepEquals(t, expected, ec2tokens.EC2CredentialsBuildStringToSignV2(opts)) } func TestEC2CredentialsBuildCanonicalQueryStringV4(t *testing.T) { params := map[string]string{ "Action": "foo", "Value": "bar", } expected := "Action=foo&Value=bar" testhelper.CheckEquals(t, expected, ec2tokens.EC2CredentialsBuildCanonicalQueryStringV4("foo", params)) testhelper.CheckEquals(t, "", ec2tokens.EC2CredentialsBuildCanonicalQueryStringV4("POST", params)) } func TestEC2CredentialsBuildCanonicalHeadersV4(t *testing.T) { headers := map[string]string{ "Foo": "bar", "Baz": "qux", } signedHeaders := "foo;baz" expected := "foo:bar\nbaz:qux\n" testhelper.CheckEquals(t, expected, ec2tokens.EC2CredentialsBuildCanonicalHeadersV4(headers, signedHeaders)) } func TestEC2CredentialsBuildSignatureKeyV4(t *testing.T) { expected := "246626bd815b0a0cae4bedc3f4e124ca25e208cd75fd812d836aeae184de038a" testhelper.CheckEquals(t, expected, hex.EncodeToString((ec2tokens.EC2CredentialsBuildSignatureKeyV4("foo", "bar", "baz", time.Time{})))) } func TestEC2CredentialsBuildSignatureV4(t *testing.T) { opts := ec2tokens.AuthOptions{ Verb: "GET", Path: "/", Headers: map[string]string{ "Host": "localhost", }, Params: map[string]string{ "Action": "foo", "Value": "bar", }, } expected := "6a5febe41427bf601f0ae7c34dbb0fd67094776138b03fb8e65783d733d302a5" date := time.Time{} stringToSign := ec2tokens.EC2CredentialsBuildStringToSignV4(opts, "host", "foo", date) key := ec2tokens.EC2CredentialsBuildSignatureKeyV4("", "", "", date) testhelper.CheckEquals(t, expected, ec2tokens.EC2CredentialsBuildSignatureV4(key, stringToSign)) } golang-github-gophercloud-gophercloud-0.12.0/openstack/identity/v3/extensions/ec2tokens/urls.go000066400000000000000000000003711367513235700327400ustar00rootroot00000000000000package ec2tokens import "github.com/gophercloud/gophercloud" func ec2tokensURL(c *gophercloud.ServiceClient) string { return c.ServiceURL("ec2tokens") } func s3tokensURL(c *gophercloud.ServiceClient) string { return c.ServiceURL("s3tokens") } golang-github-gophercloud-gophercloud-0.12.0/openstack/identity/v3/extensions/oauth1/000077500000000000000000000000001367513235700307275ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/identity/v3/extensions/oauth1/doc.go000066400000000000000000000065511367513235700320320ustar00rootroot00000000000000/* Package oauth1 enables management of OpenStack OAuth1 tokens and Authentication. Example to Create an OAuth1 Consumer createConsumerOpts := oauth1.CreateConsumerOpts{ Description: "My consumer", } consumer, err := oauth1.CreateConsumer(identityClient, createConsumerOpts).Extract() if err != nil { panic(err) } // NOTE: Consumer secret is available only on create response fmt.Printf("Consumer: %+v\n", consumer) Example to Request an unauthorized OAuth1 token requestTokenOpts := oauth1.RequestTokenOpts{ OAuthConsumerKey: consumer.ID, OAuthConsumerSecret: consumer.Secret, OAuthSignatureMethod: oauth1.HMACSHA1, RequestedProjectID: projectID, } requestToken, err := oauth1.RequestToken(identityClient, requestTokenOpts).Extract() if err != nil { panic(err) } // NOTE: Request token secret is available only on request response fmt.Printf("Request token: %+v\n", requestToken) Example to Authorize an unauthorized OAuth1 token authorizeTokenOpts := oauth1.AuthorizeTokenOpts{ Roles: []oauth1.Role{ {Name: "member"}, }, } authToken, err := oauth1.AuthorizeToken(identityClient, requestToken.OAuthToken, authorizeTokenOpts).Extract() if err != nil { panic(err) } fmt.Printf("Verifier ID of the unauthorized Token: %+v\n", authToken.OAuthVerifier) Example to Create an OAuth1 Access Token accessTokenOpts := oauth1.CreateAccessTokenOpts{ OAuthConsumerKey: consumer.ID, OAuthConsumerSecret: consumer.Secret, OAuthToken: requestToken.OAuthToken, OAuthTokenSecret: requestToken.OAuthTokenSecret, OAuthVerifier: authToken.OAuthVerifier, OAuthSignatureMethod: oauth1.HMACSHA1, } accessToken, err := oauth1.CreateAccessToken(identityClient, accessTokenOpts).Extract() if err != nil { panic(err) } // NOTE: Access token secret is available only on create response fmt.Printf("OAuth1 Access Token: %+v\n", accessToken) Example to List User's OAuth1 Access Tokens allPages, err := oauth1.ListAccessTokens(identityClient, userID).AllPages() if err != nil { panic(err) } accessTokens, err := oauth1.ExtractAccessTokens(allPages) if err != nil { panic(err) } for _, accessToken := range accessTokens { fmt.Printf("Access Token: %+v\n", accessToken) } Example to Authenticate a client using OAuth1 method client, err := openstack.NewClient("http://localhost:5000/v3") if err != nil { panic(err) } authOptions := &oauth1.AuthOptions{ // consumer token, created earlier OAuthConsumerKey: consumer.ID, OAuthConsumerSecret: consumer.Secret, // access token, created earlier OAuthToken: accessToken.OAuthToken, OAuthTokenSecret: accessToken.OAuthTokenSecret, OAuthSignatureMethod: oauth1.HMACSHA1, } err = openstack.AuthenticateV3(client, authOptions, gophercloud.EndpointOpts{}) if err != nil { panic(err) } Example to Create a Token using OAuth1 method var oauth1Token struct { tokens.Token oauth1.TokenExt } createOpts := &oauth1.AuthOptions{ // consumer token, created earlier OAuthConsumerKey: consumer.ID, OAuthConsumerSecret: consumer.Secret, // access token, created earlier OAuthToken: accessToken.OAuthToken, OAuthTokenSecret: accessToken.OAuthTokenSecret, OAuthSignatureMethod: oauth1.HMACSHA1, } err := tokens.Create(identityClient, createOpts).ExtractInto(&oauth1Token) if err != nil { panic(err) } */ package oauth1 golang-github-gophercloud-gophercloud-0.12.0/openstack/identity/v3/extensions/oauth1/requests.go000066400000000000000000000454301367513235700331370ustar00rootroot00000000000000package oauth1 import ( "crypto/hmac" "crypto/sha1" "encoding/base64" "fmt" "io/ioutil" "math/rand" "net/url" "sort" "strconv" "strings" "time" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack/identity/v3/tokens" "github.com/gophercloud/gophercloud/pagination" ) // Type SignatureMethod is a OAuth1 SignatureMethod type. type SignatureMethod string const ( // HMACSHA1 is a recommended OAuth1 signature method. HMACSHA1 SignatureMethod = "HMAC-SHA1" // PLAINTEXT signature method is not recommended to be used in // production environment. PLAINTEXT SignatureMethod = "PLAINTEXT" // OAuth1TokenContentType is a supported content type for an OAuth1 // token. OAuth1TokenContentType = "application/x-www-form-urlencoded" ) // AuthOptions represents options for authenticating a user using OAuth1 tokens. type AuthOptions struct { // OAuthConsumerKey is the OAuth1 Consumer Key. OAuthConsumerKey string `q:"oauth_consumer_key" required:"true"` // OAuthConsumerSecret is the OAuth1 Consumer Secret. Used to generate // an OAuth1 request signature. OAuthConsumerSecret string `required:"true"` // OAuthToken is the OAuth1 Request Token. OAuthToken string `q:"oauth_token" required:"true"` // OAuthTokenSecret is the OAuth1 Request Token Secret. Used to generate // an OAuth1 request signature. OAuthTokenSecret string `required:"true"` // OAuthSignatureMethod is the OAuth1 signature method the Consumer used // to sign the request. Supported values are "HMAC-SHA1" or "PLAINTEXT". // "PLAINTEXT" is not recommended for production usage. OAuthSignatureMethod SignatureMethod `q:"oauth_signature_method" required:"true"` // OAuthTimestamp is an OAuth1 request timestamp. If nil, current Unix // timestamp will be used. OAuthTimestamp *time.Time // OAuthNonce is an OAuth1 request nonce. Nonce must be a random string, // uniquely generated for each request. Will be generated automatically // when it is not set. OAuthNonce string `q:"oauth_nonce"` // AllowReauth allows Gophercloud to re-authenticate automatically // if/when your token expires. AllowReauth bool } // ToTokenV3HeadersMap builds the headers required for an OAuth1-based create // request. func (opts AuthOptions) ToTokenV3HeadersMap(headerOpts map[string]interface{}) (map[string]string, error) { q, err := buildOAuth1QueryString(opts, opts.OAuthTimestamp, "") if err != nil { return nil, err } signatureKeys := []string{opts.OAuthConsumerSecret, opts.OAuthTokenSecret} method := headerOpts["method"].(string) u := headerOpts["url"].(string) stringToSign := buildStringToSign(method, u, q.Query()) signature := url.QueryEscape(signString(opts.OAuthSignatureMethod, stringToSign, signatureKeys)) authHeader := buildAuthHeader(q.Query(), signature) headers := map[string]string{ "Authorization": authHeader, "X-Auth-Token": "", } return headers, nil } // ToTokenV3ScopeMap allows AuthOptions to satisfy the tokens.AuthOptionsBuilder // interface. func (opts AuthOptions) ToTokenV3ScopeMap() (map[string]interface{}, error) { return nil, nil } // CanReauth allows AuthOptions to satisfy the tokens.AuthOptionsBuilder // interface. func (opts AuthOptions) CanReauth() bool { return opts.AllowReauth } // ToTokenV3CreateMap builds a create request body. func (opts AuthOptions) ToTokenV3CreateMap(map[string]interface{}) (map[string]interface{}, error) { // identityReq defines the "identity" portion of an OAuth1-based authentication // create request body. type identityReq struct { Methods []string `json:"methods"` OAuth1 struct{} `json:"oauth1"` } // authReq defines the "auth" portion of an OAuth1-based authentication // create request body. type authReq struct { Identity identityReq `json:"identity"` } // oauth1Request defines how an OAuth1-based authentication create // request body looks. type oauth1Request struct { Auth authReq `json:"auth"` } var req oauth1Request req.Auth.Identity.Methods = []string{"oauth1"} return gophercloud.BuildRequestBody(req, "") } // Create authenticates and either generates a new OpenStack token from an // OAuth1 token. func Create(client *gophercloud.ServiceClient, opts tokens.AuthOptionsBuilder) (r tokens.CreateResult) { b, err := opts.ToTokenV3CreateMap(nil) if err != nil { r.Err = err return } headerOpts := map[string]interface{}{ "method": "POST", "url": authURL(client), } h, err := opts.ToTokenV3HeadersMap(headerOpts) if err != nil { r.Err = err return } resp, err := client.Post(authURL(client), b, &r.Body, &gophercloud.RequestOpts{ MoreHeaders: h, OkCodes: []int{201}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // CreateConsumerOptsBuilder allows extensions to add additional parameters to // the CreateConsumer request. type CreateConsumerOptsBuilder interface { ToOAuth1CreateConsumerMap() (map[string]interface{}, error) } // CreateConsumerOpts provides options used to create a new Consumer. type CreateConsumerOpts struct { // Description is the consumer description. Description string `json:"description"` } // ToOAuth1CreateConsumerMap formats a CreateConsumerOpts into a create request. func (opts CreateConsumerOpts) ToOAuth1CreateConsumerMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "consumer") } // Create creates a new Consumer. func CreateConsumer(client *gophercloud.ServiceClient, opts CreateConsumerOptsBuilder) (r CreateConsumerResult) { b, err := opts.ToOAuth1CreateConsumerMap() if err != nil { r.Err = err return } resp, err := client.Post(consumersURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{201}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete deletes a Consumer. func DeleteConsumer(client *gophercloud.ServiceClient, id string) (r DeleteConsumerResult) { resp, err := client.Delete(consumerURL(client, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // List enumerates Consumers. func ListConsumers(client *gophercloud.ServiceClient) pagination.Pager { return pagination.NewPager(client, consumersURL(client), func(r pagination.PageResult) pagination.Page { return ConsumersPage{pagination.LinkedPageBase{PageResult: r}} }) } // GetConsumer retrieves details on a single Consumer by ID. func GetConsumer(client *gophercloud.ServiceClient, id string) (r GetConsumerResult) { resp, err := client.Get(consumerURL(client, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // UpdateConsumerOpts provides options used to update a consumer. type UpdateConsumerOpts struct { // Description is the consumer description. Description string `json:"description"` } // ToOAuth1UpdateConsumerMap formats an UpdateConsumerOpts into a consumer update // request. func (opts UpdateConsumerOpts) ToOAuth1UpdateConsumerMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "consumer") } // UpdateConsumer updates an existing Consumer. func UpdateConsumer(client *gophercloud.ServiceClient, id string, opts UpdateConsumerOpts) (r UpdateConsumerResult) { b, err := opts.ToOAuth1UpdateConsumerMap() if err != nil { r.Err = err return } resp, err := client.Patch(consumerURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // RequestTokenOptsBuilder allows extensions to add additional parameters to the // RequestToken request. type RequestTokenOptsBuilder interface { ToOAuth1RequestTokenHeaders(string, string) (map[string]string, error) } // RequestTokenOpts provides options used to get a consumer unauthorized // request token. type RequestTokenOpts struct { // OAuthConsumerKey is the OAuth1 Consumer Key. OAuthConsumerKey string `q:"oauth_consumer_key" required:"true"` // OAuthConsumerSecret is the OAuth1 Consumer Secret. Used to generate // an OAuth1 request signature. OAuthConsumerSecret string `required:"true"` // OAuthSignatureMethod is the OAuth1 signature method the Consumer used // to sign the request. Supported values are "HMAC-SHA1" or "PLAINTEXT". // "PLAINTEXT" is not recommended for production usage. OAuthSignatureMethod SignatureMethod `q:"oauth_signature_method" required:"true"` // OAuthTimestamp is an OAuth1 request timestamp. If nil, current Unix // timestamp will be used. OAuthTimestamp *time.Time // OAuthNonce is an OAuth1 request nonce. Nonce must be a random string, // uniquely generated for each request. Will be generated automatically // when it is not set. OAuthNonce string `q:"oauth_nonce"` // RequestedProjectID is a Project ID a consumer user requested an // access to. RequestedProjectID string `h:"Requested-Project-Id"` } // ToOAuth1RequestTokenHeaders formats a RequestTokenOpts into a map of request // headers. func (opts RequestTokenOpts) ToOAuth1RequestTokenHeaders(method, u string) (map[string]string, error) { q, err := buildOAuth1QueryString(opts, opts.OAuthTimestamp, "oob") if err != nil { return nil, err } h, err := gophercloud.BuildHeaders(opts) if err != nil { return nil, err } signatureKeys := []string{opts.OAuthConsumerSecret} stringToSign := buildStringToSign(method, u, q.Query()) signature := url.QueryEscape(signString(opts.OAuthSignatureMethod, stringToSign, signatureKeys)) authHeader := buildAuthHeader(q.Query(), signature) h["Authorization"] = authHeader return h, nil } // RequestToken requests an unauthorized OAuth1 Token. func RequestToken(client *gophercloud.ServiceClient, opts RequestTokenOptsBuilder) (r TokenResult) { h, err := opts.ToOAuth1RequestTokenHeaders("POST", requestTokenURL(client)) if err != nil { r.Err = err return } resp, err := client.Post(requestTokenURL(client), nil, nil, &gophercloud.RequestOpts{ MoreHeaders: h, OkCodes: []int{201}, KeepResponseBody: true, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) if r.Err != nil { return } defer resp.Body.Close() if v := r.Header.Get("Content-Type"); v != OAuth1TokenContentType { r.Err = fmt.Errorf("unsupported Content-Type: %q", v) return } r.Body, r.Err = ioutil.ReadAll(resp.Body) return } // AuthorizeTokenOptsBuilder allows extensions to add additional parameters to // the AuthorizeToken request. type AuthorizeTokenOptsBuilder interface { ToOAuth1AuthorizeTokenMap() (map[string]interface{}, error) } // AuthorizeTokenOpts provides options used to authorize a request token. type AuthorizeTokenOpts struct { Roles []Role `json:"roles"` } // Role is a struct representing a role object in a AuthorizeTokenOpts struct. type Role struct { ID string `json:"id,omitempty"` Name string `json:"name,omitempty"` } // ToOAuth1AuthorizeTokenMap formats an AuthorizeTokenOpts into an authorize token // request. func (opts AuthorizeTokenOpts) ToOAuth1AuthorizeTokenMap() (map[string]interface{}, error) { for _, r := range opts.Roles { if r == (Role{}) { return nil, fmt.Errorf("role must not be empty") } } return gophercloud.BuildRequestBody(opts, "") } // AuthorizeToken authorizes an unauthorized consumer token. func AuthorizeToken(client *gophercloud.ServiceClient, id string, opts AuthorizeTokenOptsBuilder) (r AuthorizeTokenResult) { b, err := opts.ToOAuth1AuthorizeTokenMap() if err != nil { r.Err = err return } resp, err := client.Put(authorizeTokenURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // CreateAccessTokenOptsBuilder allows extensions to add additional parameters // to the CreateAccessToken request. type CreateAccessTokenOptsBuilder interface { ToOAuth1CreateAccessTokenHeaders(string, string) (map[string]string, error) } // CreateAccessTokenOpts provides options used to create an OAuth1 token. type CreateAccessTokenOpts struct { // OAuthConsumerKey is the OAuth1 Consumer Key. OAuthConsumerKey string `q:"oauth_consumer_key" required:"true"` // OAuthConsumerSecret is the OAuth1 Consumer Secret. Used to generate // an OAuth1 request signature. OAuthConsumerSecret string `required:"true"` // OAuthToken is the OAuth1 Request Token. OAuthToken string `q:"oauth_token" required:"true"` // OAuthTokenSecret is the OAuth1 Request Token Secret. Used to generate // an OAuth1 request signature. OAuthTokenSecret string `required:"true"` // OAuthVerifier is the OAuth1 verification code. OAuthVerifier string `q:"oauth_verifier" required:"true"` // OAuthSignatureMethod is the OAuth1 signature method the Consumer used // to sign the request. Supported values are "HMAC-SHA1" or "PLAINTEXT". // "PLAINTEXT" is not recommended for production usage. OAuthSignatureMethod SignatureMethod `q:"oauth_signature_method" required:"true"` // OAuthTimestamp is an OAuth1 request timestamp. If nil, current Unix // timestamp will be used. OAuthTimestamp *time.Time // OAuthNonce is an OAuth1 request nonce. Nonce must be a random string, // uniquely generated for each request. Will be generated automatically // when it is not set. OAuthNonce string `q:"oauth_nonce"` } // ToOAuth1CreateAccessTokenHeaders formats a CreateAccessTokenOpts into a map of // request headers. func (opts CreateAccessTokenOpts) ToOAuth1CreateAccessTokenHeaders(method, u string) (map[string]string, error) { q, err := buildOAuth1QueryString(opts, opts.OAuthTimestamp, "") if err != nil { return nil, err } signatureKeys := []string{opts.OAuthConsumerSecret, opts.OAuthTokenSecret} stringToSign := buildStringToSign(method, u, q.Query()) signature := url.QueryEscape(signString(opts.OAuthSignatureMethod, stringToSign, signatureKeys)) authHeader := buildAuthHeader(q.Query(), signature) headers := map[string]string{ "Authorization": authHeader, } return headers, nil } // CreateAccessToken creates a new OAuth1 Access Token func CreateAccessToken(client *gophercloud.ServiceClient, opts CreateAccessTokenOptsBuilder) (r TokenResult) { h, err := opts.ToOAuth1CreateAccessTokenHeaders("POST", createAccessTokenURL(client)) if err != nil { r.Err = err return } resp, err := client.Post(createAccessTokenURL(client), nil, nil, &gophercloud.RequestOpts{ MoreHeaders: h, OkCodes: []int{201}, KeepResponseBody: true, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) if r.Err != nil { return } defer resp.Body.Close() if v := r.Header.Get("Content-Type"); v != OAuth1TokenContentType { r.Err = fmt.Errorf("unsupported Content-Type: %q", v) return } r.Body, r.Err = ioutil.ReadAll(resp.Body) return } // GetAccessToken retrieves details on a single OAuth1 access token by an ID. func GetAccessToken(client *gophercloud.ServiceClient, userID string, id string) (r GetAccessTokenResult) { resp, err := client.Get(userAccessTokenURL(client, userID, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // RevokeAccessToken revokes an OAuth1 access token. func RevokeAccessToken(client *gophercloud.ServiceClient, userID string, id string) (r RevokeAccessTokenResult) { resp, err := client.Delete(userAccessTokenURL(client, userID, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // ListAccessTokens enumerates authorized access tokens. func ListAccessTokens(client *gophercloud.ServiceClient, userID string) pagination.Pager { url := userAccessTokensURL(client, userID) return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { return AccessTokensPage{pagination.LinkedPageBase{PageResult: r}} }) } // ListAccessTokenRoles enumerates authorized access token roles. func ListAccessTokenRoles(client *gophercloud.ServiceClient, userID string, id string) pagination.Pager { url := userAccessTokenRolesURL(client, userID, id) return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { return AccessTokenRolesPage{pagination.LinkedPageBase{PageResult: r}} }) } // GetAccessTokenRole retrieves details on a single OAuth1 access token role by // an ID. func GetAccessTokenRole(client *gophercloud.ServiceClient, userID string, id string, roleID string) (r GetAccessTokenRoleResult) { resp, err := client.Get(userAccessTokenRoleURL(client, userID, id, roleID), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // The following are small helper functions used to help build the signature. // buildOAuth1QueryString builds a URLEncoded parameters string specific for // OAuth1-based requests. func buildOAuth1QueryString(opts interface{}, timestamp *time.Time, callback string) (*url.URL, error) { q, err := gophercloud.BuildQueryString(opts) if err != nil { return nil, err } query := q.Query() if timestamp != nil { // use provided timestamp query.Set("oauth_timestamp", strconv.FormatInt(timestamp.Unix(), 10)) } else { // use current timestamp query.Set("oauth_timestamp", strconv.FormatInt(time.Now().UTC().Unix(), 10)) } if query.Get("oauth_nonce") == "" { // when nonce is not set, generate a random one query.Set("oauth_nonce", strconv.FormatInt(rand.Int63(), 10)+query.Get("oauth_timestamp")) } if callback != "" { query.Set("oauth_callback", callback) } query.Set("oauth_version", "1.0") return &url.URL{RawQuery: query.Encode()}, nil } // buildStringToSign builds a string to be signed. func buildStringToSign(method string, u string, query url.Values) []byte { parsedURL, _ := url.Parse(u) p := parsedURL.Port() s := parsedURL.Scheme // Default scheme port must be stripped if s == "http" && p == "80" || s == "https" && p == "443" { parsedURL.Host = strings.TrimSuffix(parsedURL.Host, ":"+p) } // Ensure that URL doesn't contain queries parsedURL.RawQuery = "" v := strings.Join( []string{method, url.QueryEscape(parsedURL.String()), url.QueryEscape(query.Encode())}, "&") return []byte(v) } // signString signs a string using an OAuth1 signature method. func signString(signatureMethod SignatureMethod, strToSign []byte, signatureKeys []string) string { var key []byte for i, k := range signatureKeys { key = append(key, []byte(url.QueryEscape(k))...) if i == 0 { key = append(key, '&') } } var signedString string switch signatureMethod { case PLAINTEXT: signedString = string(key) default: h := hmac.New(sha1.New, key) h.Write(strToSign) signedString = base64.StdEncoding.EncodeToString(h.Sum(nil)) } return signedString } // buildAuthHeader generates an OAuth1 Authorization header with a signature // calculated using an OAuth1 signature method. func buildAuthHeader(query url.Values, signature string) string { var authHeader []string var keys []string for k := range query { keys = append(keys, k) } sort.Strings(keys) for _, k := range keys { for _, v := range query[k] { authHeader = append(authHeader, fmt.Sprintf("%s=%q", k, url.QueryEscape(v))) } } authHeader = append(authHeader, fmt.Sprintf("oauth_signature=%q", signature)) return "OAuth " + strings.Join(authHeader, ", ") } golang-github-gophercloud-gophercloud-0.12.0/openstack/identity/v3/extensions/oauth1/results.go000066400000000000000000000176171367513235700327730ustar00rootroot00000000000000package oauth1 import ( "encoding/json" "net/url" "time" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // Consumer represents a delegated authorization request between two // identities. type Consumer struct { ID string `json:"id"` Secret string `json:"secret"` Description string `json:"description"` } type consumerResult struct { gophercloud.Result } // CreateConsumerResult is the response from a Create operation. Call its // Extract method to interpret it as a Consumer. type CreateConsumerResult struct { consumerResult } // UpdateConsumerResult is the response from a Create operation. Call its // Extract method to interpret it as a Consumer. type UpdateConsumerResult struct { consumerResult } // DeleteConsumerResult is the response from a Delete operation. Call its // ExtractErr to determine if the request succeeded or failed. type DeleteConsumerResult struct { gophercloud.ErrResult } // ConsumersPage is a single page of Region results. type ConsumersPage struct { pagination.LinkedPageBase } // GetConsumerResult is the response from a Get operation. Call its Extract // method to interpret it as a Consumer. type GetConsumerResult struct { consumerResult } // IsEmpty determines whether or not a page of Consumers contains any results. func (c ConsumersPage) IsEmpty() (bool, error) { consumers, err := ExtractConsumers(c) return len(consumers) == 0, err } // NextPageURL extracts the "next" link from the links section of the result. func (c ConsumersPage) NextPageURL() (string, error) { var s struct { Links struct { Next string `json:"next"` Previous string `json:"previous"` } `json:"links"` } err := c.ExtractInto(&s) if err != nil { return "", err } return s.Links.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.(ConsumersPage)).ExtractInto(&s) return s.Consumers, err } // Extract interprets any consumer result as a Consumer. func (c consumerResult) Extract() (*Consumer, error) { var s struct { Consumer *Consumer `json:"consumer"` } err := c.ExtractInto(&s) return s.Consumer, err } // Token contains an OAuth1 token. type Token struct { // OAuthToken is the key value for the oauth token that the Identity API returns. OAuthToken string `q:"oauth_token"` // OAuthTokenSecret is the secret value associated with the OAuth Token. OAuthTokenSecret string `q:"oauth_token_secret"` // OAuthExpiresAt is the date and time when an OAuth token expires. OAuthExpiresAt *time.Time `q:"-"` } // TokenResult is a struct to handle // "Content-Type: application/x-www-form-urlencoded" response. type TokenResult struct { gophercloud.Result Body []byte } // Extract interprets any OAuth1 token result as a Token. func (r TokenResult) Extract() (*Token, error) { if r.Err != nil { return nil, r.Err } values, err := url.ParseQuery(string(r.Body)) if err != nil { return nil, err } token := &Token{ OAuthToken: values.Get("oauth_token"), OAuthTokenSecret: values.Get("oauth_token_secret"), } if v := values.Get("oauth_expires_at"); v != "" { if t, err := time.Parse(gophercloud.RFC3339Milli, v); err != nil { return nil, err } else { token.OAuthExpiresAt = &t } } return token, nil } // AuthorizedToken contains an OAuth1 authorized token info. type AuthorizedToken struct { // OAuthVerifier is the ID of the token verifier. OAuthVerifier string `json:"oauth_verifier"` } type AuthorizeTokenResult struct { gophercloud.Result } // Extract interprets AuthorizeTokenResult result as a AuthorizedToken. func (r AuthorizeTokenResult) Extract() (*AuthorizedToken, error) { var s struct { AuthorizedToken *AuthorizedToken `json:"token"` } err := r.ExtractInto(&s) return s.AuthorizedToken, err } // AccessToken represents an AccessToken response as a struct. type AccessToken struct { ID string `json:"id"` ConsumerID string `json:"consumer_id"` ProjectID string `json:"project_id"` AuthorizingUserID string `json:"authorizing_user_id"` ExpiresAt *time.Time `json:"-"` } func (r *AccessToken) UnmarshalJSON(b []byte) error { type tmp AccessToken var s struct { tmp ExpiresAt *gophercloud.JSONRFC3339Milli `json:"expires_at"` } err := json.Unmarshal(b, &s) if err != nil { return err } *r = AccessToken(s.tmp) if s.ExpiresAt != nil { t := time.Time(*s.ExpiresAt) r.ExpiresAt = &t } return nil } type GetAccessTokenResult struct { gophercloud.Result } // Extract interprets any GetAccessTokenResult result as an AccessToken. func (r GetAccessTokenResult) Extract() (*AccessToken, error) { var s struct { AccessToken *AccessToken `json:"access_token"` } err := r.ExtractInto(&s) return s.AccessToken, err } // RevokeAccessTokenResult is the response from a Delete operation. Call its // ExtractErr to determine if the request succeeded or failed. type RevokeAccessTokenResult struct { gophercloud.ErrResult } // AccessTokensPage is a single page of Access Tokens results. type AccessTokensPage struct { pagination.LinkedPageBase } // IsEmpty determines whether or not a an AccessTokensPage contains any results. func (r AccessTokensPage) IsEmpty() (bool, error) { accessTokens, err := ExtractAccessTokens(r) return len(accessTokens) == 0, err } // NextPageURL extracts the "next" link from the links section of the result. func (r AccessTokensPage) 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 } // ExtractAccessTokens returns a slice of AccessTokens contained in a single // page of results. func ExtractAccessTokens(r pagination.Page) ([]AccessToken, error) { var s struct { AccessTokens []AccessToken `json:"access_tokens"` } err := (r.(AccessTokensPage)).ExtractInto(&s) return s.AccessTokens, err } // AccessTokenRole represents an Access Token Role struct. type AccessTokenRole struct { ID string `json:"id"` Name string `json:"name"` DomainID string `json:"domain_id"` } // AccessTokenRolesPage is a single page of Access Token roles results. type AccessTokenRolesPage struct { pagination.LinkedPageBase } // IsEmpty determines whether or not a an AccessTokensPage contains any results. func (r AccessTokenRolesPage) IsEmpty() (bool, error) { accessTokenRoles, err := ExtractAccessTokenRoles(r) return len(accessTokenRoles) == 0, err } // NextPageURL extracts the "next" link from the links section of the result. func (r AccessTokenRolesPage) 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 } // ExtractAccessTokenRoles returns a slice of AccessTokenRole contained in a // single page of results. func ExtractAccessTokenRoles(r pagination.Page) ([]AccessTokenRole, error) { var s struct { AccessTokenRoles []AccessTokenRole `json:"roles"` } err := (r.(AccessTokenRolesPage)).ExtractInto(&s) return s.AccessTokenRoles, err } type GetAccessTokenRoleResult struct { gophercloud.Result } // Extract interprets any GetAccessTokenRoleResult result as an AccessTokenRole. func (r GetAccessTokenRoleResult) Extract() (*AccessTokenRole, error) { var s struct { AccessTokenRole *AccessTokenRole `json:"role"` } err := r.ExtractInto(&s) return s.AccessTokenRole, err } // OAuth1 is an OAuth1 object, returned in OAuth1 token result. type OAuth1 struct { AccessTokenID string `json:"access_token_id"` ConsumerID string `json:"consumer_id"` } // TokenExt represents an extension of the base token result. type TokenExt struct { OAuth1 OAuth1 `json:"OS-OAUTH1"` } golang-github-gophercloud-gophercloud-0.12.0/openstack/identity/v3/extensions/oauth1/testing/000077500000000000000000000000001367513235700324045ustar00rootroot00000000000000fixtures.go000066400000000000000000000360541367513235700345350ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/identity/v3/extensions/oauth1/testingpackage testing import ( "fmt" "net/http" "testing" "time" "github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/oauth1" tokens "github.com/gophercloud/gophercloud/openstack/identity/v3/tokens/testing" "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) const CreateConsumerRequest = ` { "consumer": { "description": "My consumer" } } ` const CreateConsumerResponse = ` { "consumer": { "secret": "secretsecret", "description": "My consumer", "id": "7fea2d", "links": { "self": "http://example.com/identity/v3/OS-OAUTH1/consumers/7fea2d" } } } ` const UpdateConsumerRequest = ` { "consumer": { "description": "My new consumer" } } ` const UpdateConsumerResponse = ` { "consumer": { "description": "My new consumer", "id": "7fea2d", "links": { "self": "http://example.com/identity/v3/OS-OAUTH1/consumers/7fea2d" } } } ` // GetConsumerOutput provides a Get result. const GetConsumerResponse = ` { "consumer": { "id": "7fea2d", "description": "My consumer", "links": { "self": "http://example.com/identity/v3/OS-OAUTH1/consumers/7fea2d" } } } ` // ListConsumersResponse provides a single page of Consumers results. const ListConsumersResponse = ` { "consumers": [ { "description": "My consumer", "id": "7fea2d", "links": { "self": "http://example.com/identity/v3/OS-OAUTH1/consumers/7fea2d" } }, { "id": "0c2a74", "links": { "self": "http://example.com/identity/v3/OS-OAUTH1/consumers/0c2a74" } } ], "links": { "next": null, "previous": null, "self": "http://example.com/identity/v3/OS-OAUTH1/consumers" } } ` const AuthorizeTokenRequest = ` { "roles": [ { "id": "a3b29b" }, { "id": "49993e" } ] } ` const AuthorizeTokenResponse = ` { "token": { "oauth_verifier": "8171" } } ` const GetUserAccessTokenResponse = ` { "access_token": { "consumer_id": "7fea2d", "id": "6be26a", "expires_at": "2013-09-11T06:07:51.501805Z", "links": { "roles": "http://example.com/identity/v3/users/ce9e07/OS-OAUTH1/access_tokens/6be26a/roles", "self": "http://example.com/identity/v3/users/ce9e07/OS-OAUTH1/access_tokens/6be26a" }, "project_id": "b9fca3", "authorizing_user_id": "ce9e07" } } ` const ListUserAccessTokensResponse = ` { "access_tokens": [ { "consumer_id": "7fea2d", "id": "6be26a", "expires_at": "2013-09-11T06:07:51.501805Z", "links": { "roles": "http://example.com/identity/v3/users/ce9e07/OS-OAUTH1/access_tokens/6be26a/roles", "self": "http://example.com/identity/v3/users/ce9e07/OS-OAUTH1/access_tokens/6be26a" }, "project_id": "b9fca3", "authorizing_user_id": "ce9e07" } ], "links": { "next": null, "previous": null, "self": "http://example.com/identity/v3/users/ce9e07/OS-OAUTH1/access_tokens" } } ` const ListUserAccessTokenRolesResponse = ` { "roles": [ { "id": "5ad150", "domain_id": "7cf37b", "links": { "self": "http://example.com/identity/v3/roles/5ad150" }, "name": "admin" }, { "id": "a62eb6", "domain_id": "7cf37b", "links": { "self": "http://example.com/identity/v3/roles/a62eb6" }, "name": "member" } ], "links": { "next": null, "previous": null, "self": "http://example.com/identity/v3/users/ce9e07/OS-OAUTH1/access_tokens/6be26a/roles" } } ` const ListUserAccessTokenRoleResponse = ` { "role": { "id": "5ad150", "domain_id": "7cf37b", "links": { "self": "http://example.com/identity/v3/roles/5ad150" }, "name": "admin" } } ` var tokenExpiresAt = time.Date(2013, time.September, 11, 06, 07, 51, 501805000, time.UTC) var UserAccessToken = oauth1.AccessToken{ ID: "6be26a", ConsumerID: "7fea2d", ProjectID: "b9fca3", AuthorizingUserID: "ce9e07", ExpiresAt: &tokenExpiresAt, } var UserAccessTokenRole = oauth1.AccessTokenRole{ ID: "5ad150", DomainID: "7cf37b", Name: "admin", } var UserAccessTokenRoleSecond = oauth1.AccessTokenRole{ ID: "a62eb6", DomainID: "7cf37b", Name: "member", } var ExpectedUserAccessTokensSlice = []oauth1.AccessToken{UserAccessToken} var ExpectedUserAccessTokenRolesSlice = []oauth1.AccessTokenRole{UserAccessTokenRole, UserAccessTokenRoleSecond} // HandleCreateConsumer creates an HTTP handler at `/OS-OAUTH1/consumers` on the // test handler mux that tests consumer creation. func HandleCreateConsumer(t *testing.T) { testhelper.Mux.HandleFunc("/OS-OAUTH1/consumers", 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, CreateConsumerRequest) w.WriteHeader(http.StatusCreated) _, err := fmt.Fprintf(w, CreateConsumerResponse) testhelper.AssertNoErr(t, err) }) } // HandleUpdateConsumer creates an HTTP handler at `/OS-OAUTH1/consumers/7fea2d` on the // test handler mux that tests consumer update. func HandleUpdateConsumer(t *testing.T) { testhelper.Mux.HandleFunc("/OS-OAUTH1/consumers/7fea2d", func(w http.ResponseWriter, r *http.Request) { testhelper.TestMethod(t, r, "PATCH") testhelper.TestHeader(t, r, "Content-Type", "application/json") testhelper.TestHeader(t, r, "Accept", "application/json") testhelper.TestJSONRequest(t, r, UpdateConsumerRequest) w.WriteHeader(http.StatusOK) _, err := fmt.Fprintf(w, UpdateConsumerResponse) testhelper.AssertNoErr(t, err) }) } // HandleDeleteConsumer creates an HTTP handler at `/OS-OAUTH1/consumers/7fea2d` on the // test handler mux that tests consumer deletion. func HandleDeleteConsumer(t *testing.T) { testhelper.Mux.HandleFunc("/OS-OAUTH1/consumers/7fea2d", func(w http.ResponseWriter, r *http.Request) { testhelper.TestMethod(t, r, "DELETE") testhelper.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.WriteHeader(http.StatusNoContent) }) } // HandleGetConsumer creates an HTTP handler at `/OS-OAUTH1/consumers/7fea2d` on the // test handler mux that responds with a single consumer. func HandleGetConsumer(t *testing.T) { testhelper.Mux.HandleFunc("/OS-OAUTH1/consumers/7fea2d", func(w http.ResponseWriter, r *http.Request) { testhelper.TestMethod(t, r, "GET") testhelper.TestHeader(t, r, "Accept", "application/json") testhelper.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, GetConsumerResponse) }) } var Consumer = oauth1.Consumer{ ID: "7fea2d", Description: "My consumer", Secret: "secretsecret", } var UpdatedConsumer = oauth1.Consumer{ ID: "7fea2d", Description: "My new consumer", } var FirstConsumer = oauth1.Consumer{ ID: "7fea2d", Description: "My consumer", } var SecondConsumer = oauth1.Consumer{ ID: "0c2a74", } // ExpectedConsumersSlice is the slice of consumers expected to be returned from ListOutput. var ExpectedConsumersSlice = []oauth1.Consumer{FirstConsumer, SecondConsumer} // HandleListConsumers creates an HTTP handler at `/OS-OAUTH1/consumers` on the // test handler mux that responds with a list of two consumers. func HandleListConsumers(t *testing.T) { testhelper.Mux.HandleFunc("/OS-OAUTH1/consumers", func(w http.ResponseWriter, r *http.Request) { testhelper.TestMethod(t, r, "GET") testhelper.TestHeader(t, r, "Accept", "application/json") testhelper.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ListConsumersResponse) }) } var Token = oauth1.Token{ OAuthToken: "29971f", OAuthTokenSecret: "238eb8", OAuthExpiresAt: &tokenExpiresAt, } // HandleRequestToken creates an HTTP handler at `/OS-OAUTH1/request_token` on the // test handler mux that responds with a OAuth1 unauthorized token. func HandleRequestToken(t *testing.T) { testhelper.Mux.HandleFunc("/OS-OAUTH1/request_token", func(w http.ResponseWriter, r *http.Request) { testhelper.TestMethod(t, r, "POST") testhelper.TestHeader(t, r, "Accept", "application/json") testhelper.TestHeader(t, r, "X-Auth-Token", client.TokenID) testhelper.TestHeader(t, r, "Authorization", `OAuth oauth_callback="oob", oauth_consumer_key="7fea2d", oauth_nonce="71416001758914252991586795052", oauth_signature_method="HMAC-SHA1", oauth_timestamp="0", oauth_version="1.0", oauth_signature="jCSPVryCYF52Ks0VNNmBmeKSGuw%3D"`) testhelper.TestHeader(t, r, "Requested-Project-Id", "1df927e8a466498f98788ed73d3c8ab4") testhelper.TestBody(t, r, "") w.Header().Set("Content-Type", oauth1.OAuth1TokenContentType) w.WriteHeader(http.StatusCreated) fmt.Fprintf(w, `oauth_token=29971f&oauth_token_secret=238eb8&oauth_expires_at=2013-09-11T06:07:51.501805Z`) }) } // HandleAuthorizeToken creates an HTTP handler at `/OS-OAUTH1/authorize/29971f` on the // test handler mux that tests unauthorized token authorization. func HandleAuthorizeToken(t *testing.T) { testhelper.Mux.HandleFunc("/OS-OAUTH1/authorize/29971f", func(w http.ResponseWriter, r *http.Request) { testhelper.TestMethod(t, r, "PUT") testhelper.TestHeader(t, r, "Content-Type", "application/json") testhelper.TestHeader(t, r, "Accept", "application/json") testhelper.TestJSONRequest(t, r, AuthorizeTokenRequest) w.WriteHeader(http.StatusOK) _, err := fmt.Fprintf(w, AuthorizeTokenResponse) testhelper.AssertNoErr(t, err) }) } var AccessToken = oauth1.Token{ OAuthToken: "accd36", OAuthTokenSecret: "aa47da", OAuthExpiresAt: &tokenExpiresAt, } // HandleCreateAccessToken creates an HTTP handler at `/OS-OAUTH1/access_token` on the // test handler mux that responds with a OAuth1 access token. func HandleCreateAccessToken(t *testing.T) { testhelper.Mux.HandleFunc("/OS-OAUTH1/access_token", func(w http.ResponseWriter, r *http.Request) { testhelper.TestMethod(t, r, "POST") testhelper.TestHeader(t, r, "Accept", "application/json") testhelper.TestHeader(t, r, "X-Auth-Token", client.TokenID) testhelper.TestHeader(t, r, "Authorization", `OAuth oauth_consumer_key="7fea2d", oauth_nonce="66148873158553341551586804894", oauth_signature_method="HMAC-SHA1", oauth_timestamp="1586804894", oauth_token="29971f", oauth_verifier="8171", oauth_version="1.0", oauth_signature="usQ89Y3IYG0IBE7%2Ft8aVsc8XgEk%3D"`) testhelper.TestBody(t, r, "") w.Header().Set("Content-Type", oauth1.OAuth1TokenContentType) w.WriteHeader(http.StatusCreated) fmt.Fprintf(w, `oauth_token=accd36&oauth_token_secret=aa47da&oauth_expires_at=2013-09-11T06:07:51.501805Z`) }) } // HandleGetAccessToken creates an HTTP handler at `/users/ce9e07/OS-OAUTH1/access_tokens/6be26a` on the // test handler mux that responds with a single access token. func HandleGetAccessToken(t *testing.T) { testhelper.Mux.HandleFunc("/users/ce9e07/OS-OAUTH1/access_tokens/6be26a", func(w http.ResponseWriter, r *http.Request) { testhelper.TestMethod(t, r, "GET") testhelper.TestHeader(t, r, "Accept", "application/json") testhelper.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, GetUserAccessTokenResponse) }) } // HandleRevokeAccessToken creates an HTTP handler at `/users/ce9e07/OS-OAUTH1/access_tokens/6be26a` on the // test handler mux that tests access token deletion. func HandleRevokeAccessToken(t *testing.T) { testhelper.Mux.HandleFunc("/users/ce9e07/OS-OAUTH1/access_tokens/6be26a", func(w http.ResponseWriter, r *http.Request) { testhelper.TestMethod(t, r, "DELETE") testhelper.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.WriteHeader(http.StatusNoContent) }) } // HandleListAccessTokens creates an HTTP handler at `/users/ce9e07/OS-OAUTH1/access_tokens` on the // test handler mux that responds with a slice of access tokens. func HandleListAccessTokens(t *testing.T) { testhelper.Mux.HandleFunc("/users/ce9e07/OS-OAUTH1/access_tokens", func(w http.ResponseWriter, r *http.Request) { testhelper.TestMethod(t, r, "GET") testhelper.TestHeader(t, r, "Accept", "application/json") testhelper.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ListUserAccessTokensResponse) }) } // HandleListAccessTokenRoles creates an HTTP handler at `/users/ce9e07/OS-OAUTH1/access_tokens/6be26a/roles` on the // test handler mux that responds with a slice of access token roles. func HandleListAccessTokenRoles(t *testing.T) { testhelper.Mux.HandleFunc("/users/ce9e07/OS-OAUTH1/access_tokens/6be26a/roles", func(w http.ResponseWriter, r *http.Request) { testhelper.TestMethod(t, r, "GET") testhelper.TestHeader(t, r, "Accept", "application/json") testhelper.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ListUserAccessTokenRolesResponse) }) } // HandleGetAccessTokenRole creates an HTTP handler at `/users/ce9e07/OS-OAUTH1/access_tokens/6be26a/roles/5ad150` on the // test handler mux that responds with an access token role. func HandleGetAccessTokenRole(t *testing.T) { testhelper.Mux.HandleFunc("/users/ce9e07/OS-OAUTH1/access_tokens/6be26a/roles/5ad150", func(w http.ResponseWriter, r *http.Request) { testhelper.TestMethod(t, r, "GET") testhelper.TestHeader(t, r, "Accept", "application/json") testhelper.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ListUserAccessTokenRoleResponse) }) } // HandleAuthenticate creates an HTTP handler at `/auth/tokens` on the // test handler mux that responds with an OpenStack token. func HandleAuthenticate(t *testing.T) { 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.TestHeader(t, r, "Authorization", `OAuth oauth_consumer_key="7fea2d", oauth_nonce="66148873158553341551586804894", oauth_signature_method="HMAC-SHA1", oauth_timestamp="0", oauth_token="accd36", oauth_version="1.0", oauth_signature="JgMHu4e7rXGlqz3A%2FLhHDMvtjp8%3D"`) testhelper.TestJSONRequest(t, r, `{"auth": {"identity": {"oauth1": {}, "methods": ["oauth1"]}}}`) w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) fmt.Fprintf(w, tokens.TokenOutput) }) } requests_test.go000066400000000000000000000157601367513235700355770ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/identity/v3/extensions/oauth1/testingpackage testing import ( "testing" "time" "github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/oauth1" "github.com/gophercloud/gophercloud/openstack/identity/v3/tokens" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) func TestCreateConsumer(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleCreateConsumer(t) consumer, err := oauth1.CreateConsumer(client.ServiceClient(), oauth1.CreateConsumerOpts{ Description: "My consumer", }).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, Consumer, *consumer) } func TestUpdateConsumer(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleUpdateConsumer(t) consumer, err := oauth1.UpdateConsumer(client.ServiceClient(), "7fea2d", oauth1.UpdateConsumerOpts{ Description: "My new consumer", }).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, UpdatedConsumer, *consumer) } func TestDeleteConsumer(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleDeleteConsumer(t) err := oauth1.DeleteConsumer(client.ServiceClient(), "7fea2d").ExtractErr() th.AssertNoErr(t, err) } func TestGetConsumer(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleGetConsumer(t) consumer, err := oauth1.GetConsumer(client.ServiceClient(), "7fea2d").Extract() th.AssertNoErr(t, err) th.AssertEquals(t, FirstConsumer, *consumer) } func TestListConsumers(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleListConsumers(t) count := 0 err := oauth1.ListConsumers(client.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) { count++ actual, err := oauth1.ExtractConsumers(page) th.AssertNoErr(t, err) th.CheckDeepEquals(t, ExpectedConsumersSlice, actual) return true, nil }) th.AssertNoErr(t, err) th.CheckEquals(t, count, 1) } func TestListConsumersAllPages(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleListConsumers(t) allPages, err := oauth1.ListConsumers(client.ServiceClient()).AllPages() th.AssertNoErr(t, err) actual, err := oauth1.ExtractConsumers(allPages) th.AssertNoErr(t, err) th.CheckDeepEquals(t, ExpectedConsumersSlice, actual) } func TestRequestToken(t *testing.T) { th.SetupPersistentPortHTTP(t, 33199) defer th.TeardownHTTP() HandleRequestToken(t) ts := time.Unix(0, 0) token, err := oauth1.RequestToken(client.ServiceClient(), oauth1.RequestTokenOpts{ OAuthConsumerKey: Consumer.ID, OAuthConsumerSecret: Consumer.Secret, OAuthSignatureMethod: oauth1.HMACSHA1, OAuthTimestamp: &ts, OAuthNonce: "71416001758914252991586795052", RequestedProjectID: "1df927e8a466498f98788ed73d3c8ab4", }).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, Token, *token) } func TestAuthorizeToken(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleAuthorizeToken(t) token, err := oauth1.AuthorizeToken(client.ServiceClient(), "29971f", oauth1.AuthorizeTokenOpts{ Roles: []oauth1.Role{ { ID: "a3b29b", }, { ID: "49993e", }, }, }).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, "8171", token.OAuthVerifier) } func TestCreateAccessToken(t *testing.T) { th.SetupPersistentPortHTTP(t, 33199) defer th.TeardownHTTP() HandleCreateAccessToken(t) ts := time.Unix(1586804894, 0) token, err := oauth1.CreateAccessToken(client.ServiceClient(), oauth1.CreateAccessTokenOpts{ OAuthConsumerKey: Consumer.ID, OAuthConsumerSecret: Consumer.Secret, OAuthToken: Token.OAuthToken, OAuthTokenSecret: Token.OAuthTokenSecret, OAuthVerifier: "8171", OAuthSignatureMethod: oauth1.HMACSHA1, OAuthTimestamp: &ts, OAuthNonce: "66148873158553341551586804894", }).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, AccessToken, *token) } func TestGetAccessToken(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleGetAccessToken(t) token, err := oauth1.GetAccessToken(client.ServiceClient(), "ce9e07", "6be26a").Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, UserAccessToken, *token) } func TestRevokeAccessToken(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleRevokeAccessToken(t) err := oauth1.RevokeAccessToken(client.ServiceClient(), "ce9e07", "6be26a").ExtractErr() th.AssertNoErr(t, err) } func TestListAccessTokens(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleListAccessTokens(t) count := 0 err := oauth1.ListAccessTokens(client.ServiceClient(), "ce9e07").EachPage(func(page pagination.Page) (bool, error) { count++ actual, err := oauth1.ExtractAccessTokens(page) th.AssertNoErr(t, err) th.CheckDeepEquals(t, ExpectedUserAccessTokensSlice, actual) return true, nil }) th.AssertNoErr(t, err) th.CheckEquals(t, count, 1) } func TestListAccessTokensAllPages(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleListAccessTokens(t) allPages, err := oauth1.ListAccessTokens(client.ServiceClient(), "ce9e07").AllPages() th.AssertNoErr(t, err) actual, err := oauth1.ExtractAccessTokens(allPages) th.AssertNoErr(t, err) th.CheckDeepEquals(t, ExpectedUserAccessTokensSlice, actual) } func TestListAccessTokenRoles(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleListAccessTokenRoles(t) count := 0 err := oauth1.ListAccessTokenRoles(client.ServiceClient(), "ce9e07", "6be26a").EachPage(func(page pagination.Page) (bool, error) { count++ actual, err := oauth1.ExtractAccessTokenRoles(page) th.AssertNoErr(t, err) th.CheckDeepEquals(t, ExpectedUserAccessTokenRolesSlice, actual) return true, nil }) th.AssertNoErr(t, err) th.CheckEquals(t, count, 1) } func TestListAccessTokenRolesAllPages(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleListAccessTokenRoles(t) allPages, err := oauth1.ListAccessTokenRoles(client.ServiceClient(), "ce9e07", "6be26a").AllPages() th.AssertNoErr(t, err) actual, err := oauth1.ExtractAccessTokenRoles(allPages) th.AssertNoErr(t, err) th.CheckDeepEquals(t, ExpectedUserAccessTokenRolesSlice, actual) } func TestGetAccessTokenRole(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleGetAccessTokenRole(t) role, err := oauth1.GetAccessTokenRole(client.ServiceClient(), "ce9e07", "6be26a", "5ad150").Extract() th.AssertNoErr(t, err) th.AssertEquals(t, UserAccessTokenRole, *role) } func TestAuthenticate(t *testing.T) { th.SetupPersistentPortHTTP(t, 33199) defer th.TeardownHTTP() HandleAuthenticate(t) expected := &tokens.Token{ ExpiresAt: time.Date(2017, 6, 3, 2, 19, 49, 0, time.UTC), } ts := time.Unix(0, 0) options := &oauth1.AuthOptions{ OAuthConsumerKey: Consumer.ID, OAuthConsumerSecret: Consumer.Secret, OAuthToken: AccessToken.OAuthToken, OAuthTokenSecret: AccessToken.OAuthTokenSecret, OAuthSignatureMethod: oauth1.HMACSHA1, OAuthTimestamp: &ts, OAuthNonce: "66148873158553341551586804894", } actual, err := oauth1.Create(client.ServiceClient(), options).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, expected, actual) } golang-github-gophercloud-gophercloud-0.12.0/openstack/identity/v3/extensions/oauth1/urls.go000066400000000000000000000026311367513235700322450ustar00rootroot00000000000000package oauth1 import "github.com/gophercloud/gophercloud" func consumersURL(c *gophercloud.ServiceClient) string { return c.ServiceURL("OS-OAUTH1", "consumers") } func consumerURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL("OS-OAUTH1", "consumers", id) } func requestTokenURL(c *gophercloud.ServiceClient) string { return c.ServiceURL("OS-OAUTH1", "request_token") } func authorizeTokenURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL("OS-OAUTH1", "authorize", id) } func createAccessTokenURL(c *gophercloud.ServiceClient) string { return c.ServiceURL("OS-OAUTH1", "access_token") } func userAccessTokensURL(c *gophercloud.ServiceClient, userID string) string { return c.ServiceURL("users", userID, "OS-OAUTH1", "access_tokens") } func userAccessTokenURL(c *gophercloud.ServiceClient, userID string, id string) string { return c.ServiceURL("users", userID, "OS-OAUTH1", "access_tokens", id) } func userAccessTokenRolesURL(c *gophercloud.ServiceClient, userID string, id string) string { return c.ServiceURL("users", userID, "OS-OAUTH1", "access_tokens", id, "roles") } func userAccessTokenRoleURL(c *gophercloud.ServiceClient, userID string, id string, roleID string) string { return c.ServiceURL("users", userID, "OS-OAUTH1", "access_tokens", id, "roles", roleID) } func authURL(c *gophercloud.ServiceClient) string { return c.ServiceURL("auth", "tokens") } golang-github-gophercloud-gophercloud-0.12.0/openstack/identity/v3/extensions/trusts/000077500000000000000000000000001367513235700310725ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/identity/v3/extensions/trusts/doc.go000066400000000000000000000036151367513235700321730ustar00rootroot00000000000000/* 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) } Example to Create a Trust expiresAt := time.Date(2019, 12, 1, 14, 0, 0, 999999999, time.UTC) createOpts := trusts.CreateOpts{ ExpiresAt: &expiresAt, Impersonation: true, AllowRedelegation: true, ProjectID: "9b71012f5a4a4aef9193f1995fe159b2", Roles: []trusts.Role{ { Name: "member", }, }, TrusteeUserID: "ecb37e88cc86431c99d0332208cb6fbf", TrustorUserID: "959ed913a32c4ec88c041c98e61cbbc3", } trust, err := trusts.Create(identityClient, createOpts).Extract() if err != nil { panic(err) } fmt.Printf("Trust: %+v\n", trust) Example to Delete a Trust trustID := "3422b7c113894f5d90665e1a79655e23" err := trusts.Delete(identityClient, trustID).ExtractErr() if err != nil { panic(err) } Example to Get a Trust trustID := "3422b7c113894f5d90665e1a79655e23" err := trusts.Get(identityClient, trustID).ExtractErr() if err != nil { panic(err) } Example to List a Trust listOpts := trusts.ListOpts{ TrustorUserId: "3422b7c113894f5d90665e1a79655e23", } allPages, err := trusts.List(identityClient, listOpts).AllPages() if err != nil { panic(err) } allTrusts, err := trusts.ExtractTrusts(allPages) if err != nil { panic(err) } for _, trust := range allTrusts { fmt.Printf("%+v\n", region) } */ package trusts golang-github-gophercloud-gophercloud-0.12.0/openstack/identity/v3/extensions/trusts/requests.go000066400000000000000000000130041367513235700332720ustar00rootroot00000000000000package trusts import ( "time" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack/identity/v3/tokens" "github.com/gophercloud/gophercloud/pagination" ) // 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() } // CreateOptsBuilder allows extensions to add additional parameters to // the Create request. type CreateOptsBuilder interface { ToTrustCreateMap() (map[string]interface{}, error) } // CreateOpts provides options used to create a new trust. type CreateOpts struct { // Impersonation allows the trustee to impersonate the trustor. Impersonation bool `json:"impersonation"` // TrusteeUserID is a user who is capable of consuming the trust. TrusteeUserID string `json:"trustee_user_id" required:"true"` // TrustorUserID is a user who created the trust. TrustorUserID string `json:"trustor_user_id" required:"true"` // AllowRedelegation enables redelegation of a trust. AllowRedelegation bool `json:"allow_redelegation,omitempty"` // ExpiresAt sets expiration time on trust. ExpiresAt *time.Time `json:"-"` // ProjectID identifies the project. ProjectID string `json:"project_id,omitempty"` // RedelegationCount specifies a depth of the redelegation chain. RedelegationCount int `json:"redelegation_count,omitempty"` // RemainingUses specifies how many times a trust can be used to get a token. RemainingUses int `json:"remaining_uses,omitempty"` // Roles specifies roles that need to be granted to trustee. Roles []Role `json:"roles,omitempty"` } // ToTrustCreateMap formats a CreateOpts into a create request. func (opts CreateOpts) ToTrustCreateMap() (map[string]interface{}, error) { parent := "trust" b, err := gophercloud.BuildRequestBody(opts, parent) if err != nil { return nil, err } if opts.ExpiresAt != nil { if v, ok := b[parent].(map[string]interface{}); ok { v["expires_at"] = opts.ExpiresAt.Format(gophercloud.RFC3339Milli) } } return b, nil } type ListOptsBuilder interface { ToTrustListQuery() (string, error) } // ListOpts provides options to filter the List results. type ListOpts struct { // TrustorUserID filters the response by a trustor user Id. TrustorUserID string `q:"trustor_user_id"` // TrusteeUserID filters the response by a trustee user Id. TrusteeUserID string `q:"trustee_user_id"` } // ToTrustListQuery formats a ListOpts into a query string. func (opts ListOpts) ToTrustListQuery() (string, error) { q, err := gophercloud.BuildQueryString(opts) return q.String(), err } // Create creates a new Trust. func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToTrustCreateMap() if err != nil { r.Err = err return } resp, err := client.Post(createURL(client), &b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{201}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete deletes a Trust. func Delete(client *gophercloud.ServiceClient, trustID string) (r DeleteResult) { resp, err := client.Delete(deleteURL(client, trustID), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // List enumerates the Trust 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.ToTrustListQuery() if err != nil { return pagination.Pager{Err: err} } url += query } return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { return TrustPage{pagination.LinkedPageBase{PageResult: r}} }) } // Get retrieves details on a single Trust, by ID. func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { resp, err := client.Get(resourceURL(client, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // ListRoles lists roles delegated by a Trust. func ListRoles(client *gophercloud.ServiceClient, id string) pagination.Pager { url := listRolesURL(client, id) return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { return RolesPage{pagination.LinkedPageBase{PageResult: r}} }) } // GetRole retrieves details on a single role delegated by a Trust. func GetRole(client *gophercloud.ServiceClient, id string, roleID string) (r GetRoleResult) { resp, err := client.Get(getRoleURL(client, id, roleID), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // CheckRole checks whether a role ID is delegated by a Trust. func CheckRole(client *gophercloud.ServiceClient, id string, roleID string) (r CheckRoleResult) { resp, err := client.Head(getRoleURL(client, id, roleID), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } golang-github-gophercloud-gophercloud-0.12.0/openstack/identity/v3/extensions/trusts/results.go000066400000000000000000000076701367513235700331340ustar00rootroot00000000000000package trusts import ( "time" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) type trustResult struct { gophercloud.Result } // CreateResult is the response from a Create operation. Call its Extract method // to interpret it as a Trust. type CreateResult struct { trustResult } // DeleteResult is the response from a Delete operation. Call its ExtractErr to // determine if the request succeeded or failed. type DeleteResult struct { gophercloud.ErrResult } // TrustPage is a single page of Region results. type TrustPage struct { pagination.LinkedPageBase } // GetResult is the response from a Get operation. Call its Extract method // to interpret it as a Trust. type GetResult struct { trustResult } // IsEmpty determines whether or not a page of Trusts contains any results. func (t TrustPage) IsEmpty() (bool, error) { roles, err := ExtractTrusts(t) return len(roles) == 0, err } // NextPageURL extracts the "next" link from the links section of the result. func (t TrustPage) NextPageURL() (string, error) { var s struct { Links struct { Next string `json:"next"` Previous string `json:"previous"` } `json:"links"` } err := t.ExtractInto(&s) if err != nil { return "", err } return s.Links.Next, err } // ExtractProjects returns a slice of Trusts contained in a single page of // results. func ExtractTrusts(r pagination.Page) ([]Trust, error) { var s struct { Trusts []Trust `json:"trusts"` } err := (r.(TrustPage)).ExtractInto(&s) return s.Trusts, err } // Extract interprets any trust result as a Trust. func (t trustResult) Extract() (*Trust, error) { var s struct { Trust *Trust `json:"trust"` } err := t.ExtractInto(&s) return s.Trust, err } // Trust represents a delegated authorization request between two // identities. type Trust struct { ID string `json:"id"` Impersonation bool `json:"impersonation"` TrusteeUserID string `json:"trustee_user_id"` TrustorUserID string `json:"trustor_user_id"` RedelegatedTrustID string `json:"redelegated_trust_id"` RedelegationCount int `json:"redelegation_count,omitempty"` AllowRedelegation bool `json:"allow_redelegation,omitempty"` ProjectID string `json:"project_id,omitempty"` RemainingUses int `json:"remaining_uses,omitempty"` Roles []Role `json:"roles,omitempty"` DeletedAt time.Time `json:"deleted_at"` ExpiresAt time.Time `json:"expires_at"` } // Role specifies a single role that is granted to a trustee. type Role struct { ID string `json:"id,omitempty"` Name string `json:"name,omitempty"` } // TokenExt represents an extension of the base token result. type TokenExt struct { Trust Trust `json:"OS-TRUST:trust"` } // RolesPage is a single page of Trust roles results. type RolesPage struct { pagination.LinkedPageBase } // IsEmpty determines whether or not a a Page contains any results. func (r RolesPage) IsEmpty() (bool, error) { accessTokenRoles, err := ExtractRoles(r) return len(accessTokenRoles) == 0, err } // NextPageURL extracts the "next" link from the links section of the result. func (r RolesPage) 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 } // ExtractRoles returns a slice of Role contained in a single page of results. func ExtractRoles(r pagination.Page) ([]Role, error) { var s struct { Roles []Role `json:"roles"` } err := (r.(RolesPage)).ExtractInto(&s) return s.Roles, err } type GetRoleResult struct { gophercloud.Result } // Extract interprets any GetRoleResult result as an Role. func (r GetRoleResult) Extract() (*Role, error) { var s struct { Role *Role `json:"role"` } err := r.ExtractInto(&s) return s.Role, err } type CheckRoleResult struct { gophercloud.ErrResult } golang-github-gophercloud-gophercloud-0.12.0/openstack/identity/v3/extensions/trusts/testing/000077500000000000000000000000001367513235700325475ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/identity/v3/extensions/trusts/testing/doc.go000066400000000000000000000000451367513235700336420ustar00rootroot00000000000000// trusts unit tests package testing fixtures.go000066400000000000000000000313271367513235700346760ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/identity/v3/extensions/trusts/testingpackage testing import ( "fmt" "net/http" "testing" "time" "github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/trusts" "github.com/gophercloud/gophercloud/openstack/identity/v3/tokens" "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) const CreateRequest = ` { "trust": { "expires_at": "2019-12-01T14:00:00Z", "impersonation": false, "allow_redelegation": true, "project_id": "9b71012f5a4a4aef9193f1995fe159b2", "roles": [ { "name": "member" } ], "trustee_user_id": "ecb37e88cc86431c99d0332208cb6fbf", "trustor_user_id": "959ed913a32c4ec88c041c98e61cbbc3" } } ` const CreateRequestNoExpire = ` { "trust": { "impersonation": false, "allow_redelegation": true, "project_id": "9b71012f5a4a4aef9193f1995fe159b2", "roles": [ { "name": "member" } ], "trustee_user_id": "ecb37e88cc86431c99d0332208cb6fbf", "trustor_user_id": "959ed913a32c4ec88c041c98e61cbbc3" } } ` const CreateResponse = ` { "trust": { "expires_at": "2019-12-01T14:00:00.000000Z", "id": "3422b7c113894f5d90665e1a79655e23", "impersonation": false, "redelegation_count": 10, "project_id": "9b71012f5a4a4aef9193f1995fe159b2", "remaining_uses": null, "roles": [ { "id": "b627fca5-beb0-471a-9857-0e852b719e76", "links": { "self": "http://example.com/identity/v3/roles/b627fca5-beb0-471a-9857-0e852b719e76" }, "name": "member" } ], "trustee_user_id": "ecb37e88cc86431c99d0332208cb6fbf", "trustor_user_id": "959ed913a32c4ec88c041c98e61cbbc3" } } ` const CreateResponseNoExpire = ` { "trust": { "id": "3422b7c113894f5d90665e1a79655e23", "impersonation": false, "redelegation_count": 10, "project_id": "9b71012f5a4a4aef9193f1995fe159b2", "remaining_uses": null, "roles": [ { "id": "b627fca5-beb0-471a-9857-0e852b719e76", "links": { "self": "http://example.com/identity/v3/roles/b627fca5-beb0-471a-9857-0e852b719e76" }, "name": "member" } ], "trustee_user_id": "ecb37e88cc86431c99d0332208cb6fbf", "trustor_user_id": "959ed913a32c4ec88c041c98e61cbbc3" } } ` // GetOutput provides a Get result. const GetResponse = ` { "trust": { "id": "987fe8", "expires_at": "2013-02-27T18:30:59.000000Z", "impersonation": true, "links": { "self": "http://example.com/identity/v3/OS-TRUST/trusts/987fe8" }, "roles": [ { "id": "ed7b78", "links": { "self": "http://example.com/identity/v3/roles/ed7b78" }, "name": "member" } ], "roles_links": { "next": null, "previous": null, "self": "http://example.com/identity/v3/OS-TRUST/trusts/1ff900/roles" }, "project_id": "0f1233", "trustee_user_id": "be34d1", "trustor_user_id": "56ae32" } } ` // ListOutput provides a single page of Role results. const ListResponse = ` { "trusts": [ { "id": "1ff900", "expires_at": "2019-12-01T14:00:00.000000Z", "impersonation": true, "links": { "self": "http://example.com/identity/v3/OS-TRUST/trusts/1ff900" }, "project_id": "0f1233", "trustee_user_id": "86c0d5", "trustor_user_id": "a0fdfd" }, { "id": "f4513a", "impersonation": false, "links": { "self": "http://example.com/identity/v3/OS-TRUST/trusts/f45513a" }, "project_id": "0f1233", "trustee_user_id": "86c0d5", "trustor_user_id": "3cd2ce" } ] } ` const ListTrustRolesResponse = ` { "roles": [ { "id": "c1648e", "links": { "self": "http://example.com/identity/v3/roles/c1648e" }, "name": "manager" }, { "id": "ed7b78", "links": { "self": "http://example.com/identity/v3/roles/ed7b78" }, "name": "member" } ] } ` const GetTrustRoleResponse = ` { "role": { "id": "c1648e", "links": { "self": "http://example.com/identity/v3/roles/c1648e" }, "name": "manager" } } ` var FirstRole = trusts.Role{ ID: "c1648e", Name: "manager", } var SecondRole = trusts.Role{ ID: "ed7b78", Name: "member", } var ExpectedTrustRolesSlice = []trusts.Role{FirstRole, SecondRole} // 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" } } }`) }) } // HandleCreateTrust creates an HTTP handler at `/OS-TRUST/trusts` on the // test handler mux that tests trust creation. func HandleCreateTrust(t *testing.T) { testhelper.Mux.HandleFunc("/OS-TRUST/trusts", func(w http.ResponseWriter, r *http.Request) { testhelper.TestMethod(t, r, "POST") testhelper.TestHeader(t, r, "X-Auth-Token", client.TokenID) testhelper.TestJSONRequest(t, r, CreateRequest) w.WriteHeader(http.StatusCreated) _, err := fmt.Fprintf(w, CreateResponse) testhelper.AssertNoErr(t, err) }) } // HandleCreateTrustNoExpire creates an HTTP handler at `/OS-TRUST/trusts` on the // test handler mux that tests trust creation. func HandleCreateTrustNoExpire(t *testing.T) { testhelper.Mux.HandleFunc("/OS-TRUST/trusts", func(w http.ResponseWriter, r *http.Request) { testhelper.TestMethod(t, r, "POST") testhelper.TestHeader(t, r, "X-Auth-Token", client.TokenID) testhelper.TestJSONRequest(t, r, CreateRequestNoExpire) w.WriteHeader(http.StatusCreated) _, err := fmt.Fprintf(w, CreateResponseNoExpire) testhelper.AssertNoErr(t, err) }) } // HandleDeleteUserSuccessfully creates an HTTP handler at `/users` on the // test handler mux that tests user deletion. func HandleDeleteTrust(t *testing.T) { testhelper.Mux.HandleFunc("/OS-TRUST/trusts/3422b7c113894f5d90665e1a79655e23", func(w http.ResponseWriter, r *http.Request) { testhelper.TestMethod(t, r, "DELETE") testhelper.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.WriteHeader(http.StatusNoContent) }) } // HandleGetTrustSuccessfully creates an HTTP handler at `/OS-TRUST/trusts` on the // test handler mux that responds with a single trusts. func HandleGetTrustSuccessfully(t *testing.T) { testhelper.Mux.HandleFunc("/OS-TRUST/trusts/987fe8", func(w http.ResponseWriter, r *http.Request) { testhelper.TestMethod(t, r, "GET") testhelper.TestHeader(t, r, "Accept", "application/json") testhelper.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, GetResponse) }) } var FirstTrust = trusts.Trust{ ID: "1ff900", Impersonation: true, TrusteeUserID: "86c0d5", TrustorUserID: "a0fdfd", ProjectID: "0f1233", ExpiresAt: time.Date(2019, 12, 01, 14, 00, 00, 0, time.UTC), DeletedAt: time.Time{}, } var SecondTrust = trusts.Trust{ ID: "f4513a", Impersonation: false, TrusteeUserID: "86c0d5", TrustorUserID: "3cd2ce", ProjectID: "0f1233", ExpiresAt: time.Time{}, DeletedAt: time.Time{}, } var CreatedTrust = trusts.Trust{ ID: "3422b7c113894f5d90665e1a79655e23", Impersonation: false, TrusteeUserID: "ecb37e88cc86431c99d0332208cb6fbf", TrustorUserID: "959ed913a32c4ec88c041c98e61cbbc3", ProjectID: "9b71012f5a4a4aef9193f1995fe159b2", ExpiresAt: time.Date(2019, 12, 01, 14, 00, 00, 0, time.UTC), DeletedAt: time.Time{}, RedelegationCount: 10, Roles: []trusts.Role{ { ID: "b627fca5-beb0-471a-9857-0e852b719e76", Name: "member", }, }, } var CreatedTrustNoExpire = trusts.Trust{ ID: "3422b7c113894f5d90665e1a79655e23", Impersonation: false, TrusteeUserID: "ecb37e88cc86431c99d0332208cb6fbf", TrustorUserID: "959ed913a32c4ec88c041c98e61cbbc3", ProjectID: "9b71012f5a4a4aef9193f1995fe159b2", DeletedAt: time.Time{}, RedelegationCount: 10, Roles: []trusts.Role{ { ID: "b627fca5-beb0-471a-9857-0e852b719e76", Name: "member", }, }, } // ExpectedRolesSlice is the slice of roles expected to be returned from ListOutput. var ExpectedTrustsSlice = []trusts.Trust{FirstTrust, SecondTrust} // HandleListTrustsSuccessfully creates an HTTP handler at `/OS-TRUST/trusts` on the // test handler mux that responds with a list of two trusts. func HandleListTrustsSuccessfully(t *testing.T) { testhelper.Mux.HandleFunc("/OS-TRUST/trusts", func(w http.ResponseWriter, r *http.Request) { testhelper.TestMethod(t, r, "GET") testhelper.TestHeader(t, r, "Accept", "application/json") testhelper.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ListResponse) }) } // HandleListTrustRolesSuccessfully creates an HTTP handler at `/OS-TRUST/trusts/987fe8/roles` on the // test handler mux that responds with a list trust roles. func HandleListTrustRolesSuccessfully(t *testing.T) { testhelper.Mux.HandleFunc("/OS-TRUST/trusts/987fe8/roles", func(w http.ResponseWriter, r *http.Request) { testhelper.TestMethod(t, r, "GET") testhelper.TestHeader(t, r, "Accept", "application/json") testhelper.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ListTrustRolesResponse) }) } // HandleGetTrustRoleSuccessfully creates an HTTP handler at `/OS-TRUST/trusts/987fe8/roles/c1648e` on the // test handler mux that responds with a trust role details. func HandleGetTrustRoleSuccessfully(t *testing.T) { testhelper.Mux.HandleFunc("/OS-TRUST/trusts/987fe8/roles/c1648e", func(w http.ResponseWriter, r *http.Request) { testhelper.TestMethod(t, r, "GET") testhelper.TestHeader(t, r, "Accept", "application/json") testhelper.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, GetTrustRoleResponse) }) } // HandleCheckTrustRoleSuccessfully creates an HTTP handler at `/OS-TRUST/trusts/987fe8/roles/c1648e` on the // test handler mux that responds with a list trust roles. func HandleCheckTrustRoleSuccessfully(t *testing.T) { testhelper.Mux.HandleFunc("/OS-TRUST/trusts/987fe8/roles/c1648e", func(w http.ResponseWriter, r *http.Request) { testhelper.TestMethod(t, r, "HEAD") testhelper.TestHeader(t, r, "Accept", "application/json") testhelper.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.WriteHeader(http.StatusOK) }) } requests_test.go000066400000000000000000000126321367513235700357350ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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" "github.com/gophercloud/gophercloud/pagination" 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, RedelegatedTrustID: "3ba234", RedelegationCount: 2, }, }, } th.AssertDeepEquals(t, expected, actual) } func TestCreateTrust(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleCreateTrust(t) expiresAt := time.Date(2019, 12, 1, 14, 0, 0, 0, time.UTC) result, err := trusts.Create(client.ServiceClient(), trusts.CreateOpts{ ExpiresAt: &expiresAt, AllowRedelegation: true, ProjectID: "9b71012f5a4a4aef9193f1995fe159b2", Roles: []trusts.Role{ { Name: "member", }, }, TrusteeUserID: "ecb37e88cc86431c99d0332208cb6fbf", TrustorUserID: "959ed913a32c4ec88c041c98e61cbbc3", }).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, CreatedTrust, *result) } func TestCreateTrustNoExpire(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleCreateTrustNoExpire(t) result, err := trusts.Create(client.ServiceClient(), trusts.CreateOpts{ AllowRedelegation: true, ProjectID: "9b71012f5a4a4aef9193f1995fe159b2", Roles: []trusts.Role{ { Name: "member", }, }, TrusteeUserID: "ecb37e88cc86431c99d0332208cb6fbf", TrustorUserID: "959ed913a32c4ec88c041c98e61cbbc3", }).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, CreatedTrustNoExpire, *result) } func TestDeleteTrust(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleDeleteTrust(t) res := trusts.Delete(client.ServiceClient(), "3422b7c113894f5d90665e1a79655e23") th.AssertNoErr(t, res.Err) } func TestGetTrust(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleGetTrustSuccessfully(t) res := trusts.Get(client.ServiceClient(), "987fe8") th.AssertNoErr(t, res.Err) } func TestListTrusts(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleListTrustsSuccessfully(t) count := 0 err := trusts.List(client.ServiceClient(), nil).EachPage(func(page pagination.Page) (bool, error) { count++ actual, err := trusts.ExtractTrusts(page) th.AssertNoErr(t, err) th.CheckDeepEquals(t, ExpectedTrustsSlice, actual) return true, nil }) th.AssertNoErr(t, err) th.CheckEquals(t, count, 1) } func TestListTrustsAllPages(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleListTrustsSuccessfully(t) allPages, err := trusts.List(client.ServiceClient(), nil).AllPages() th.AssertNoErr(t, err) actual, err := trusts.ExtractTrusts(allPages) th.AssertNoErr(t, err) th.CheckDeepEquals(t, ExpectedTrustsSlice, actual) } func TestListTrustsFiltered(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleListTrustsSuccessfully(t) trustsListOpts := trusts.ListOpts{ TrustorUserID: "86c0d5", } allPages, err := trusts.List(client.ServiceClient(), trustsListOpts).AllPages() th.AssertNoErr(t, err) actual, err := trusts.ExtractTrusts(allPages) th.AssertNoErr(t, err) th.CheckDeepEquals(t, ExpectedTrustsSlice, actual) } func TestListTrustRoles(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleListTrustRolesSuccessfully(t) count := 0 err := trusts.ListRoles(client.ServiceClient(), "987fe8").EachPage(func(page pagination.Page) (bool, error) { count++ actual, err := trusts.ExtractRoles(page) th.AssertNoErr(t, err) th.CheckDeepEquals(t, ExpectedTrustRolesSlice, actual) return true, nil }) th.AssertNoErr(t, err) th.CheckEquals(t, count, 1) } func TestListTrustRolesAllPages(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleListTrustRolesSuccessfully(t) allPages, err := trusts.ListRoles(client.ServiceClient(), "987fe8").AllPages() th.AssertNoErr(t, err) actual, err := trusts.ExtractRoles(allPages) th.AssertNoErr(t, err) th.CheckDeepEquals(t, ExpectedTrustRolesSlice, actual) } func TestGetTrustRole(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleGetTrustRoleSuccessfully(t) role, err := trusts.GetRole(client.ServiceClient(), "987fe8", "c1648e").Extract() th.AssertNoErr(t, err) th.AssertEquals(t, FirstRole, *role) } func TestCheckTrustRole(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleCheckTrustRoleSuccessfully(t) err := trusts.CheckRole(client.ServiceClient(), "987fe8", "c1648e").ExtractErr() th.AssertNoErr(t, err) } golang-github-gophercloud-gophercloud-0.12.0/openstack/identity/v3/extensions/trusts/urls.go000066400000000000000000000014541367513235700324120ustar00rootroot00000000000000package trusts import "github.com/gophercloud/gophercloud" const resourcePath = "OS-TRUST/trusts" func rootURL(c *gophercloud.ServiceClient) string { return c.ServiceURL(resourcePath) } func resourceURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL(resourcePath, id) } func createURL(c *gophercloud.ServiceClient) string { return rootURL(c) } func deleteURL(c *gophercloud.ServiceClient, id string) string { return resourceURL(c, id) } func listURL(c *gophercloud.ServiceClient) string { return c.ServiceURL(resourcePath) } func listRolesURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL(resourcePath, id, "roles") } func getRoleURL(c *gophercloud.ServiceClient, id, roleID string) string { return c.ServiceURL(resourcePath, id, "roles", roleID) } golang-github-gophercloud-gophercloud-0.12.0/openstack/identity/v3/groups/000077500000000000000000000000001367513235700266465ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/identity/v3/groups/doc.go000066400000000000000000000022311367513235700277400ustar00rootroot00000000000000/* 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 golang-github-gophercloud-gophercloud-0.12.0/openstack/identity/v3/groups/errors.go000066400000000000000000000005461367513235700305160ustar00rootroot00000000000000package 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 } golang-github-gophercloud-gophercloud-0.12.0/openstack/identity/v3/groups/requests.go000066400000000000000000000116201367513235700310500ustar00rootroot00000000000000package 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) { resp, err := client.Get(getURL(client, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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 } resp, err := client.Post(createURL(client), &b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{201}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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 } resp, err := client.Patch(updateURL(client, groupID), &b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete deletes a group. func Delete(client *gophercloud.ServiceClient, groupID string) (r DeleteResult) { resp, err := client.Delete(deleteURL(client, groupID), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } golang-github-gophercloud-gophercloud-0.12.0/openstack/identity/v3/groups/results.go000066400000000000000000000060611367513235700307010ustar00rootroot00000000000000package 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.12.0/openstack/identity/v3/groups/testing/000077500000000000000000000000001367513235700303235ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/identity/v3/groups/testing/fixtures.go000066400000000000000000000140021367513235700325200ustar00rootroot00000000000000package 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) }) } golang-github-gophercloud-gophercloud-0.12.0/openstack/identity/v3/groups/testing/requests_test.go000066400000000000000000000063551367513235700335750ustar00rootroot00000000000000package 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) var description = "L2 Support Team" updateOpts := groups.UpdateOpts{ Description: &description, 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.12.0/openstack/identity/v3/groups/urls.go000066400000000000000000000011541367513235700301630ustar00rootroot00000000000000package 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.12.0/openstack/identity/v3/policies/000077500000000000000000000000001367513235700271365ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/identity/v3/policies/doc.go000066400000000000000000000031471367513235700302370ustar00rootroot00000000000000/* 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 golang-github-gophercloud-gophercloud-0.12.0/openstack/identity/v3/policies/errors.go000066400000000000000000000013301367513235700307760ustar00rootroot00000000000000package 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, ) } golang-github-gophercloud-gophercloud-0.12.0/openstack/identity/v3/policies/requests.go000066400000000000000000000121571367513235700313460ustar00rootroot00000000000000package 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 } resp, err := client.Post(createURL(client), &b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{201}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get retrieves details on a single policy, by ID. func Get(client *gophercloud.ServiceClient, policyID string) (r GetResult) { resp, err := client.Get(getURL(client, policyID), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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 } resp, err := client.Patch(updateURL(client, policyID), &b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete deletes a policy. func Delete(client *gophercloud.ServiceClient, policyID string) (r DeleteResult) { resp, err := client.Delete(deleteURL(client, policyID), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } golang-github-gophercloud-gophercloud-0.12.0/openstack/identity/v3/policies/results.go000066400000000000000000000061401367513235700311670ustar00rootroot00000000000000package 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 } golang-github-gophercloud-gophercloud-0.12.0/openstack/identity/v3/policies/testing/000077500000000000000000000000001367513235700306135ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/identity/v3/policies/testing/doc.go000066400000000000000000000001001367513235700316760ustar00rootroot00000000000000// Package testing contains policies unit tests package testing golang-github-gophercloud-gophercloud-0.12.0/openstack/identity/v3/policies/testing/fixtures.go000066400000000000000000000154211367513235700330160ustar00rootroot00000000000000package 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) }) } golang-github-gophercloud-gophercloud-0.12.0/openstack/identity/v3/policies/testing/requests_test.go000066400000000000000000000124571367513235700340650ustar00rootroot00000000000000package 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) } golang-github-gophercloud-gophercloud-0.12.0/openstack/identity/v3/policies/urls.go000066400000000000000000000012351367513235700304530ustar00rootroot00000000000000package 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.12.0/openstack/identity/v3/projects/000077500000000000000000000000001367513235700271605ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/identity/v3/projects/doc.go000066400000000000000000000025551367513235700302630ustar00rootroot00000000000000/* 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", Tags: []string{"FirstTag", "SecondTag"}, } 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) } updateOpts = projects.UpdateOpts{ Tags: &[]string{"FirstTag"}, } 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 golang-github-gophercloud-gophercloud-0.12.0/openstack/identity/v3/projects/errors.go000066400000000000000000000005501367513235700310230ustar00rootroot00000000000000package 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 } golang-github-gophercloud-gophercloud-0.12.0/openstack/identity/v3/projects/requests.go000066400000000000000000000154461367513235700313740ustar00rootroot00000000000000package 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"` // Tags filters on specific project tags. All tags must be present for the project. Tags string `q:"tags"` // TagsAny filters on specific project tags. At least one of the tags must be present for the project. TagsAny string `q:"tags-any"` // NotTags filters on specific project tags. All tags must be absent for the project. NotTags string `q:"not-tags"` // NotTagsAny filters on specific project tags. At least one of the tags must be absent for the project. NotTagsAny string `q:"not-tags-any"` // 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) { resp, err := client.Get(getURL(client, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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"` // Tags is a list of tags to associate with the project. Tags []string `json:"tags,omitempty"` // Extra is free-form extra key/value pairs to describe the project. Extra map[string]interface{} `json:"-"` // Options are defined options in the API to enable certain features. Options map[Option]interface{} `json:"options,omitempty"` } // ToProjectCreateMap formats a CreateOpts into a create request. func (opts CreateOpts) ToProjectCreateMap() (map[string]interface{}, error) { b, err := gophercloud.BuildRequestBody(opts, "project") if err != nil { return nil, err } if opts.Extra != nil { if v, ok := b["project"].(map[string]interface{}); ok { for key, value := range opts.Extra { v[key] = value } } } return b, nil } // 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 } resp, err := client.Post(createURL(client), &b, &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete deletes a project. func Delete(client *gophercloud.ServiceClient, projectID string) (r DeleteResult) { resp, err := client.Delete(deleteURL(client, projectID), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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"` // Tags is a list of tags to associate with the project. Tags *[]string `json:"tags,omitempty"` // Extra is free-form extra key/value pairs to describe the project. Extra map[string]interface{} `json:"-"` // Options are defined options in the API to enable certain features. Options map[Option]interface{} `json:"options,omitempty"` } // ToUpdateCreateMap formats a UpdateOpts into an update request. func (opts UpdateOpts) ToProjectUpdateMap() (map[string]interface{}, error) { b, err := gophercloud.BuildRequestBody(opts, "project") if err != nil { return nil, err } if opts.Extra != nil { if v, ok := b["project"].(map[string]interface{}); ok { for key, value := range opts.Extra { v[key] = value } } } return b, nil } // 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 } resp, err := client.Patch(updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } golang-github-gophercloud-gophercloud-0.12.0/openstack/identity/v3/projects/results.go000066400000000000000000000072701367513235700312160ustar00rootroot00000000000000package projects import ( "encoding/json" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/internal" "github.com/gophercloud/gophercloud/pagination" ) // Option is a specific option defined at the API to enable features // on a project. type Option string const ( Immutable Option = "immutable" ) 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"` // Tags is the list of tags associated with the project. Tags []string `json:"tags,omitempty"` // Extra is free-form extra key/value pairs to describe the project. Extra map[string]interface{} `json:"-"` // Options are defined options in the API to enable certain features. Options map[Option]interface{} `json:"options,omitempty"` } func (r *Project) UnmarshalJSON(b []byte) error { type tmp Project var s struct { tmp Extra map[string]interface{} `json:"extra"` } err := json.Unmarshal(b, &s) if err != nil { return err } *r = Project(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(Project{}, resultMap) } } return err } // 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 } golang-github-gophercloud-gophercloud-0.12.0/openstack/identity/v3/projects/testing/000077500000000000000000000000001367513235700306355ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/identity/v3/projects/testing/fixtures.go000066400000000000000000000126301367513235700330370ustar00rootroot00000000000000package 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, "tags": ["Red", "Team"], "test": "old" }, { "is_domain": false, "description": "The team that is blue", "domain_id": "default", "enabled": true, "id": "9876", "name": "Blue Team", "parent_id": null, "options": { "immutable": true } } ], "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, "tags": ["Red", "Team"], "test": "old" } } ` // CreateRequest provides the input to a Create request. const CreateRequest = ` { "project": { "description": "The team that is red", "name": "Red Team", "tags": ["Red", "Team"], "test": "old" } } ` // UpdateRequest provides the input to an Update request. const UpdateRequest = ` { "project": { "description": "The team that is bright red", "name": "Bright Red Team", "tags": ["Red"], "test": "new" } } ` // 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, "tags": ["Red"], "test": "new" } } ` // 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: "", Tags: []string{"Red", "Team"}, Extra: map[string]interface{}{"test": "old"}, } // 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: "", Extra: make(map[string]interface{}), Options: map[projects.Option]interface{}{ projects.Immutable: true, }, } // 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: "", Tags: []string{"Red"}, Extra: map[string]interface{}{"test": "new"}, } // 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) }) } golang-github-gophercloud-gophercloud-0.12.0/openstack/identity/v3/projects/testing/requests_test.go000066400000000000000000000053771367513235700341120ustar00rootroot00000000000000package 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", Tags: []string{"Red", "Team"}, Extra: map[string]interface{}{"test": "old"}, } 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) var description = "The team that is bright red" updateOpts := projects.UpdateOpts{ Name: "Bright Red Team", Description: &description, Tags: &[]string{"Red"}, Extra: map[string]interface{}{"test": "new"}, } actual, err := projects.Update(client.ServiceClient(), "1234", updateOpts).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, UpdatedRedTeam, *actual) } golang-github-gophercloud-gophercloud-0.12.0/openstack/identity/v3/projects/urls.go000066400000000000000000000012041367513235700304710ustar00rootroot00000000000000package 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.12.0/openstack/identity/v3/regions/000077500000000000000000000000001367513235700267755ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/identity/v3/regions/doc.go000066400000000000000000000025311367513235700300720ustar00rootroot00000000000000/* 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 golang-github-gophercloud-gophercloud-0.12.0/openstack/identity/v3/regions/requests.go000066400000000000000000000115671367513235700312110ustar00rootroot00000000000000package 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) { resp, err := client.Get(getURL(client, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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 } resp, err := client.Post(createURL(client), &b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{201}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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 } resp, err := client.Patch(updateURL(client, regionID), &b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete deletes a region. func Delete(client *gophercloud.ServiceClient, regionID string) (r DeleteResult) { resp, err := client.Delete(deleteURL(client, regionID), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } golang-github-gophercloud-gophercloud-0.12.0/openstack/identity/v3/regions/results.go000066400000000000000000000060511367513235700310270ustar00rootroot00000000000000package 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 } golang-github-gophercloud-gophercloud-0.12.0/openstack/identity/v3/regions/testing/000077500000000000000000000000001367513235700304525ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/identity/v3/regions/testing/fixtures.go000066400000000000000000000153641367513235700326630ustar00rootroot00000000000000package 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) }) } golang-github-gophercloud-gophercloud-0.12.0/openstack/identity/v3/regions/testing/requests_test.go000066400000000000000000000054221367513235700337160ustar00rootroot00000000000000package 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) var description = "First West sub-region of RegionOne" updateOpts := regions.UpdateOpts{ Description: &description, /* // 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.12.0/openstack/identity/v3/regions/urls.go000066400000000000000000000011701367513235700303100ustar00rootroot00000000000000package 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.12.0/openstack/identity/v3/roles/000077500000000000000000000000001367513235700264535ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/identity/v3/roles/doc.go000066400000000000000000000055641367513235700275610ustar00rootroot00000000000000/* 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.12.0/openstack/identity/v3/roles/errors.go000066400000000000000000000005451367513235700303220ustar00rootroot00000000000000package 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 } golang-github-gophercloud-gophercloud-0.12.0/openstack/identity/v3/roles/requests.go000066400000000000000000000264211367513235700306620ustar00rootroot00000000000000package 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) { resp, err := client.Get(getURL(client, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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 } resp, err := client.Post(createURL(client), &b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{201}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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 } resp, err := client.Patch(updateURL(client, roleID), &b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete deletes a role. func Delete(client *gophercloud.ServiceClient, roleID string) (r DeleteResult) { resp, err := client.Delete(deleteURL(client, roleID), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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" } resp, err := client.Put(assignURL(client, targetType, targetID, actorType, actorID, roleID), nil, nil, &gophercloud.RequestOpts{ OkCodes: []int{204}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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" } resp, err := client.Delete(assignURL(client, targetType, targetID, actorType, actorID, roleID), &gophercloud.RequestOpts{ OkCodes: []int{204}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } golang-github-gophercloud-gophercloud-0.12.0/openstack/identity/v3/roles/results.go000066400000000000000000000124051367513235700305050ustar00rootroot00000000000000package 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.12.0/openstack/identity/v3/roles/testing/000077500000000000000000000000001367513235700301305ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/identity/v3/roles/testing/doc.go000066400000000000000000000000441367513235700312220ustar00rootroot00000000000000// roles unit tests package testing golang-github-gophercloud-gophercloud-0.12.0/openstack/identity/v3/roles/testing/fixtures.go000066400000000000000000000315611367513235700323360ustar00rootroot00000000000000package 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) } golang-github-gophercloud-gophercloud-0.12.0/openstack/identity/v3/roles/testing/requests_test.go000066400000000000000000000166561367513235700334070ustar00rootroot00000000000000package 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.12.0/openstack/identity/v3/roles/urls.go000066400000000000000000000022261367513235700277710ustar00rootroot00000000000000package 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.12.0/openstack/identity/v3/services/000077500000000000000000000000001367513235700271525ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/identity/v3/services/doc.go000066400000000000000000000024721367513235700302530ustar00rootroot00000000000000/* 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 golang-github-gophercloud-gophercloud-0.12.0/openstack/identity/v3/services/requests.go000066400000000000000000000107361367513235700313630ustar00rootroot00000000000000package 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 } resp, err := client.Post(createURL(client), &b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{201}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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) { resp, err := client.Get(serviceURL(client, serviceID), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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 } resp, err := client.Patch(updateURL(client, serviceID), &b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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) { resp, err := client.Delete(serviceURL(client, serviceID), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } golang-github-gophercloud-gophercloud-0.12.0/openstack/identity/v3/services/results.go000066400000000000000000000061711367513235700312070ustar00rootroot00000000000000package 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 } golang-github-gophercloud-gophercloud-0.12.0/openstack/identity/v3/services/testing/000077500000000000000000000000001367513235700306275ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/identity/v3/services/testing/doc.go000066400000000000000000000000471367513235700317240ustar00rootroot00000000000000// services unit tests package testing golang-github-gophercloud-gophercloud-0.12.0/openstack/identity/v3/services/testing/fixtures.go000066400000000000000000000130721367513235700330320ustar00rootroot00000000000000package 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) }) } golang-github-gophercloud-gophercloud-0.12.0/openstack/identity/v3/services/testing/requests_test.go000066400000000000000000000056201367513235700340730ustar00rootroot00000000000000package 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) } golang-github-gophercloud-gophercloud-0.12.0/openstack/identity/v3/services/urls.go000066400000000000000000000010071367513235700304640ustar00rootroot00000000000000package 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.12.0/openstack/identity/v3/tokens/000077500000000000000000000000001367513235700266325ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/identity/v3/tokens/doc.go000066400000000000000000000043601367513235700277310ustar00rootroot00000000000000/* 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 golang-github-gophercloud-gophercloud-0.12.0/openstack/identity/v3/tokens/requests.go000066400000000000000000000135741367513235700310460ustar00rootroot00000000000000package 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 System bool } // 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) ToTokenV3HeadersMap(map[string]interface{}) (map[string]string, 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"` // Passcode is used in TOTP authentication method Passcode string `json:"passcode,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, Passcode: opts.Passcode, 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) } // ToTokenV3ScopeMap 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 { if opts.Passcode != "" { // cannot reauth using TOTP passcode return false } return opts.AllowReauth } // ToTokenV3HeadersMap allows AuthOptions to satisfy the AuthOptionsBuilder // interface in the v3 tokens package. func (opts *AuthOptions) ToTokenV3HeadersMap(map[string]interface{}) (map[string]string, error) { return nil, nil } func subjectTokenHeaders(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.Header, r.Err = gophercloud.ParseResponse(resp, err) 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(token), OkCodes: []int{200, 203}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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(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) { resp, err := c.Delete(tokenURL(c), &gophercloud.RequestOpts{ MoreHeaders: subjectTokenHeaders(token), }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } golang-github-gophercloud-gophercloud-0.12.0/openstack/identity/v3/tokens/results.go000066400000000000000000000131171367513235700306650ustar00rootroot00000000000000package 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 } // ExtractTokenID implements the gophercloud.AuthResult interface. The returned // string is the same as the ID field of the Token struct returned from // ExtractToken(). func (r CreateResult) ExtractTokenID() (string, error) { return r.Header.Get("X-Subject-Token"), r.Err } // ExtractTokenID implements the gophercloud.AuthResult interface. The returned // string is the same as the ID field of the Token struct returned from // ExtractToken(). func (r GetResult) ExtractTokenID() (string, error) { return r.Header.Get("X-Subject-Token"), r.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 } // ExtractDomain returns Domain to which User is authorized. func (r commonResult) ExtractDomain() (*Domain, error) { var s struct { Domain *Domain `json:"domain"` } err := r.ExtractInto(&s) return s.Domain, 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.12.0/openstack/identity/v3/tokens/testing/000077500000000000000000000000001367513235700303075ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/identity/v3/tokens/testing/doc.go000066400000000000000000000000451367513235700314020ustar00rootroot00000000000000// tokens unit tests package testing golang-github-gophercloud-gophercloud-0.12.0/openstack/identity/v3/tokens/testing/fixtures.go000066400000000000000000000217241367513235700325150ustar00rootroot00000000000000package 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" } }` const DomainToken = ` { "token": { "domain": { "id": "default", "name": "Default" }, "methods": [ "password" ], "roles":[ { "id":"434426788d5a451faf763b0e6db5aefb", "name":"admin" } ], "expires_at": "2019-09-18T23:12:32.000000Z", "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": [ "P4QTZuYXS1u8SC6b3BSK1g" ], "issued_at": "2019-09-18T15:12:32.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", } // ExpectedDomain contains expected domain extracted from token response. var ExpectedDomain = tokens.Domain{ ID: "default", Name: "Default", } 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 } func getGetDomainResult(t *testing.T) tokens.GetResult { result := tokens.GetResult{} result.Header = http.Header{ "X-Subject-Token": []string{testTokenID}, } err := json.Unmarshal([]byte(DomainToken), &result.Body) testhelper.AssertNoErr(t, err) return result } golang-github-gophercloud-gophercloud-0.12.0/openstack/identity/v3/tokens/testing/requests_test.go000066400000000000000000000430451367513235700335560ustar00rootroot00000000000000package 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: "someuser", Password: "somepassword"} scope := &tokens.Scope{ProjectID: "123456"} authTokenPost(t, options, scope, ` { "auth": { "identity": { "methods": ["password"], "password": { "user": { "id": "someuser", "password": "somepassword" } } }, "scope": { "project": { "id": "123456" } } } } `) } func TestCreateDomainIDScope(t *testing.T) { options := tokens.AuthOptions{UserID: "someuser", Password: "somepassword"} scope := &tokens.Scope{DomainID: "1000"} authTokenPost(t, options, scope, ` { "auth": { "identity": { "methods": ["password"], "password": { "user": { "id": "someuser", "password": "somepassword" } } }, "scope": { "domain": { "id": "1000" } } } } `) } func TestCreateDomainNameScope(t *testing.T) { options := tokens.AuthOptions{UserID: "someuser", Password: "somepassword"} scope := &tokens.Scope{DomainName: "evil-plans"} authTokenPost(t, options, scope, ` { "auth": { "identity": { "methods": ["password"], "password": { "user": { "id": "someuser", "password": "somepassword" } } }, "scope": { "domain": { "name": "evil-plans" } } } } `) } func TestCreateProjectNameAndDomainIDScope(t *testing.T) { options := tokens.AuthOptions{UserID: "someuser", Password: "somepassword"} scope := &tokens.Scope{ProjectName: "world-domination", DomainID: "1000"} authTokenPost(t, options, scope, ` { "auth": { "identity": { "methods": ["password"], "password": { "user": { "id": "someuser", "password": "somepassword" } } }, "scope": { "project": { "domain": { "id": "1000" }, "name": "world-domination" } } } } `) } func TestCreateProjectNameAndDomainNameScope(t *testing.T) { options := tokens.AuthOptions{UserID: "someuser", Password: "somepassword"} scope := &tokens.Scope{ProjectName: "world-domination", DomainName: "evil-plans"} authTokenPost(t, options, scope, ` { "auth": { "identity": { "methods": ["password"], "password": { "user": { "id": "someuser", "password": "somepassword" } } }, "scope": { "project": { "domain": { "name": "evil-plans" }, "name": "world-domination" } } } } `) } func TestCreateSystemScope(t *testing.T) { options := tokens.AuthOptions{UserID: "someuser", Password: "somepassword"} scope := &tokens.Scope{System: true} authTokenPost(t, options, scope, ` { "auth": { "identity": { "methods": ["password"], "password": { "user": { "id": "someuser", "password": "somepassword" } } }, "scope": { "system": { "all": true } } } } `) } 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: "someuser", DomainName: "evil-plans"}, nil, ` { "auth": { "identity": { "application_credential": { "name": "myappcred", "secret": "mysecret", "user": { "name": "someuser", "domain": { "name": "evil-plans" } } }, "methods": [ "application_credential" ] } } } `) } func TestCreateTOTPProjectNameAndDomainNameScope(t *testing.T) { options := tokens.AuthOptions{UserID: "someuser", Passcode: "12345678"} scope := &tokens.Scope{ProjectName: "world-domination", DomainName: "evil-plans"} authTokenPost(t, options, scope, ` { "auth": { "identity": { "methods": ["totp"], "totp": { "user": { "id": "someuser", "passcode": "12345678" } } }, "scope": { "project": { "domain": { "name": "evil-plans" }, "name": "world-domination" } } } } `) } func TestCreatePasswordTOTPProjectNameAndDomainNameScope(t *testing.T) { options := tokens.AuthOptions{UserID: "someuser", Password: "somepassword", Passcode: "12345678"} scope := &tokens.Scope{ProjectName: "world-domination", DomainName: "evil-plans"} authTokenPost(t, options, scope, ` { "auth": { "identity": { "methods": ["password","totp"], "password": { "user": { "id": "someuser", "password": "somepassword" } }, "totp": { "user": { "id": "someuser", "passcode": "12345678" } } }, "scope": { "project": { "domain": { "name": "evil-plans" }, "name": "world-domination" } } } } `) } 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) } golang-github-gophercloud-gophercloud-0.12.0/openstack/identity/v3/tokens/testing/results_test.go000066400000000000000000000024061367513235700334000ustar00rootroot00000000000000package 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) } func TestExtractDomain(t *testing.T) { result := getGetDomainResult(t) domain, err := result.ExtractDomain() testhelper.AssertNoErr(t, err) testhelper.CheckDeepEquals(t, &ExpectedDomain, domain) } golang-github-gophercloud-gophercloud-0.12.0/openstack/identity/v3/tokens/urls.go000066400000000000000000000002331367513235700301440ustar00rootroot00000000000000package tokens import "github.com/gophercloud/gophercloud" func tokenURL(c *gophercloud.ServiceClient) string { return c.ServiceURL("auth", "tokens") } golang-github-gophercloud-gophercloud-0.12.0/openstack/identity/v3/users/000077500000000000000000000000001367513235700264705ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/identity/v3/users/doc.go000066400000000000000000000070231367513235700275660ustar00rootroot00000000000000/* 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.12.0/openstack/identity/v3/users/errors.go000066400000000000000000000005451367513235700303370ustar00rootroot00000000000000package 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 } golang-github-gophercloud-gophercloud-0.12.0/openstack/identity/v3/users/requests.go000066400000000000000000000245651367513235700307060ustar00rootroot00000000000000package users import ( "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) { resp, err := client.Get(getURL(client, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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 } resp, err := client.Post(createURL(client), &b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{201}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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 } resp, err := client.Patch(updateURL(client, userID), &b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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 } resp, err := client.Post(changePasswordURL(client, userID), &b, nil, &gophercloud.RequestOpts{ OkCodes: []int{204}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete deletes a user. func Delete(client *gophercloud.ServiceClient, userID string) (r DeleteResult) { resp, err := client.Delete(deleteURL(client, userID), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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) resp, err := client.Put(url, nil, nil, &gophercloud.RequestOpts{ OkCodes: []int{204}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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) resp, err := client.Head(url, &gophercloud.RequestOpts{ OkCodes: []int{204, 404}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) if r.Err == nil { if resp.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) resp, err := client.Delete(url, &gophercloud.RequestOpts{ OkCodes: []int{204}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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}} }) } golang-github-gophercloud-gophercloud-0.12.0/openstack/identity/v3/users/results.go000066400000000000000000000112031367513235700305150ustar00rootroot00000000000000package 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.12.0/openstack/identity/v3/users/testing/000077500000000000000000000000001367513235700301455ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/identity/v3/users/testing/fixtures.go000066400000000000000000000401011367513235700323410ustar00rootroot00000000000000package 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", Extra: map[string]interface{}{ "links": map[string]interface{}{"self": "http://localhost:5000/identity/v3/projects/abcde"}, }, } var SecondProject = projects.Project{ Description: "my second project", DomainID: "22222", Enabled: true, ID: "bcdef", Name: "project 2", ParentID: "22222", Extra: map[string]interface{}{ "links": map[string]interface{}{"self": "http://localhost:5000/identity/v3/projects/bcdef"}, }, } 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) }) } golang-github-gophercloud-gophercloud-0.12.0/openstack/identity/v3/users/testing/requests_test.go000066400000000000000000000145421367513235700334140ustar00rootroot00000000000000package 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.12.0/openstack/identity/v3/users/urls.go000066400000000000000000000031271367513235700300070ustar00rootroot00000000000000package 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.12.0/openstack/imageservice/000077500000000000000000000000001367513235700256115ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/imageservice/v2/000077500000000000000000000000001367513235700261405ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/imageservice/v2/README.md000066400000000000000000000002401367513235700274130ustar00rootroot00000000000000This 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.12.0/openstack/imageservice/v2/imagedata/000077500000000000000000000000001367513235700300545ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/imageservice/v2/imagedata/doc.go000066400000000000000000000017551367513235700311600ustar00rootroot00000000000000/* 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) } // close the reader, when reading has finished defer image.Close() imageData, err := ioutil.ReadAll(image) if err != nil { panic(err) } */ package imagedata golang-github-gophercloud-gophercloud-0.12.0/openstack/imageservice/v2/imagedata/requests.go000066400000000000000000000023761367513235700322660ustar00rootroot00000000000000package imagedata import ( "io" "github.com/gophercloud/gophercloud" ) // Upload uploads an image file. func Upload(client *gophercloud.ServiceClient, id string, data io.Reader) (r UploadResult) { resp, err := client.Put(uploadURL(client, id), data, nil, &gophercloud.RequestOpts{ MoreHeaders: map[string]string{"Content-Type": "application/octet-stream"}, OkCodes: []int{204}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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) { resp, err := client.Put(stageURL(client, id), data, nil, &gophercloud.RequestOpts{ MoreHeaders: map[string]string{"Content-Type": "application/octet-stream"}, OkCodes: []int{204}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Download retrieves an image. func Download(client *gophercloud.ServiceClient, id string) (r DownloadResult) { resp, err := client.Get(downloadURL(client, id), nil, &gophercloud.RequestOpts{ KeepResponseBody: true, }) r.Body, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } golang-github-gophercloud-gophercloud-0.12.0/openstack/imageservice/v2/imagedata/results.go000066400000000000000000000014651367513235700321120ustar00rootroot00000000000000package imagedata import ( "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 Body io.ReadCloser } // Extract builds images model from io.Reader func (r DownloadResult) Extract() (io.ReadCloser, error) { if r.Err != nil { return nil, r.Err } return r.Body, nil } golang-github-gophercloud-gophercloud-0.12.0/openstack/imageservice/v2/imagedata/testing/000077500000000000000000000000001367513235700315315ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/imageservice/v2/imagedata/testing/doc.go000066400000000000000000000000501367513235700326200ustar00rootroot00000000000000// imagedata unit tests package testing golang-github-gophercloud-gophercloud-0.12.0/openstack/imageservice/v2/imagedata/testing/fixtures.go000066400000000000000000000031461367513235700337350ustar00rootroot00000000000000package 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.go000066400000000000000000000041421367513235700347140ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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) defer rdr.Close() bs, err := ioutil.ReadAll(rdr) th.AssertNoErr(t, err) th.AssertByteArrayEquals(t, []byte{34, 87, 0, 23, 23, 23, 56, 255, 254, 0}, bs) } golang-github-gophercloud-gophercloud-0.12.0/openstack/imageservice/v2/imagedata/urls.go000066400000000000000000000011431367513235700313670ustar00rootroot00000000000000package 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) } golang-github-gophercloud-gophercloud-0.12.0/openstack/imageservice/v2/imageimport/000077500000000000000000000000001367513235700304555ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/imageservice/v2/imageimport/doc.go000066400000000000000000000012711367513235700315520ustar00rootroot00000000000000/* 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) Example to Create a new image import createOpts := imageimport.CreateOpts{ Name: imageimport.WebDownloadMethod, URI: "http://download.cirros-cloud.net/0.4.0/cirros-0.4.0-x86_64-disk.img", } imageID := "da3b75d9-3f4a-40e7-8a2c-bfab23927dea" err := imageimport.Create(imagesClient, imageID, createOpts).ExtractErr() if err != nil { panic(err) } */ package imageimport golang-github-gophercloud-gophercloud-0.12.0/openstack/imageservice/v2/imageimport/requests.go000066400000000000000000000031561367513235700326640ustar00rootroot00000000000000package 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) { resp, err := c.Get(infoURL(c), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // CreateOptsBuilder allows to add additional parameters to the Create request. type CreateOptsBuilder interface { ToImportCreateMap() (map[string]interface{}, error) } // CreateOpts specifies parameters of a new image import. type CreateOpts struct { Name ImportMethod `json:"name"` URI string `json:"uri"` } // ToImportCreateMap constructs a request body from CreateOpts. func (opts CreateOpts) ToImportCreateMap() (map[string]interface{}, error) { b, err := gophercloud.BuildRequestBody(opts, "") if err != nil { return nil, err } return map[string]interface{}{"method": b}, nil } // Create requests the creation of a new image import on the server. func Create(client *gophercloud.ServiceClient, imageID string, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToImportCreateMap() if err != nil { r.Err = err return } resp, err := client.Post(importURL(client, imageID), b, nil, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } golang-github-gophercloud-gophercloud-0.12.0/openstack/imageservice/v2/imageimport/results.go000066400000000000000000000017651367513235700325160ustar00rootroot00000000000000package 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 } // CreateResult is the result of import Create operation. Call its ExtractErr // method to determine if the request succeeded or failed. type CreateResult struct { gophercloud.ErrResult } // 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 } golang-github-gophercloud-gophercloud-0.12.0/openstack/imageservice/v2/imageimport/testing/000077500000000000000000000000001367513235700321325ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/imageservice/v2/imageimport/testing/doc.go000066400000000000000000000000201367513235700332160ustar00rootroot00000000000000package testing fixtures.go000066400000000000000000000010511367513235700342500ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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" ] } } ` // ImportCreateRequest represents a request to create image import. const ImportCreateRequest = ` { "method": { "name": "web-download", "uri": "http://download.cirros-cloud.net/0.4.0/cirros-0.4.0-x86_64-disk.img" } } ` requests_test.go000066400000000000000000000033551367513235700353220ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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) } func TestCreate(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/images/da3b75d9-3f4a-40e7-8a2c-bfab23927dea/import", 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, ImportCreateRequest) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusAccepted) fmt.Fprintf(w, `{}`) }) opts := imageimport.CreateOpts{ Name: imageimport.WebDownloadMethod, URI: "http://download.cirros-cloud.net/0.4.0/cirros-0.4.0-x86_64-disk.img", } err := imageimport.Create(fakeclient.ServiceClient(), "da3b75d9-3f4a-40e7-8a2c-bfab23927dea", opts).ExtractErr() th.AssertNoErr(t, err) } golang-github-gophercloud-gophercloud-0.12.0/openstack/imageservice/v2/imageimport/urls.go000066400000000000000000000005701367513235700317730ustar00rootroot00000000000000package imageimport import "github.com/gophercloud/gophercloud" const ( rootPath = "images" infoPath = "info" resourcePath = "import" ) func infoURL(c *gophercloud.ServiceClient) string { return c.ServiceURL(infoPath, resourcePath) } func importURL(c *gophercloud.ServiceClient, imageID string) string { return c.ServiceURL(rootPath, imageID, resourcePath) } golang-github-gophercloud-gophercloud-0.12.0/openstack/imageservice/v2/images/000077500000000000000000000000001367513235700274055ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/imageservice/v2/images/doc.go000066400000000000000000000021551367513235700305040ustar00rootroot00000000000000/* 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 golang-github-gophercloud-gophercloud-0.12.0/openstack/imageservice/v2/images/requests.go000066400000000000000000000255141367513235700316160ustar00rootroot00000000000000package 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 } resp, err := client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{OkCodes: []int{201}}) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete implements image delete request. func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { resp, err := client.Delete(deleteURL(client, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get implements image get request. func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { resp, err := client.Get(getURL(client, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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 } resp, 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"}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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, } } // ReplaceImageMinDisk represents an updated min_disk property request. type ReplaceImageMinDisk struct { NewMinDisk int } // ToImagePatchMap assembles a request body based on ReplaceImageTags. func (r ReplaceImageMinDisk) ToImagePatchMap() map[string]interface{} { return map[string]interface{}{ "op": "replace", "path": "/min_disk", "value": r.NewMinDisk, } } // ReplaceImageMinRam represents an updated min_ram property request. type ReplaceImageMinRam struct { NewMinRam int } // ToImagePatchMap assembles a request body based on ReplaceImageTags. func (r ReplaceImageMinRam) ToImagePatchMap() map[string]interface{} { return map[string]interface{}{ "op": "replace", "path": "/min_ram", "value": r.NewMinRam, } } // 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.Op != RemoveOp { updateMap["value"] = r.Value } return updateMap } golang-github-gophercloud-gophercloud-0.12.0/openstack/imageservice/v2/images/results.go000066400000000000000000000150741367513235700314440ustar00rootroot00000000000000package images import ( "encoding/json" "fmt" "reflect" "strings" "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"` // OpenStackImageImportMethods is a slice listing the types of import // methods available in the cloud. OpenStackImageImportMethods []string `json:"-"` // OpenStackImageStoreIDs is a slice listing the store IDs available in // the cloud. OpenStackImageStoreIDs []string `json:"-"` } func (r *Image) UnmarshalJSON(b []byte) error { type tmp Image var s struct { tmp SizeBytes interface{} `json:"size"` OpenStackImageImportMethods string `json:"openstack-image-import-methods"` OpenStackImageStoreIDs string `json:"openstack-image-store-ids"` } 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") delete(resultMap, "openstack-image-import-methods") delete(resultMap, "openstack-image-store-ids") r.Properties = internal.RemainingKeys(Image{}, resultMap) } if v := strings.FieldsFunc(strings.TrimSpace(s.OpenStackImageImportMethods), splitFunc); len(v) > 0 { r.OpenStackImageImportMethods = v } if v := strings.FieldsFunc(strings.TrimSpace(s.OpenStackImageStoreIDs), splitFunc); len(v) > 0 { r.OpenStackImageStoreIDs = v } return err } type commonResult struct { gophercloud.Result } // Extract interprets any commonResult as an Image. func (r commonResult) Extract() (*Image, error) { var s *Image if v, ok := r.Body.(map[string]interface{}); ok { for k, h := range r.Header { if strings.ToLower(k) == "openstack-image-import-methods" { for _, s := range h { v["openstack-image-import-methods"] = s } } if strings.ToLower(k) == "openstack-image-store-ids" { for _, s := range h { v["openstack-image-store-ids"] = s } } } } 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 } // splitFunc is a helper function used to avoid a slice of empty strings. func splitFunc(c rune) bool { return c == ',' } golang-github-gophercloud-gophercloud-0.12.0/openstack/imageservice/v2/images/testing/000077500000000000000000000000001367513235700310625ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/imageservice/v2/images/testing/doc.go000066400000000000000000000000451367513235700321550ustar00rootroot00000000000000// images unit tests package testing golang-github-gophercloud-gophercloud-0.12.0/openstack/imageservice/v2/images/testing/fixtures.go000066400000000000000000000330301367513235700332610ustar00rootroot00000000000000package 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.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) 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.Header().Set("Content-Type", "application/json") w.Header().Set("OpenStack-image-import-methods", "glance-direct,web-download") w.Header().Set("OpenStack-image-store-ids", "123,456") w.WriteHeader(http.StatusCreated) 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.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) 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" ] }, { "op": "replace", "path": "/min_disk", "value": 21 }, { "op": "replace", "path": "/min_ram", "value": 1024 }, { "op": "add", "path": "/empty_value", "value": "" } ]`) th.AssertEquals(t, "application/openstack-images-v2.1-json-patch", r.Header.Get("Content-Type")) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) 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": 1024, "min_disk": 21, "disk_format": "", "virtual_size": 0, "container_format": "", "empty_value": "", "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.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) 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.go000066400000000000000000000262241367513235700342520ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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("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, OpenStackImageImportMethods: []string{ "glance-direct", "web-download", }, OpenStackImageStoreIDs: []string{ "123", "456", }, } 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"}}, images.ReplaceImageMinDisk{NewMinDisk: 21}, images.ReplaceImageMinRam{NewMinRam: 1024}, images.UpdateImageProperty{ Op: images.AddOp, Name: "empty_value", Value: "", }, }).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: 1024, MinDiskGigabytes: 21, 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", "empty_value": "", }, } 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) } golang-github-gophercloud-gophercloud-0.12.0/openstack/imageservice/v2/images/types.go000066400000000000000000000071261367513235700311060ustar00rootroot00000000000000package 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" // ImageStatusImporting denotes that an import call has been made but that // the image is not yet ready for use. ImageStatusImporting ImageStatus = "importing" ) // 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 } golang-github-gophercloud-gophercloud-0.12.0/openstack/imageservice/v2/images/urls.go000066400000000000000000000031641367513235700307250ustar00rootroot00000000000000package 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.12.0/openstack/imageservice/v2/members/000077500000000000000000000000001367513235700275725ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/imageservice/v2/members/doc.go000066400000000000000000000024441367513235700306720ustar00rootroot00000000000000/* 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 golang-github-gophercloud-gophercloud-0.12.0/openstack/imageservice/v2/members/requests.go000066400000000000000000000054241367513235700320010ustar00rootroot00000000000000package 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} resp, err := client.Post(createMemberURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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) { resp, err := client.Get(getMemberURL(client, imageID, memberID), &r.Body, &gophercloud.RequestOpts{OkCodes: []int{200}}) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete membership for given image. Callee should be image owner. func Delete(client *gophercloud.ServiceClient, imageID string, memberID string) (r DeleteResult) { resp, err := client.Delete(deleteMemberURL(client, imageID, memberID), &gophercloud.RequestOpts{OkCodes: []int{204}}) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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 } resp, err := client.Put(updateMemberURL(client, imageID, memberID), b, &r.Body, &gophercloud.RequestOpts{OkCodes: []int{200}}) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } golang-github-gophercloud-gophercloud-0.12.0/openstack/imageservice/v2/members/results.go000066400000000000000000000035051367513235700316250ustar00rootroot00000000000000package 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 } golang-github-gophercloud-gophercloud-0.12.0/openstack/imageservice/v2/members/testing/000077500000000000000000000000001367513235700312475ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/imageservice/v2/members/testing/doc.go000066400000000000000000000000461367513235700323430ustar00rootroot00000000000000// members unit tests package testing golang-github-gophercloud-gophercloud-0.12.0/openstack/imageservice/v2/members/testing/fixtures.go000066400000000000000000000106031367513235700334470ustar00rootroot00000000000000package 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.go000066400000000000000000000105531367513235700344350ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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) } golang-github-gophercloud-gophercloud-0.12.0/openstack/imageservice/v2/members/urls.go000066400000000000000000000017441367513235700311140ustar00rootroot00000000000000package 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.12.0/openstack/imageservice/v2/tasks/000077500000000000000000000000001367513235700272655ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/imageservice/v2/tasks/doc.go000066400000000000000000000022411367513235700303600ustar00rootroot00000000000000/* 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 golang-github-gophercloud-gophercloud-0.12.0/openstack/imageservice/v2/tasks/requests.go000066400000000000000000000072601367513235700314740ustar00rootroot00000000000000package 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) { resp, err := c.Get(getURL(c, taskID), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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 } resp, err := client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{201}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } golang-github-gophercloud-gophercloud-0.12.0/openstack/imageservice/v2/tasks/results.go000066400000000000000000000055171367513235700313250ustar00rootroot00000000000000package 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 } golang-github-gophercloud-gophercloud-0.12.0/openstack/imageservice/v2/tasks/testing/000077500000000000000000000000001367513235700307425ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/imageservice/v2/tasks/testing/doc.go000066400000000000000000000000441367513235700320340ustar00rootroot00000000000000// tasks unit tests package testing golang-github-gophercloud-gophercloud-0.12.0/openstack/imageservice/v2/tasks/testing/fixtures.go000066400000000000000000000077011367513235700331470ustar00rootroot00000000000000package 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.go000066400000000000000000000103711367513235700341260ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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", }, }) } golang-github-gophercloud-gophercloud-0.12.0/openstack/imageservice/v2/tasks/urls.go000066400000000000000000000021531367513235700306020ustar00rootroot00000000000000package 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.12.0/openstack/keymanager/000077500000000000000000000000001367513235700252715ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/keymanager/v1/000077500000000000000000000000001367513235700256175ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/keymanager/v1/acls/000077500000000000000000000000001367513235700265415ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/keymanager/v1/acls/doc.go000066400000000000000000000017431367513235700276420ustar00rootroot00000000000000/* 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 golang-github-gophercloud-gophercloud-0.12.0/openstack/keymanager/v1/acls/requests.go000066400000000000000000000076431367513235700307550ustar00rootroot00000000000000package acls import ( "github.com/gophercloud/gophercloud" ) // GetContainerACL retrieves the ACL of a container. func GetContainerACL(client *gophercloud.ServiceClient, containerID string) (r ACLResult) { resp, err := client.Get(containerURL(client, containerID), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // GetSecretACL retrieves the ACL of a secret. func GetSecretACL(client *gophercloud.ServiceClient, secretID string) (r ACLResult) { resp, err := client.Get(secretURL(client, secretID), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // SetOptsBuilder allows extensions to add additional parameters to the // Set request. type SetOptsBuilder interface { ToACLSetMap() (map[string]interface{}, error) } // SetOpt represents options to set a particular ACL type on a resource. type SetOpt 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"` } // SetOpts represents options to set an ACL on a resource. type SetOpts []SetOpt // ToACLSetMap formats a SetOpts into a set request. func (opts SetOpts) ToACLSetMap() (map[string]interface{}, error) { b := make(map[string]interface{}) for _, v := range opts { m, err := gophercloud.BuildRequestBody(v, v.Type) if err != nil { return nil, err } b[v.Type] = m[v.Type] } return b, nil } // 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 } resp, err := client.Put(containerURL(client, containerID), &b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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 } resp, err := client.Put(secretURL(client, secretID), &b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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 } resp, err := client.Patch(containerURL(client, containerID), &b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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 } resp, err := client.Patch(secretURL(client, secretID), &b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // DeleteContainerACL will delete an ACL from a conatiner. func DeleteContainerACL(client *gophercloud.ServiceClient, containerID string) (r DeleteResult) { resp, err := client.Delete(containerURL(client, containerID), &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // DeleteSecretACL will delete an ACL from a secret. func DeleteSecretACL(client *gophercloud.ServiceClient, secretID string) (r DeleteResult) { resp, err := client.Delete(secretURL(client, secretID), &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } golang-github-gophercloud-gophercloud-0.12.0/openstack/keymanager/v1/acls/results.go000066400000000000000000000035231367513235700305740ustar00rootroot00000000000000package 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.12.0/openstack/keymanager/v1/acls/testing/000077500000000000000000000000001367513235700302165ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/keymanager/v1/acls/testing/fixtures.go000066400000000000000000000125051367513235700324210ustar00rootroot00000000000000package 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) }) } golang-github-gophercloud-gophercloud-0.12.0/openstack/keymanager/v1/acls/testing/requests_test.go000066400000000000000000000062651367513235700334700ustar00rootroot00000000000000package 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{ acls.SetOpt{ 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{ acls.SetOpt{ 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{ acls.SetOpt{ 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{ acls.SetOpt{ 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.12.0/openstack/keymanager/v1/acls/urls.go000066400000000000000000000005201367513235700300520ustar00rootroot00000000000000package 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.12.0/openstack/keymanager/v1/containers/000077500000000000000000000000001367513235700277645ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/keymanager/v1/containers/doc.go000066400000000000000000000032521367513235700310620ustar00rootroot00000000000000/* 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 golang-github-gophercloud-gophercloud-0.12.0/openstack/keymanager/v1/containers/requests.go000066400000000000000000000175151367513235700321770ustar00rootroot00000000000000package 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) { resp, err := client.Get(getURL(client, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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,omitempty"` } // 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 } resp, err := client.Post(createURL(client), &b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{201}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete deletes a container. func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { resp, err := client.Delete(deleteURL(client, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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 } resp, err := client.Post(createConsumerURL(client, containerID), &b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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 } resp, err := client.Request("DELETE", url, &gophercloud.RequestOpts{ JSONBody: b, JSONResponse: &r.Body, OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // SecretRefBuilder allows extensions to add additional parameters to the // Create request. type SecretRefBuilder interface { ToContainerSecretRefMap() (map[string]interface{}, error) } // ToContainerSecretRefMap formats a SecretRefBuilder into a create // request. func (opts SecretRef) ToContainerSecretRefMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "") } // CreateSecret creates a new consumer. func CreateSecretRef(client *gophercloud.ServiceClient, containerID string, opts SecretRefBuilder) (r CreateSecretRefResult) { b, err := opts.ToContainerSecretRefMap() if err != nil { r.Err = err return } resp, err := client.Post(createSecretRefURL(client, containerID), &b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{201}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // DeleteSecret deletes a consumer. func DeleteSecretRef(client *gophercloud.ServiceClient, containerID string, opts SecretRefBuilder) (r DeleteSecretRefResult) { url := deleteSecretRefURL(client, containerID) b, err := opts.ToContainerSecretRefMap() if err != nil { r.Err = err return } resp, err := client.Request("DELETE", url, &gophercloud.RequestOpts{ JSONBody: b, OkCodes: []int{204}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } golang-github-gophercloud-gophercloud-0.12.0/openstack/keymanager/v1/containers/results.go000066400000000000000000000141451367513235700320210ustar00rootroot00000000000000package 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 } // Extract interprets any CreateSecretRefResult as a Container func (r CreateSecretRefResult) Extract() (*Container, error) { var c *Container err := r.ExtractInto(&c) return c, err } // CreateSecretRefResult is the response from a CreateSecretRef operation. // Call its Extract method to interpret it as a container. type CreateSecretRefResult struct { // This is not a typo. commonResult } // DeleteSecretRefResult is the response from a DeleteSecretRef operation. type DeleteSecretRefResult struct { gophercloud.ErrResult } golang-github-gophercloud-gophercloud-0.12.0/openstack/keymanager/v1/containers/testing/000077500000000000000000000000001367513235700314415ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/keymanager/v1/containers/testing/fixtures.go000066400000000000000000000237371367513235700336550ustar00rootroot00000000000000package 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.go000066400000000000000000000076061367513235700346340ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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) } golang-github-gophercloud-gophercloud-0.12.0/openstack/keymanager/v1/containers/urls.go000066400000000000000000000022401367513235700312760ustar00rootroot00000000000000package 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") } func createSecretRefURL(client *gophercloud.ServiceClient, id string) string { return client.ServiceURL("containers", id, "secrets") } func deleteSecretRefURL(client *gophercloud.ServiceClient, id string) string { return client.ServiceURL("containers", id, "secrets") } golang-github-gophercloud-gophercloud-0.12.0/openstack/keymanager/v1/orders/000077500000000000000000000000001367513235700271155ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/keymanager/v1/orders/doc.go000066400000000000000000000014061367513235700302120ustar00rootroot00000000000000/* 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 golang-github-gophercloud-gophercloud-0.12.0/openstack/keymanager/v1/orders/requests.go000066400000000000000000000070371367513235700313260ustar00rootroot00000000000000package 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) { resp, err := client.Get(getURL(client, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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 } resp, err := client.Post(createURL(client), &b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete deletes a orders. func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { resp, err := client.Delete(deleteURL(client, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } golang-github-gophercloud-gophercloud-0.12.0/openstack/keymanager/v1/orders/results.go000066400000000000000000000075751367513235700311630ustar00rootroot00000000000000package 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 } golang-github-gophercloud-gophercloud-0.12.0/openstack/keymanager/v1/orders/testing/000077500000000000000000000000001367513235700305725ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/keymanager/v1/orders/testing/fixtures.go000066400000000000000000000137401367513235700327770ustar00rootroot00000000000000package 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) }) } golang-github-gophercloud-gophercloud-0.12.0/openstack/keymanager/v1/orders/testing/requests_test.go000066400000000000000000000040031367513235700340300ustar00rootroot00000000000000package 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) } golang-github-gophercloud-gophercloud-0.12.0/openstack/keymanager/v1/orders/urls.go000066400000000000000000000007351367513235700304360ustar00rootroot00000000000000package 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.12.0/openstack/keymanager/v1/secrets/000077500000000000000000000000001367513235700272675ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/keymanager/v1/secrets/doc.go000066400000000000000000000053561367513235700303740ustar00rootroot00000000000000/* 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 // if "Extract" method is not called, the HTTP connection will remain consumed 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 golang-github-gophercloud-gophercloud-0.12.0/openstack/keymanager/v1/secrets/requests.go000066400000000000000000000306301367513235700314730ustar00rootroot00000000000000package 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) { resp, err := client.Get(getURL(client, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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}, KeepResponseBody: true, }) r.Body, r.Header, r.Err = gophercloud.ParseResponse(resp, 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"` // Expiration is the expiration date of the secret. Expiration *time.Time `json:"-"` } // ToSecretCreateMap formats a CreateOpts into a create request. func (opts CreateOpts) ToSecretCreateMap() (map[string]interface{}, error) { b, err := gophercloud.BuildRequestBody(opts, "") if err != nil { return nil, err } if opts.Expiration != nil { b["expiration"] = opts.Expiration.Format(gophercloud.RFC3339NoZ) } return b, nil } // 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 } resp, err := client.Post(createURL(client), &b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{201}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete deletes a secrets. func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { resp, err := client.Delete(deleteURL(client, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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, strings.NewReader(b), nil, &gophercloud.RequestOpts{ MoreHeaders: h, OkCodes: []int{204}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // GetMetadata will list metadata for a given secret. func GetMetadata(client *gophercloud.ServiceClient, secretID string) (r MetadataResult) { resp, err := client.Get(metadataURL(client, secretID), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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 } resp, err := client.Put(metadataURL(client, secretID), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{201}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // GetMetadatum will get a single key/value metadata from a secret. func GetMetadatum(client *gophercloud.ServiceClient, secretID string, key string) (r MetadatumResult) { resp, err := client.Get(metadatumURL(client, secretID, key), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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 } resp, err := client.Post(metadataURL(client, secretID), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{201}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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 } resp, err := client.Put(metadatumURL(client, secretID, key), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // DeleteMetadatum will delete an individual metadatum from a secret. func DeleteMetadatum(client *gophercloud.ServiceClient, secretID string, key string) (r MetadatumDeleteResult) { resp, err := client.Delete(metadatumURL(client, secretID, key), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } golang-github-gophercloud-gophercloud-0.12.0/openstack/keymanager/v1/secrets/results.go000066400000000000000000000137241367513235700313260ustar00rootroot00000000000000package 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 } 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 } golang-github-gophercloud-gophercloud-0.12.0/openstack/keymanager/v1/secrets/testing/000077500000000000000000000000001367513235700307445ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/keymanager/v1/secrets/testing/fixtures.go000066400000000000000000000270431367513235700331520ustar00rootroot00000000000000package 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", "expiration": "2028-06-21T02:49:48" }` // 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) th.TestBody(t, r, `foobar`) 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.go000066400000000000000000000116331367513235700341320ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/keymanager/v1/secrets/testingpackage testing import ( "testing" "time" "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) expiration := time.Date(2028, 6, 21, 2, 49, 48, 0, time.UTC) createOpts := secrets.CreateOpts{ Algorithm: "aes", BitLength: 256, Mode: "cbc", Name: "mysecret", Payload: "foobar", PayloadContentType: "text/plain", SecretType: secrets.OpaqueSecret, Expiration: &expiration, } 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) } golang-github-gophercloud-gophercloud-0.12.0/openstack/keymanager/v1/secrets/urls.go000066400000000000000000000017351367513235700306110ustar00rootroot00000000000000package 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.12.0/openstack/loadbalancer/000077500000000000000000000000001367513235700255555ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/loadbalancer/v2/000077500000000000000000000000001367513235700261045ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/loadbalancer/v2/amphorae/000077500000000000000000000000001367513235700277005ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/loadbalancer/v2/amphorae/doc.go000066400000000000000000000012631367513235700307760ustar00rootroot00000000000000/* 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) } Example to Failover an amphora ampID := "d67d56a6-4a86-4688-a282-f46444705c64" err := amphorae.Failover(octaviaClient, ampID).ExtractErr() if err != nil { panic(err) } */ package amphorae golang-github-gophercloud-gophercloud-0.12.0/openstack/loadbalancer/v2/amphorae/requests.go000066400000000000000000000045411367513235700321060ustar00rootroot00000000000000package 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) { resp, err := c.Get(resourceURL(c, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Failover performs a failover of an amphora. func Failover(c *gophercloud.ServiceClient, id string) (r FailoverResult) { resp, err := c.Put(failoverRootURL(c, id), nil, nil, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } golang-github-gophercloud-gophercloud-0.12.0/openstack/loadbalancer/v2/amphorae/results.go000066400000000000000000000106161367513235700317340ustar00rootroot00000000000000package 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 } // 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 } golang-github-gophercloud-gophercloud-0.12.0/openstack/loadbalancer/v2/amphorae/testing/000077500000000000000000000000001367513235700313555ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/loadbalancer/v2/amphorae/testing/doc.go000066400000000000000000000000201367513235700324410ustar00rootroot00000000000000package testing golang-github-gophercloud-gophercloud-0.12.0/openstack/loadbalancer/v2/amphorae/testing/fixtures.go000066400000000000000000000145131367513235700335610ustar00rootroot00000000000000package 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) }) } // HandleAmphoraFailoverSuccessfully sets up the test server to respond to an amphora failover request. func HandleAmphoraFailoverSuccessfully(t *testing.T) { th.Mux.HandleFunc("/v2.0/octavia/amphorae/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.go000066400000000000000000000034361367513235700345450ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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) } func TestFailoverAmphora(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleAmphoraFailoverSuccessfully(t) res := amphorae.Failover(fake.ServiceClient(), "36e08a3e-a78f-4b40-a229-1e7e23eee1ab") th.AssertNoErr(t, res.Err) } golang-github-gophercloud-gophercloud-0.12.0/openstack/loadbalancer/v2/amphorae/urls.go000066400000000000000000000007751367513235700312250ustar00rootroot00000000000000package amphorae import "github.com/gophercloud/gophercloud" const ( rootPath = "octavia" resourcePath = "amphorae" 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 failoverRootURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL(rootPath, resourcePath, id, failoverPath) } golang-github-gophercloud-gophercloud-0.12.0/openstack/loadbalancer/v2/apiversions/000077500000000000000000000000001367513235700304465ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/loadbalancer/v2/apiversions/doc.go000066400000000000000000000010111367513235700315330ustar00rootroot00000000000000/* Package apiversions provides information and interaction with the different API versions for the OpenStack Load Balancer service. This functionality is not restricted to this particular version. Example to List API Versions allPages, err := apiversions.List(loadbalancerClient).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 golang-github-gophercloud-gophercloud-0.12.0/openstack/loadbalancer/v2/apiversions/requests.go000066400000000000000000000006231367513235700326510ustar00rootroot00000000000000package apiversions import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // List lists all the load balancer 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)} }) } golang-github-gophercloud-gophercloud-0.12.0/openstack/loadbalancer/v2/apiversions/results.go000066400000000000000000000017101367513235700324750ustar00rootroot00000000000000package apiversions import "github.com/gophercloud/gophercloud/pagination" // APIVersion represents an API version for load balancer. 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 } golang-github-gophercloud-gophercloud-0.12.0/openstack/loadbalancer/v2/apiversions/testing/000077500000000000000000000000001367513235700321235ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/loadbalancer/v2/apiversions/testing/doc.go000066400000000000000000000000521367513235700332140ustar00rootroot00000000000000// apiversions unit tests package testing fixture.go000066400000000000000000000041531367513235700340640ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/loadbalancer/v2/apiversions/testingpackage testing import ( "fmt" "net/http" "testing" "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/apiversions" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) const OctaviaAllAPIVersionsResponse = ` { "versions": [ { "id": "v1", "links": [ { "href": "http://10.0.0.105:9876/v1", "rel": "self" } ], "status": "DEPRECATED", "updated": "2014-12-11T00:00:00Z" }, { "id": "v2.0", "links": [ { "href": "http://10.0.0.105:9876/v2", "rel": "self" } ], "status": "SUPPORTED", "updated": "2016-12-11T00:00:00Z" }, { "id": "v2.1", "links": [ { "href": "http://10.0.0.105:9876/v2", "rel": "self" } ], "status": "SUPPORTED", "updated": "2018-04-20T00:00:00Z" }, { "id": "v2.2", "links": [ { "href": "http://10.0.0.105:9876/v2", "rel": "self" } ], "status": "CURRENT", "updated": "2018-07-31T00:00:00Z" } ] } ` var OctaviaAllAPIVersionResults = []apiversions.APIVersion{ apiversions.APIVersion{ ID: "v1", Status: "DEPRECATED", }, apiversions.APIVersion{ ID: "v2.0", Status: "SUPPORTED", }, apiversions.APIVersion{ ID: "v2.1", Status: "SUPPORTED", }, apiversions.APIVersion{ ID: "v2.2", Status: "CURRENT", }, } 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, OctaviaAllAPIVersionsResponse) }) } requests_test.go000066400000000000000000000010751367513235700353100ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/loadbalancer/v2/apiversions/testingpackage testing import ( "testing" "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/apiversions" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) func TestListVersions(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, OctaviaAllAPIVersionResults, actual) } golang-github-gophercloud-gophercloud-0.12.0/openstack/loadbalancer/v2/apiversions/urls.go000066400000000000000000000004731367513235700317660ustar00rootroot00000000000000package apiversions import ( "strings" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack/utils" ) func listURL(c *gophercloud.ServiceClient) string { baseEndpoint, _ := utils.BaseEndpoint(c.Endpoint) endpoint := strings.TrimRight(baseEndpoint, "/") + "/" return endpoint } golang-github-gophercloud-gophercloud-0.12.0/openstack/loadbalancer/v2/doc.go000066400000000000000000000002441367513235700272000ustar00rootroot00000000000000// 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.12.0/openstack/loadbalancer/v2/l7policies/000077500000000000000000000000001367513235700301565ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/loadbalancer/v2/l7policies/doc.go000066400000000000000000000060721367513235700312570ustar00rootroot00000000000000/* 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 golang-github-gophercloud-gophercloud-0.12.0/openstack/loadbalancer/v2/l7policies/requests.go000066400000000000000000000317111367513235700323630ustar00rootroot00000000000000package 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"` // The administrative state of the Loadbalancer. A valid value is true (UP) // or false (DOWN). AdminStateUp *bool `json:"admin_state_up,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 } resp, err := c.Post(rootURL(c), b, &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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"` Description string `q:"description"` 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"` Position int32 `q:"position"` AdminStateUp bool `q:"admin_state_up"` 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) { resp, err := c.Get(resourceURL(c, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete will permanently delete a particular l7policy based on its unique ID. func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { resp, err := c.Delete(resourceURL(c, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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"` // The administrative state of the Loadbalancer. A valid value is true (UP) // or false (DOWN). AdminStateUp *bool `json:"admin_state_up,omitempty"` } // ToL7PolicyUpdateMap builds a request body from UpdateOpts. func (opts UpdateOpts) ToL7PolicyUpdateMap() (map[string]interface{}, error) { b, err := gophercloud.BuildRequestBody(opts, "l7policy") if err != nil { return nil, err } m := b["l7policy"].(map[string]interface{}) if m["redirect_pool_id"] == "" { m["redirect_pool_id"] = nil } if m["redirect_url"] == "" { m["redirect_url"] = nil } return b, nil } // 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 } resp, err := c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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"` // The administrative state of the Loadbalancer. A valid value is true (UP) // or false (DOWN). AdminStateUp *bool `json:"admin_state_up,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 } resp, err := c.Post(ruleRootURL(c, policyID), b, &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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"` CompareType CompareType `q:"compare_type"` Value string `q:"value"` Key string `q:"key"` Invert bool `q:"invert"` AdminStateUp bool `q:"admin_state_up"` 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) { resp, err := c.Get(ruleResourceURL(c, policyID, ruleID), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // DeleteRule will remove a Rule from a particular L7Policy. func DeleteRule(c *gophercloud.ServiceClient, policyID string, ruleID string) (r DeleteRuleResult) { resp, err := c.Delete(ruleResourceURL(c, policyID, ruleID), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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"` // The administrative state of the Loadbalancer. A valid value is true (UP) // or false (DOWN). AdminStateUp *bool `json:"admin_state_up,omitempty"` } // ToRuleUpdateMap builds a request body from UpdateRuleOpts. func (opts UpdateRuleOpts) ToRuleUpdateMap() (map[string]interface{}, error) { b, err := gophercloud.BuildRequestBody(opts, "rule") if err != nil { return nil, err } if m := b["rule"].(map[string]interface{}); m["key"] == "" { m["key"] = nil } return b, nil } // 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 } resp, err := c.Put(ruleResourceURL(c, policyID, ruleID), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201, 202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } golang-github-gophercloud-gophercloud-0.12.0/openstack/loadbalancer/v2/l7policies/results.go000066400000000000000000000162451367513235700322160ustar00rootroot00000000000000package 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"` // The provisioning status of the L7 policy. // This value is ACTIVE, PENDING_* or ERROR. ProvisioningStatus string `json:"provisioning_status"` // The operating status of the L7 policy. OperatingStatus string `json:"operating_status"` // 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"` // The provisioning status of the L7 rule. // This value is ACTIVE, PENDING_* or ERROR. ProvisioningStatus string `json:"provisioning_status"` // The operating status of the L7 policy. OperatingStatus string `json:"operating_status"` } 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 } golang-github-gophercloud-gophercloud-0.12.0/openstack/loadbalancer/v2/l7policies/testing/000077500000000000000000000000001367513235700316335ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/loadbalancer/v2/l7policies/testing/doc.go000066400000000000000000000000511367513235700327230ustar00rootroot00000000000000// l7policies unit tests package testing fixtures.go000066400000000000000000000332561367513235700337650ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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{}, } L7PolicyNullRedirectURLUpdated = 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: "", AdminStateUp: true, Rules: []l7policies.Rule{}, } RulePath = l7policies.Rule{ ID: "16621dbb-a736-4888-a57a-3ecd53df784c", RuleType: "PATH", CompareType: "REGEX", Value: "/images*", ProjectID: "e3cd678b11784734bc366148aa37580e", Key: "", Invert: true, 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_url": "http://www.new-example.com", "action": "REDIRECT_TO_URL", "position": 1, "project_id": "e3cd678b11784734bc366148aa37580e", "id": "8a1412f0-4c32-4257-8b07-af4770b604fd", "name": "NewL7PolicyName", "rules": [] } } ` // PostUpdateL7PolicyNullRedirectURLBody is the canned response body of a Update request // on an existing l7policy with a null redirect_url . const PostUpdateL7PolicyNullRedirectURLBody = ` { "l7policy": { "listener_id": "023f2e34-7806-443b-bfae-16c324569a3d", "description": "Redirect requests to example.com", "admin_state_up": true, "redirect_url": null, "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) }) } // HandleL7PolicyUpdateNullRedirectURLSuccessfully sets up the test server to respond to a l7policy Update request. func HandleL7PolicyUpdateNullRedirectURLSuccessfully(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", "redirect_url": null } }`) fmt.Fprintf(w, PostUpdateL7PolicyNullRedirectURLBody) }) } // SingleRuleBody is the canned body of a Get request on an existing rule. const SingleRuleBody = ` { "rule": { "compare_type": "REGEX", "invert": true, "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": true, "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", "invert": false, "key": null, "type": "PATH", "value": "/images/special*" } }`) fmt.Fprintf(w, PostUpdateRuleBody) }) } requests_test.go000066400000000000000000000211101367513235700350100ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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" redirectURL := "http://www.new-example.com" actual, err := l7policies.Update(client, "8a1412f0-4c32-4257-8b07-af4770b604fd", l7policies.UpdateOpts{ Name: &newName, Action: l7policies.ActionRedirectToURL, RedirectURL: &redirectURL, }).Extract() if err != nil { t.Fatalf("Unexpected Update error: %v", err) } th.CheckDeepEquals(t, L7PolicyUpdated, *actual) } func TestUpdateL7PolicyNullRedirectURL(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleL7PolicyUpdateNullRedirectURLSuccessfully(t) client := fake.ServiceClient() newName := "NewL7PolicyName" redirectURL := "" actual, err := l7policies.Update(client, "8a1412f0-4c32-4257-8b07-af4770b604fd", l7policies.UpdateOpts{ Name: &newName, RedirectURL: &redirectURL, }).Extract() if err != nil { t.Fatalf("Unexpected Update error: %v", err) } th.CheckDeepEquals(t, L7PolicyNullRedirectURLUpdated, *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() invert := false key := "" 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*", Key: &key, Invert: &invert, }).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") } } golang-github-gophercloud-gophercloud-0.12.0/openstack/loadbalancer/v2/l7policies/urls.go000066400000000000000000000012501367513235700314700ustar00rootroot00000000000000package 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.12.0/openstack/loadbalancer/v2/listeners/000077500000000000000000000000001367513235700301145ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/loadbalancer/v2/listeners/doc.go000066400000000000000000000033331367513235700312120ustar00rootroot00000000000000/* 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 i181000 := 181000 updateOpts := listeners.UpdateOpts{ ConnLimit: &i1001, TimeoutClientData: &i181000, TimeoutMemberData: &i181000, } 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 golang-github-gophercloud-gophercloud-0.12.0/openstack/loadbalancer/v2/listeners/requests.go000066400000000000000000000225351367513235700323250ustar00rootroot00000000000000package 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" ProtocolUDP Protocol = "UDP" ProtocolPROXY Protocol = "PROXY" 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"` TimeoutClientData *int `q:"timeout_client_data"` TimeoutMemberData *int `q:"timeout_member_data"` TimeoutMemberConnect *int `q:"timeout_member_connect"` TimeoutTCPInspect *int `q:"timeout_tcp_inspect"` } // 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"` // Frontend client inactivity timeout in milliseconds TimeoutClientData *int `json:"timeout_client_data,omitempty"` // Backend member inactivity timeout in milliseconds TimeoutMemberData *int `json:"timeout_member_data,omitempty"` // Backend member connection timeout in milliseconds TimeoutMemberConnect *int `json:"timeout_member_connect,omitempty"` // Time, in milliseconds, to wait for additional TCP packets for content inspection TimeoutTCPInspect *int `json:"timeout_tcp_inspect,omitempty"` // A dictionary of optional headers to insert into the request before it is sent to the backend member. InsertHeaders map[string]string `json:"insert_headers,omitempty"` // A list of IPv4, IPv6 or mix of both CIDRs AllowedCIDRs []string `json:"allowed_cidrs,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 } resp, err := c.Post(rootURL(c), b, &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get retrieves a particular Listeners based on its unique ID. func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { resp, err := c.Get(resourceURL(c, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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"` // 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"` // Frontend client inactivity timeout in milliseconds TimeoutClientData *int `json:"timeout_client_data,omitempty"` // Backend member inactivity timeout in milliseconds TimeoutMemberData *int `json:"timeout_member_data,omitempty"` // Backend member connection timeout in milliseconds TimeoutMemberConnect *int `json:"timeout_member_connect,omitempty"` // Time, in milliseconds, to wait for additional TCP packets for content inspection TimeoutTCPInspect *int `json:"timeout_tcp_inspect,omitempty"` // A dictionary of optional headers to insert into the request before it is sent to the backend member. InsertHeaders *map[string]string `json:"insert_headers,omitempty"` // A list of IPv4, IPv6 or mix of both CIDRs AllowedCIDRs *[]string `json:"allowed_cidrs,omitempty"` } // ToListenerUpdateMap builds a request body from UpdateOpts. func (opts UpdateOpts) ToListenerUpdateMap() (map[string]interface{}, error) { b, err := gophercloud.BuildRequestBody(opts, "listener") if err != nil { return nil, err } if m := b["listener"].(map[string]interface{}); m["default_pool_id"] == "" { m["default_pool_id"] = nil } return b, nil } // 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 } resp, err := c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete will permanently delete a particular Listeners based on its unique ID. func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { resp, err := c.Delete(resourceURL(c, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // GetStats will return the shows the current statistics of a particular Listeners. func GetStats(c *gophercloud.ServiceClient, id string) (r StatsResult) { resp, err := c.Get(statisticsRootURL(c, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } golang-github-gophercloud-gophercloud-0.12.0/openstack/loadbalancer/v2/listeners/results.go000066400000000000000000000134571367513235700321560ustar00rootroot00000000000000package listeners import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/l7policies" "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"` // L7policies are the L7 policies which are part of this listener. L7Policies []l7policies.L7Policy `json:"l7policies"` // The provisioning status of the Listener. // This value is ACTIVE, PENDING_* or ERROR. ProvisioningStatus string `json:"provisioning_status"` // Frontend client inactivity timeout in milliseconds TimeoutClientData int `json:"timeout_client_data"` // Backend member inactivity timeout in milliseconds TimeoutMemberData int `json:"timeout_member_data"` // Backend member connection timeout in milliseconds TimeoutMemberConnect int `json:"timeout_member_connect"` // Time, in milliseconds, to wait for additional TCP packets for content inspection TimeoutTCPInspect int `json:"timeout_tcp_inspect"` // A dictionary of optional headers to insert into the request before it is sent to the backend member. InsertHeaders map[string]string `json:"insert_headers"` // A list of IPv4, IPv6 or mix of both CIDRs AllowedCIDRs []string `json:"allowed_cidrs"` } 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 } golang-github-gophercloud-gophercloud-0.12.0/openstack/loadbalancer/v2/listeners/testing/000077500000000000000000000000001367513235700315715ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/loadbalancer/v2/listeners/testing/doc.go000066400000000000000000000000501367513235700326600ustar00rootroot00000000000000// listeners unit tests package testing golang-github-gophercloud-gophercloud-0.12.0/openstack/loadbalancer/v2/listeners/testing/fixtures.go000066400000000000000000000253641367513235700340030ustar00rootroot00000000000000package 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"], "allowed_cidrs": [ "192.0.2.0/24", "198.51.100.0/24" ] }, { "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"], "timeout_client_data": 50000, "timeout_member_data": 50000, "timeout_member_connect": 5000, "timeout_tcp_inspect": 0, "insert_headers": { "X-Forwarded-For": "true" }, "allowed_cidrs": [ "192.0.2.0/24", "198.51.100.0/24" ] } ] } ` // 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"], "timeout_client_data": 50000, "timeout_member_data": 50000, "timeout_member_connect": 5000, "timeout_tcp_inspect": 0, "insert_headers": { "X-Forwarded-For": "true" }, "allowed_cidrs": [ "192.0.2.0/24", "198.51.100.0/24" ] } } ` // 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"], "timeout_client_data": 181000, "timeout_member_data": 181000, "timeout_member_connect": 181000, "timeout_tcp_inspect": 181000, "insert_headers": { "X-Forwarded-For": "true", "X-Forwarded-Port": "false" } } } ` // 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"}, AllowedCIDRs: []string{"192.0.2.0/24", "198.51.100.0/24"}, } 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"}, TimeoutClientData: 50000, TimeoutMemberData: 50000, TimeoutMemberConnect: 5000, TimeoutTCPInspect: 0, InsertHeaders: map[string]string{"X-Forwarded-For": "true"}, AllowedCIDRs: []string{"192.0.2.0/24", "198.51.100.0/24"}, } 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"}, TimeoutClientData: 181000, TimeoutMemberData: 181000, TimeoutMemberConnect: 181000, TimeoutTCPInspect: 181000, InsertHeaders: map[string]string{ "X-Forwarded-For": "true", "X-Forwarded-Port": "false", }, } 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, "insert_headers": { "X-Forwarded-For": "true" }, "allowed_cidrs": [ "192.0.2.0/24", "198.51.100.0/24" ] } }`) 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", "default_pool_id": null, "connection_limit": 1001, "timeout_client_data": 181000, "timeout_member_data": 181000, "timeout_member_connect": 181000, "timeout_tcp_inspect": 181000, "insert_headers": { "X-Forwarded-For": "true", "X-Forwarded-Port": "false" } } }`) 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.go000066400000000000000000000113441367513235700347560ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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, InsertHeaders: map[string]string{"X-Forwarded-For": "true"}, AllowedCIDRs: []string{"192.0.2.0/24", "198.51.100.0/24"}, }).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 i181000 := 181000 name := "NewListenerName" defaultPoolID := "" insertHeaders := map[string]string{ "X-Forwarded-For": "true", "X-Forwarded-Port": "false", } actual, err := listeners.Update(client, "4ec89087-d057-4e2c-911f-60a3b47ee304", listeners.UpdateOpts{ Name: &name, ConnLimit: &i1001, DefaultPoolID: &defaultPoolID, TimeoutMemberData: &i181000, TimeoutClientData: &i181000, TimeoutMemberConnect: &i181000, TimeoutTCPInspect: &i181000, InsertHeaders: &insertHeaders, }).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) } golang-github-gophercloud-gophercloud-0.12.0/openstack/loadbalancer/v2/listeners/urls.go000066400000000000000000000010041367513235700314230ustar00rootroot00000000000000package 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) } golang-github-gophercloud-gophercloud-0.12.0/openstack/loadbalancer/v2/loadbalancers/000077500000000000000000000000001367513235700306765ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/loadbalancer/v2/loadbalancers/doc.go000066400000000000000000000041141367513235700317720ustar00rootroot00000000000000/* 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", FlavorID: "60df399a-ee85-11e9-81b4-2a2ae2dbcce4", Provider: "haproxy", Tags: []string{"test", "stage"}, } lb, err := loadbalancers.Create(networkClient, createOpts).Extract() if err != nil { panic(err) } Example to Update a Load Balancer lbID := "d67d56a6-4a86-4688-a282-f46444705c64" name := "new-name" updateOpts := loadbalancers.UpdateOpts{ Name: &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 golang-github-gophercloud-gophercloud-0.12.0/openstack/loadbalancer/v2/loadbalancers/requests.go000066400000000000000000000214471367513235700331100ustar00rootroot00000000000000package 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"` VipNetworkID string `q:"vip_network_id"` ID string `q:"id"` OperatingStatus string `q:"operating_status"` Name string `q:"name"` FlavorID string `q:"flavor_id"` Provider string `q:"provider"` Limit int `q:"limit"` Marker string `q:"marker"` SortKey string `q:"sort_key"` SortDir string `q:"sort_dir"` Tags []string `q:"tags"` TagsAny []string `q:"tags-any"` TagsNot []string `q:"not-tags"` TagsNotAny []string `q:"not-tags-any"` } // 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"` // Providing a neutron port ID for the vip_port_id tells Octavia to use this // port for the VIP. If the port has more than one subnet you must specify // either the vip_subnet_id or vip_address to clarify which address should // be used for the VIP. VipPortID string `json:"vip_port_id,omitempty"` // The subnet 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,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). VipNetworkID string `json:"vip_network_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. FlavorID string `json:"flavor_id,omitempty"` // The name of the provider. Provider string `json:"provider,omitempty"` // Tags is a set of resource tags. Tags []string `json:"tags,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 } resp, err := c.Post(rootURL(c), b, &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get retrieves a particular Loadbalancer based on its unique ID. func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { resp, err := c.Get(resourceURL(c, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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"` // Tags is a set of resource tags. Tags *[]string `json:"tags,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 } resp, err := c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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 } resp, err := c.Delete(url, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // GetStatuses will return the status of a particular LoadBalancer. func GetStatuses(c *gophercloud.ServiceClient, id string) (r GetStatusesResult) { resp, err := c.Get(statusRootURL(c, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // GetStats will return the shows the current statistics of a particular LoadBalancer. func GetStats(c *gophercloud.ServiceClient, id string) (r StatsResult) { resp, err := c.Get(statisticsRootURL(c, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Failover performs a failover of a load balancer. func Failover(c *gophercloud.ServiceClient, id string) (r FailoverResult) { resp, err := c.Put(failoverRootURL(c, id), nil, nil, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } golang-github-gophercloud-gophercloud-0.12.0/openstack/loadbalancer/v2/loadbalancers/results.go000066400000000000000000000156721367513235700327410ustar00rootroot00000000000000package loadbalancers import ( "encoding/json" "time" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/listeners" "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/pools" "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"` // UpdatedAt and CreatedAt contain ISO-8601 timestamps of when the state of the // loadbalancer last changed, and when it was created. UpdatedAt time.Time `json:"-"` CreatedAt time.Time `json:"-"` // 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 UUID of the network on which to allocate the virtual IP for the // Loadbalancer address. VipNetworkID string `json:"vip_network_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. FlavorID string `json:"flavor_id"` // The name of the provider. Provider string `json:"provider"` // Listeners are the listeners related to this Loadbalancer. Listeners []listeners.Listener `json:"listeners"` // Pools are the pools related to this Loadbalancer. Pools []pools.Pool `json:"pools"` // Tags is a list of resource tags. Tags are arbitrarily defined strings // attached to the resource. Tags []string `json:"tags"` } func (r *LoadBalancer) UnmarshalJSON(b []byte) error { type tmp LoadBalancer // Support for older neutron time format var s1 struct { tmp CreatedAt gophercloud.JSONRFC3339NoZ `json:"created_at"` UpdatedAt gophercloud.JSONRFC3339NoZ `json:"updated_at"` } err := json.Unmarshal(b, &s1) if err == nil { *r = LoadBalancer(s1.tmp) r.CreatedAt = time.Time(s1.CreatedAt) r.UpdatedAt = time.Time(s1.UpdatedAt) return nil } // Support for newer neutron time format var s2 struct { tmp CreatedAt time.Time `json:"created_at"` UpdatedAt time.Time `json:"updated_at"` } err = json.Unmarshal(b, &s2) if err != nil { return err } *r = LoadBalancer(s2.tmp) r.CreatedAt = time.Time(s2.CreatedAt) r.UpdatedAt = time.Time(s2.UpdatedAt) return nil } // 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 } golang-github-gophercloud-gophercloud-0.12.0/openstack/loadbalancer/v2/loadbalancers/testing/000077500000000000000000000000001367513235700323535ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/loadbalancer/v2/loadbalancers/testing/doc.go000066400000000000000000000000541367513235700334460ustar00rootroot00000000000000// loadbalancers unit tests package testing fixtures.go000066400000000000000000000311631367513235700345000ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/loadbalancer/v2/loadbalancers/testingpackage testing import ( "fmt" "net/http" "testing" "time" "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" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) // 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", "created_at": "2019-06-30T04:15:37", "updated_at": "2019-06-30T05:18:49", "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_id": "60df399a-ee85-11e9-81b4-2a2ae2dbcce4", "provider": "haproxy", "admin_state_up": true, "provisioning_status": "ACTIVE", "operating_status": "ONLINE", "tags": ["test", "stage"] }, { "id": "36e08a3e-a78f-4b40-a229-1e7e23eee1ab", "project_id": "54030507-44f7-473c-9342-b4d14a95f692", "created_at": "2019-06-30T04:15:37", "updated_at": "2019-06-30T05:18:49", "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_id": "bba40eb2-ee8c-11e9-81b4-2a2ae2dbcce4", "provider": "haproxy", "admin_state_up": true, "provisioning_status": "PENDING_CREATE", "operating_status": "OFFLINE", "tags": ["test", "stage"] } ] } ` // 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", "created_at": "2019-06-30T04:15:37", "updated_at": "2019-06-30T05:18:49", "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_id": "bba40eb2-ee8c-11e9-81b4-2a2ae2dbcce4", "provider": "haproxy", "admin_state_up": true, "provisioning_status": "PENDING_CREATE", "operating_status": "OFFLINE", "tags": ["test", "stage"] } } ` // 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", "created_at": "2019-06-30T04:15:37", "updated_at": "2019-06-30T05:18:49", "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_id": "bba40eb2-ee8c-11e9-81b4-2a2ae2dbcce4", "provider": "haproxy", "admin_state_up": true, "provisioning_status": "PENDING_CREATE", "operating_status": "OFFLINE", "tags": ["test"] } } ` // 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", "tags": ["test", "stage"], "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 createdTime, _ = time.Parse(time.RFC3339, "2019-06-30T04:15:37Z") var updatedTime, _ = time.Parse(time.RFC3339, "2019-06-30T05:18:49Z") var ( LoadbalancerWeb = loadbalancers.LoadBalancer{ ID: "c331058c-6a40-4144-948e-b9fb1df9db4b", ProjectID: "54030507-44f7-473c-9342-b4d14a95f692", CreatedAt: createdTime, UpdatedAt: updatedTime, 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", FlavorID: "60df399a-ee85-11e9-81b4-2a2ae2dbcce4", Provider: "haproxy", AdminStateUp: true, ProvisioningStatus: "ACTIVE", OperatingStatus: "ONLINE", Tags: []string{"test", "stage"}, } LoadbalancerDb = loadbalancers.LoadBalancer{ ID: "36e08a3e-a78f-4b40-a229-1e7e23eee1ab", ProjectID: "54030507-44f7-473c-9342-b4d14a95f692", CreatedAt: createdTime, UpdatedAt: updatedTime, 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", FlavorID: "bba40eb2-ee8c-11e9-81b4-2a2ae2dbcce4", Provider: "haproxy", AdminStateUp: true, ProvisioningStatus: "PENDING_CREATE", OperatingStatus: "OFFLINE", Tags: []string{"test", "stage"}, } LoadbalancerUpdated = loadbalancers.LoadBalancer{ ID: "36e08a3e-a78f-4b40-a229-1e7e23eee1ab", ProjectID: "54030507-44f7-473c-9342-b4d14a95f692", CreatedAt: createdTime, UpdatedAt: updatedTime, 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", FlavorID: "bba40eb2-ee8c-11e9-81b4-2a2ae2dbcce4", Provider: "haproxy", AdminStateUp: true, ProvisioningStatus: "PENDING_CREATE", OperatingStatus: "OFFLINE", Tags: []string{"test"}, } LoadbalancerStatusesTree = loadbalancers.StatusTree{ Loadbalancer: &loadbalancers.LoadBalancer{ ID: "36e08a3e-a78f-4b40-a229-1e7e23eee1ab", Name: "db_lb", ProvisioningStatus: "PENDING_UPDATE", OperatingStatus: "ACTIVE", Tags: []string{"test", "stage"}, 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_port_id": "2bf413c8-41a9-4477-b505-333d5cbe8b55", "vip_subnet_id": "9cedb85d-0759-4898-8a4b-fa5a5ea10086", "vip_address": "10.30.176.48", "flavor_id": "bba40eb2-ee8c-11e9-81b4-2a2ae2dbcce4", "provider": "haproxy", "admin_state_up": true, "tags": ["test", "stage"] } }`) 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", "tags": ["test"] } }`) 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.go000066400000000000000000000111731367513235700355400ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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, VipPortID: "2bf413c8-41a9-4477-b505-333d5cbe8b55", VipSubnetID: "9cedb85d-0759-4898-8a4b-fa5a5ea10086", VipAddress: "10.30.176.48", FlavorID: "bba40eb2-ee8c-11e9-81b4-2a2ae2dbcce4", Provider: "haproxy", Tags: []string{"test", "stage"}, }).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, LoadbalancerDb, *actual) } 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() name := "NewLoadbalancerName" tags := []string{"test"} actual, err := loadbalancers.Update(client, "36e08a3e-a78f-4b40-a229-1e7e23eee1ab", loadbalancers.UpdateOpts{ Name: &name, Tags: &tags, }).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) } golang-github-gophercloud-gophercloud-0.12.0/openstack/loadbalancer/v2/loadbalancers/urls.go000066400000000000000000000015221367513235700322120ustar00rootroot00000000000000package 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.12.0/openstack/loadbalancer/v2/monitors/000077500000000000000000000000001367513235700277565ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/loadbalancer/v2/monitors/doc.go000066400000000000000000000030331367513235700310510ustar00rootroot00000000000000/* 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, MaxRetriesDown: 4, 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, MaxRetriesDown: 8, 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 golang-github-gophercloud-gophercloud-0.12.0/openstack/loadbalancer/v2/monitors/requests.go000066400000000000000000000214541367513235700321660ustar00rootroot00000000000000package 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"` MaxRetriesDown int `q:"max_retries_down"` 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"` // Number of permissible ping failures befor changing the member's // status to ERROR. Must be a number between 1 and 10. MaxRetriesDown int `json:"max_retries_down,omitempty"` // URI path that will be accessed if Monitor type is HTTP or HTTPS. 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", a range like "200-202", or a combination like // "200-202, 401". 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) { return gophercloud.BuildRequestBody(opts, "healthmonitor") } /* 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 } resp, err := c.Post(rootURL(c), b, &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get retrieves a particular Health Monitor based on its unique ID. func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { resp, err := c.Get(resourceURL(c, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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"` // Number of permissible ping failures befor changing the member's // status to ERROR. Must be a number between 1 and 10. MaxRetriesDown int `json:"max_retries_down,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 } resp, err := c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete will permanently delete a particular Monitor based on its unique ID. func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { resp, err := c.Delete(resourceURL(c, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } golang-github-gophercloud-gophercloud-0.12.0/openstack/loadbalancer/v2/monitors/results.go000066400000000000000000000120431367513235700320060ustar00rootroot00000000000000package 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"` // Number of allowed connection failures before changing the status of the // member to Error. A valid value is from 1 to 10. MaxRetriesDown int `json:"max_retries_down"` // 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"` // The operating status of the monitor. OperatingStatus string `json:"operating_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 } golang-github-gophercloud-gophercloud-0.12.0/openstack/loadbalancer/v2/monitors/testing/000077500000000000000000000000001367513235700314335ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/loadbalancer/v2/monitors/testing/doc.go000066400000000000000000000000471367513235700325300ustar00rootroot00000000000000// monitors unit tests package testing golang-github-gophercloud-gophercloud-0.12.0/openstack/loadbalancer/v2/monitors/testing/fixtures.go000066400000000000000000000152461367513235700336430ustar00rootroot00000000000000package 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, "max_retries_down":7, "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, "max_retries_down":4, "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, "max_retries_down":4, "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, "max_retries_down":8, "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, MaxRetriesDown: 7, 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, MaxRetriesDown: 4, 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, MaxRetriesDown: 8, 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, "max_retries_down":4, "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, "max_retries_down": 8, "url_path": "/another_check", "expected_codes": "301" } }`) fmt.Fprintf(w, PostUpdateHealthmonitorBody) }) } requests_test.go000066400000000000000000000101251367513235700346140ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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, MaxRetriesDown: 4, 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() name := "NewHealthmonitorName" actual, err := monitors.Update(client, "5d4b5228-33b0-4e60-b225-9b727c1a20e7", monitors.UpdateOpts{ Name: &name, Delay: 3, Timeout: 20, MaxRetries: 10, MaxRetriesDown: 8, 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") } } golang-github-gophercloud-gophercloud-0.12.0/openstack/loadbalancer/v2/monitors/urls.go000066400000000000000000000005351367513235700312750ustar00rootroot00000000000000package 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.12.0/openstack/loadbalancer/v2/pools/000077500000000000000000000000001367513235700272405ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/loadbalancer/v2/pools/doc.go000066400000000000000000000064261367513235700303440ustar00rootroot00000000000000/* 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" weight := 10 createOpts := pools.CreateMemberOpts{ Name: "db", SubnetID: "1981f108-3c48-48d2-b908-30f7d28532c9", Address: "10.0.2.11", ProtocolPort: 80, Weight: &weight, } 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" weight := 4 updateOpts := pools.UpdateMemberOpts{ Name: "new-name", Weight: &weight, } 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" weight_1 := 20 member1 := pools.BatchUpdateMemberOpts{ Address: "192.0.2.16", ProtocolPort: 80, Name: "web-server-1", SubnetID: "bbb35f84-35cc-4b2f-84c2-a6a29bba68aa", Weight: &weight_1, } weight_2 := 10 member2 := pools.BatchUpdateMemberOpts{ Address: "192.0.2.17", ProtocolPort: 80, Name: "web-server-2", Weight: &weight_2, 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 golang-github-gophercloud-gophercloud-0.12.0/openstack/loadbalancer/v2/pools/requests.go000066400000000000000000000366011367513235700314500ustar00rootroot00000000000000package 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" ProtocolUDP Protocol = "UDP" ProtocolPROXY Protocol = "PROXY" 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, ProtocolUDP, ProtocolPROXY, 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 } resp, err := c.Post(rootURL(c), b, &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get retrieves a particular pool based on its unique ID. func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { resp, err := c.Get(resourceURL(c, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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 } resp, err := c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete will permanently delete a particular pool based on its unique ID. func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { resp, err := c.Delete(resourceURL(c, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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"` // Is the member a backup? Backup members only receive traffic when all non-backup members are down. Backup *bool `json:"backup,omitempty"` // An alternate IP address used for health monitoring a backend member. MonitorAddress string `json:"monitor_address,omitempty"` // An alternate protocol port used for health monitoring a backend member. MonitorPort *int `json:"monitor_port,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 CreateMemberOptsBuilder) (r CreateMemberResult) { b, err := opts.ToMemberCreateMap() if err != nil { r.Err = err return } resp, err := c.Post(memberRootURL(c, poolID), b, &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // GetMember retrieves a particular Pool Member based on its unique ID. func GetMember(c *gophercloud.ServiceClient, poolID string, memberID string) (r GetMemberResult) { resp, err := c.Get(memberResourceURL(c, poolID, memberID), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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 } resp, err := c.Put(memberResourceURL(c, poolID, memberID), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201, 202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // BatchUpdateMemberOptsBuilder allows extensions to add additional parameters to the BatchUpdateMembers request. type BatchUpdateMemberOptsBuilder interface { ToBatchMemberUpdateMap() (map[string]interface{}, error) } // BatchUpdateMemberOpts is the common options struct used in this package's BatchUpdateMembers // operation. type BatchUpdateMemberOpts 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"` } // ToBatchMemberUpdateMap builds a request body from BatchUpdateMemberOpts. func (opts BatchUpdateMemberOpts) ToBatchMemberUpdateMap() (map[string]interface{}, error) { b, err := gophercloud.BuildRequestBody(opts, "") if err != nil { return nil, err } if b["subnet_id"] == "" { b["subnet_id"] = nil } return b, nil } // BatchUpdateMembers updates the pool members in batch func BatchUpdateMembers(c *gophercloud.ServiceClient, poolID string, opts []BatchUpdateMemberOpts) (r UpdateMembersResult) { 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} resp, err := c.Put(memberRootURL(c, poolID), b, nil, &gophercloud.RequestOpts{OkCodes: []int{202}}) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // DisassociateMember will remove and disassociate a Member from a particular // Pool. func DeleteMember(c *gophercloud.ServiceClient, poolID string, memberID string) (r DeleteMemberResult) { resp, err := c.Delete(memberResourceURL(c, poolID, memberID), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } golang-github-gophercloud-gophercloud-0.12.0/openstack/loadbalancer/v2/pools/results.go000066400000000000000000000235521367513235700312770ustar00rootroot00000000000000package pools import ( "encoding/json" "time" "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"` // The operating status of the pool. OperatingStatus string `json:"operating_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"` // DateTime when the member was created CreatedAt time.Time `json:"-"` // DateTime when the member was updated UpdatedAt time.Time `json:"-"` // The operating status of the member OperatingStatus string `json:"operating_status"` // Is the member a backup? Backup members only receive traffic when all non-backup members are down. Backup bool `json:"backup"` // An alternate IP address used for health monitoring a backend member. MonitorAddress string `json:"monitor_address"` // An alternate protocol port used for health monitoring a backend member. MonitorPort int `json:"monitor_port"` } // 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 } func (r *Member) UnmarshalJSON(b []byte) error { type tmp Member var s struct { tmp CreatedAt gophercloud.JSONRFC3339NoZ `json:"created_at"` UpdatedAt gophercloud.JSONRFC3339NoZ `json:"updated_at"` } err := json.Unmarshal(b, &s) if err != nil { return err } *r = Member(s.tmp) r.CreatedAt = time.Time(s.CreatedAt) r.UpdatedAt = time.Time(s.UpdatedAt) return nil } // 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 } golang-github-gophercloud-gophercloud-0.12.0/openstack/loadbalancer/v2/pools/testing/000077500000000000000000000000001367513235700307155ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/loadbalancer/v2/pools/testing/doc.go000066400000000000000000000000441367513235700320070ustar00rootroot00000000000000// pools unit tests package testing golang-github-gophercloud-gophercloud-0.12.0/openstack/loadbalancer/v2/pools/testing/fixtures.go000066400000000000000000000357541367513235700331330ustar00rootroot00000000000000package testing import ( "fmt" "net/http" "testing" "time" "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, "provisioning_status": "ACTIVE", "created_at": "2018-08-23T20:05:21", "updated_at": "2018-08-23T21:22:53", "operating_status": "ONLINE", "backup": false, "monitor_address": "192.168.1.111", "monitor_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, "provisioning_status": "ACTIVE", "created_at": "2018-08-23T20:05:21", "updated_at": "2018-08-23T21:22:53", "operating_status": "ONLINE", "backup": false, "monitor_address": "192.168.1.111", "monitor_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, ProvisioningStatus: "ACTIVE", CreatedAt: time.Date(2018, 8, 23, 20, 05, 21, 0, time.UTC), UpdatedAt: time.Date(2018, 8, 23, 21, 22, 53, 0, time.UTC), OperatingStatus: "ONLINE", Backup: false, MonitorAddress: "192.168.1.111", MonitorPort: 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) }) } // HandleEmptyMembersUpdateSuccessfully sets up the test server to respond to an empty batch member Update request. func HandleEmptyMembersUpdateSuccessfully(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": [] }`) w.WriteHeader(http.StatusAccepted) }) } requests_test.go000066400000000000000000000220531367513235700341010ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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() name := "NewPoolName" actual, err := pools.Update(client, "c3741b06-df4d-4715-b142-276b6bce75ab", pools.UpdateOpts{ Name: &name, 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) weight := 10 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: &weight, }).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) weight := 4 client := fake.ServiceClient() name := "newMemberName" actual, err := pools.UpdateMember(client, "332abe93-f488-41ba-870b-2ac66be7f853", "2a280670-c202-4b0b-a562-34077415aabf", pools.UpdateMemberOpts{ Name: &name, Weight: &weight, }).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) name_1 := "web-server-1" weight_1 := 20 subnetID := "bbb35f84-35cc-4b2f-84c2-a6a29bba68aa" member1 := pools.BatchUpdateMemberOpts{ Address: "192.0.2.16", ProtocolPort: 80, Name: &name_1, SubnetID: &subnetID, Weight: &weight_1, } name_2 := "web-server-2" weight_2 := 10 member2 := pools.BatchUpdateMemberOpts{ Address: "192.0.2.17", ProtocolPort: 80, Name: &name_2, Weight: &weight_2, SubnetID: &subnetID, } members := []pools.BatchUpdateMemberOpts{member1, member2} res := pools.BatchUpdateMembers(fake.ServiceClient(), "332abe93-f488-41ba-870b-2ac66be7f853", members) th.AssertNoErr(t, res.Err) } func TestEmptyBatchUpdateMembers(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleEmptyMembersUpdateSuccessfully(t) res := pools.BatchUpdateMembers(fake.ServiceClient(), "332abe93-f488-41ba-870b-2ac66be7f853", []pools.BatchUpdateMemberOpts{}) th.AssertNoErr(t, res.Err) } func TestRequiredBatchUpdateMemberOpts(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() name := "web-server-1" res := pools.BatchUpdateMembers(fake.ServiceClient(), "332abe93-f488-41ba-870b-2ac66be7f853", []pools.BatchUpdateMemberOpts{ { Name: &name, }, }) 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: &name, }, }) 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: &name, }, }) if res.Err == nil { t.Fatalf("Expected error, but got none") } } golang-github-gophercloud-gophercloud-0.12.0/openstack/loadbalancer/v2/pools/urls.go000066400000000000000000000012441367513235700305550ustar00rootroot00000000000000package 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, memberID string) string { return c.ServiceURL(rootPath, resourcePath, poolID, memberPath, memberID) } golang-github-gophercloud-gophercloud-0.12.0/openstack/loadbalancer/v2/providers/000077500000000000000000000000001367513235700301215ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/loadbalancer/v2/providers/doc.go000066400000000000000000000006321367513235700312160ustar00rootroot00000000000000/* Package providers provides information about the supported providers at OpenStack Octavia Load Balancing service. Example to List Providers allPages, err := providers.List(lbClient).AllPages() if err != nil { panic(err) } allProviders, err := providers.ExtractProviders(allPages) if err != nil { panic(err) } for _, p := range allProviders { fmt.Printf("%+v\n", p) } */ package providers golang-github-gophercloud-gophercloud-0.12.0/openstack/loadbalancer/v2/providers/requests.go000066400000000000000000000025501367513235700323250ustar00rootroot00000000000000package providers import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // ListOptsBuilder allows extensions to add additional parameters to the // List request. type ListOptsBuilder interface { ToProviderListQuery() (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 Provider attributes you want to see returned. type ListOpts struct { Fields []string `q:"fields"` } // ToProviderListQuery formats a ListOpts into a query string. func (opts ListOpts) ToProviderListQuery() (string, error) { q, err := gophercloud.BuildQueryString(opts) return q.String(), err } // List returns a Pager which allows you to iterate over a collection of // providers. // // Default policy settings return only those providers 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.ToProviderListQuery() if err != nil { return pagination.Pager{Err: err} } url += query } return pagination.NewPager(c, url, func(r pagination.PageResult) pagination.Page { return ProviderPage{pagination.LinkedPageBase{PageResult: r}} }) } golang-github-gophercloud-gophercloud-0.12.0/openstack/loadbalancer/v2/providers/results.go000066400000000000000000000037311367513235700321550ustar00rootroot00000000000000package providers import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // Provider is the Octavia driver that implements the load balancing mechanism type Provider struct { // Human-readable description for the Loadbalancer. Description string `json:"description"` // Human-readable name for the Provider. Name string `json:"name"` } // ProviderPage is the page returned by a pager when traversing over a // collection of providers. type ProviderPage struct { pagination.LinkedPageBase } // NextPageURL is invoked when a paginated collection of providers 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 ProviderPage) NextPageURL() (string, error) { var s struct { Links []gophercloud.Link `json:"providers_links"` } err := r.ExtractInto(&s) if err != nil { return "", err } return gophercloud.ExtractNextURL(s.Links) } // IsEmpty checks whether a ProviderPage struct is empty. func (r ProviderPage) IsEmpty() (bool, error) { is, err := ExtractProviders(r) return len(is) == 0, err } // ExtractProviders accepts a Page struct, specifically a ProviderPage // struct, and extracts the elements into a slice of Provider structs. In // other words, a generic collection is mapped into a relevant slice. func ExtractProviders(r pagination.Page) ([]Provider, error) { var s struct { Providers []Provider `json:"providers"` } err := (r.(ProviderPage)).ExtractInto(&s) return s.Providers, err } type commonResult struct { gophercloud.Result } // Extract is a function that accepts a result and extracts a provider. func (r commonResult) Extract() (*Provider, error) { var s struct { Provider *Provider `json:"provider"` } err := r.ExtractInto(&s) return s.Provider, err } // GetResult represents the result of a get operation. Call its Extract // method to interpret it as a Provider. type GetResult struct { commonResult } golang-github-gophercloud-gophercloud-0.12.0/openstack/loadbalancer/v2/providers/testing/000077500000000000000000000000001367513235700315765ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/loadbalancer/v2/providers/testing/doc.go000066400000000000000000000000501367513235700326650ustar00rootroot00000000000000// providers unit tests package testing golang-github-gophercloud-gophercloud-0.12.0/openstack/loadbalancer/v2/providers/testing/fixtures.go000066400000000000000000000025001367513235700337730ustar00rootroot00000000000000package testing import ( "fmt" "net/http" "testing" "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/providers" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) // ProvidersListBody contains the canned body of a provider list response. const ProvidersListBody = ` { "providers":[ { "name": "amphora", "description": "The Octavia Amphora driver." }, { "name": "ovn", "description": "The Octavia OVN driver" } ] } ` var ( ProviderAmphora = providers.Provider{ Name: "amphora", Description: "The Octavia Amphora driver.", } ProviderOVN = providers.Provider{ Name: "ovn", Description: "The Octavia OVN driver", } ) // HandleProviderListSuccessfully sets up the test server to respond to a provider List request. func HandleProviderListSuccessfully(t *testing.T) { th.Mux.HandleFunc("/v2.0/lbaas/providers", 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, ProvidersListBody) default: t.Fatalf("/v2.0/lbaas/providers invoked with unexpected marker=[%s]", marker) } }) } requests_test.go000066400000000000000000000025171367513235700347650ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/loadbalancer/v2/providers/testingpackage testing import ( "testing" "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/providers" fake "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/testhelper" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" ) func TestListProviders(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleProviderListSuccessfully(t) pages := 0 err := providers.List(fake.ServiceClient(), providers.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { pages++ actual, err := providers.ExtractProviders(page) if err != nil { return false, err } if len(actual) != 2 { t.Fatalf("Expected 2 providers, got %d", len(actual)) } th.CheckDeepEquals(t, ProviderAmphora, actual[0]) th.CheckDeepEquals(t, ProviderOVN, actual[1]) return true, nil }) th.AssertNoErr(t, err) if pages != 1 { t.Errorf("Expected 1 page, saw %d", pages) } } func TestListAllProviders(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleProviderListSuccessfully(t) allPages, err := providers.List(fake.ServiceClient(), providers.ListOpts{}).AllPages() th.AssertNoErr(t, err) actual, err := providers.ExtractProviders(allPages) th.AssertNoErr(t, err) th.CheckDeepEquals(t, ProviderAmphora, actual[0]) th.CheckDeepEquals(t, ProviderOVN, actual[1]) } golang-github-gophercloud-gophercloud-0.12.0/openstack/loadbalancer/v2/providers/urls.go000066400000000000000000000003421367513235700314340ustar00rootroot00000000000000package providers import "github.com/gophercloud/gophercloud" const ( rootPath = "lbaas" resourcePath = "providers" ) func rootURL(c *gophercloud.ServiceClient) string { return c.ServiceURL(rootPath, resourcePath) } golang-github-gophercloud-gophercloud-0.12.0/openstack/loadbalancer/v2/testhelper/000077500000000000000000000000001367513235700302635ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/loadbalancer/v2/testhelper/client.go000066400000000000000000000004401367513235700320660ustar00rootroot00000000000000package 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.12.0/openstack/messaging/000077500000000000000000000000001367513235700251235ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/messaging/v2/000077500000000000000000000000001367513235700254525ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/messaging/v2/claims/000077500000000000000000000000001367513235700267225ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/messaging/v2/claims/doc.go000066400000000000000000000021001367513235700300070ustar00rootroot00000000000000/* 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 golang-github-gophercloud-gophercloud-0.12.0/openstack/messaging/v2/claims/requests.go000066400000000000000000000066421367513235700311340ustar00rootroot00000000000000package claims import ( "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 } resp, err := client.Post(url, b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{201, 204}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get queries the specified claim for the specified queue. func Get(client *gophercloud.ServiceClient, queueName string, claimID string) (r GetResult) { resp, err := client.Get(getURL(client, queueName, claimID), &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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 } resp, err := client.Patch(updateURL(client, queueName, claimID), &b, nil, &gophercloud.RequestOpts{ OkCodes: []int{204}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete will delete a Claim for a specified Queue. func Delete(client *gophercloud.ServiceClient, queueName string, claimID string) (r DeleteResult) { resp, err := client.Delete(deleteURL(client, queueName, claimID), &gophercloud.RequestOpts{ OkCodes: []int{204}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } golang-github-gophercloud-gophercloud-0.12.0/openstack/messaging/v2/claims/results.go000066400000000000000000000022441367513235700307540ustar00rootroot00000000000000package 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"` } golang-github-gophercloud-gophercloud-0.12.0/openstack/messaging/v2/claims/testing/000077500000000000000000000000001367513235700303775ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/messaging/v2/claims/testing/doc.go000066400000000000000000000000451367513235700314720ustar00rootroot00000000000000// Claims unit tests package testing golang-github-gophercloud-gophercloud-0.12.0/openstack/messaging/v2/claims/testing/fixtures.go000066400000000000000000000101401367513235700325730ustar00rootroot00000000000000package 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) }) } // HandleCreateNoContent configures the test server to respond to a Create request with no content. func HandleCreateNoContent(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.StatusNoContent) }) } // 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) }) } golang-github-gophercloud-gophercloud-0.12.0/openstack/messaging/v2/claims/testing/requests_test.go000066400000000000000000000032161367513235700336420ustar00rootroot00000000000000package 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 TestCreateNoContent(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleCreateNoContent(t) createOpts := claims.CreateOpts{ TTL: 3600, Grace: 3600, Limit: 10, } actual, err := claims.Create(fake.ServiceClient(), QueueName, createOpts).Extract() th.AssertNoErr(t, err) var expected []claims.Messages th.CheckDeepEquals(t, expected, 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.12.0/openstack/messaging/v2/claims/urls.go000066400000000000000000000014111367513235700302330ustar00rootroot00000000000000package 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.12.0/openstack/messaging/v2/messages/000077500000000000000000000000001367513235700272615ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/messaging/v2/messages/doc.go000066400000000000000000000045061367513235700303620ustar00rootroot00000000000000/* 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 golang-github-gophercloud-gophercloud-0.12.0/openstack/messaging/v2/messages/requests.go000066400000000000000000000174261367513235700314750ustar00rootroot00000000000000package 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 } resp, err := client.Post(createURL(client, queueName), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{201}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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 } resp, err := client.Delete(url, &gophercloud.RequestOpts{ OkCodes: []int{200, 204}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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 } resp, err := client.Delete(url, &gophercloud.RequestOpts{ JSONResponse: &r.Body, OkCodes: []int{200, 204}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, 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 } resp, err := client.Get(url, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get requests details on a single message, by ID. func Get(client *gophercloud.ServiceClient, queueName string, messageID string) (r GetResult) { resp, err := client.Get(messageURL(client, queueName, messageID), &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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 } resp, err := client.Delete(url, &gophercloud.RequestOpts{ OkCodes: []int{204}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } golang-github-gophercloud-gophercloud-0.12.0/openstack/messaging/v2/messages/results.go000066400000000000000000000066111367513235700313150ustar00rootroot00000000000000package 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) } golang-github-gophercloud-gophercloud-0.12.0/openstack/messaging/v2/messages/testing/000077500000000000000000000000001367513235700307365ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/messaging/v2/messages/testing/doc.go000066400000000000000000000000471367513235700320330ustar00rootroot00000000000000// messages unit tests package testing golang-github-gophercloud-gophercloud-0.12.0/openstack/messaging/v2/messages/testing/fixtures.go000066400000000000000000000217011367513235700331370ustar00rootroot00000000000000package 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.go000066400000000000000000000057251367513235700341310ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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) } golang-github-gophercloud-gophercloud-0.12.0/openstack/messaging/v2/messages/urls.go000066400000000000000000000025721367513235700306030ustar00rootroot00000000000000package 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.12.0/openstack/messaging/v2/queues/000077500000000000000000000000001367513235700267615ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/messaging/v2/queues/doc.go000066400000000000000000000044241367513235700300610ustar00rootroot00000000000000/* 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 golang-github-gophercloud-gophercloud-0.12.0/openstack/messaging/v2/queues/requests.go000066400000000000000000000214441367513235700311700ustar00rootroot00000000000000package 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") resp, err := client.Put(createURL(client, queueName), b, r.Body, &gophercloud.RequestOpts{ OkCodes: []int{201, 204}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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) { resp, 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"}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get requests details on a single queue, by name. func Get(client *gophercloud.ServiceClient, queueName string) (r GetResult) { resp, err := client.Get(getURL(client, queueName), &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete deletes the specified queue. func Delete(client *gophercloud.ServiceClient, queueName string) (r DeleteResult) { resp, err := client.Delete(deleteURL(client, queueName), &gophercloud.RequestOpts{ OkCodes: []int{204}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // GetStats returns statistics for the specified queue. func GetStats(client *gophercloud.ServiceClient, queueName string) (r StatResult) { resp, err := client.Get(statURL(client, queueName), &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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 } resp, err := client.Post(shareURL(client, queueName), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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 } resp, err := client.Post(purgeURL(client, queueName), b, nil, &gophercloud.RequestOpts{ OkCodes: []int{204}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } golang-github-gophercloud-gophercloud-0.12.0/openstack/messaging/v2/queues/results.go000066400000000000000000000117231367513235700310150ustar00rootroot00000000000000package 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 } golang-github-gophercloud-gophercloud-0.12.0/openstack/messaging/v2/queues/testing/000077500000000000000000000000001367513235700304365ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/messaging/v2/queues/testing/doc.go000066400000000000000000000000451367513235700315310ustar00rootroot00000000000000// queues unit tests package testing golang-github-gophercloud-gophercloud-0.12.0/openstack/messaging/v2/queues/testing/fixtures.go000066400000000000000000000216751367513235700326510ustar00rootroot00000000000000package 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) }) } golang-github-gophercloud-gophercloud-0.12.0/openstack/messaging/v2/queues/testing/requests_test.go000066400000000000000000000066141367513235700337060ustar00rootroot00000000000000package 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.12.0/openstack/messaging/v2/queues/urls.go000066400000000000000000000031231367513235700302740ustar00rootroot00000000000000package 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.12.0/openstack/networking/000077500000000000000000000000001367513235700253355ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/000077500000000000000000000000001367513235700256645ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/apiversions/000077500000000000000000000000001367513235700302265ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/apiversions/doc.go000066400000000000000000000010111367513235700313130ustar00rootroot00000000000000/* 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 golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/apiversions/requests.go000066400000000000000000000014721367513235700324340ustar00rootroot00000000000000package 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, listURL(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, getURL(c, v), func(r pagination.PageResult) pagination.Page { return APIVersionResourcePage{pagination.SinglePageBase(r)} }) } golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/apiversions/results.go000066400000000000000000000041351367513235700322610ustar00rootroot00000000000000package 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 } golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/apiversions/testing/000077500000000000000000000000001367513235700317035ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/apiversions/testing/doc.go000066400000000000000000000000521367513235700327740ustar00rootroot00000000000000// apiversions unit tests package testing requests_test.go000066400000000000000000000101561367513235700350700ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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 }) } golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/apiversions/urls.go000066400000000000000000000010451367513235700315420ustar00rootroot00000000000000package apiversions import ( "strings" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack/utils" ) func getURL(c *gophercloud.ServiceClient, version string) string { baseEndpoint, _ := utils.BaseEndpoint(c.Endpoint) endpoint := strings.TrimRight(baseEndpoint, "/") + "/" + strings.TrimRight(version, "/") + "/" return endpoint } func listURL(c *gophercloud.ServiceClient) string { baseEndpoint, _ := utils.BaseEndpoint(c.Endpoint) endpoint := strings.TrimRight(baseEndpoint, "/") + "/" return endpoint } golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/common/000077500000000000000000000000001367513235700271545ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/common/common_tests.go000066400000000000000000000004401367513235700322130ustar00rootroot00000000000000package 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.12.0/openstack/networking/v2/extensions/000077500000000000000000000000001367513235700300635ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/agents/000077500000000000000000000000001367513235700313445ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/agents/doc.go000066400000000000000000000037231367513235700324450ustar00rootroot00000000000000/* Package agents provides the ability to retrieve and manage Agents through the Neutron API. Example of Listing Agents listOpts := agents.ListOpts{ AgentType: "Open vSwitch agent", } allPages, err := agents.List(networkClient, listOpts).AllPages() if err != nil { panic(err) } allAgents, err := agents.ExtractAgents(allPages) if err != nil { panic(err) } for _, agent := range allAgents { fmt.Printf("%+v\n", agent) } Example to Get an Agent agentID := "76af7b1f-d61b-4526-94f7-d2e14e2698df" agent, err := agents.Get(networkClient, agentID).Extract() if err != nil { panic(err) } Example to Update an Agent adminStateUp := true description := "agent description" updateOpts := &agents.UpdateOpts{ Description: &description, AdminStateUp: &adminStateUp, } agentID := "76af7b1f-d61b-4526-94f7-d2e14e2698df" agent, err := agents.Update(networkClient, agentID, updateOpts).Extract() if err != nil { panic(err) } Example to Delete an Agent agentID := "76af7b1f-d61b-4526-94f7-d2e14e2698df" err := agents.Delete(networkClient, agentID).ExtractErr() if err != nil { panic(err) } Example to List Networks hosted by a DHCP Agent agentID := "76af7b1f-d61b-4526-94f7-d2e14e2698df" networks, err := agents.ListDHCPNetworks(networkClient, agentID).Extract() if err != nil { panic(err) } for _, network := range networks { fmt.Printf("%+v\n", network) } Example to Schedule a network to a DHCP Agent agentID := "76af7b1f-d61b-4526-94f7-d2e14e2698df" opts := &agents.ScheduleDHCPNetworkOpts{ NetworkID: "1ae075ca-708b-4e66-b4a7-b7698632f05f", } err := agents.ScheduleDHCPNetwork(networkClient, agentID, opts).ExtractErr() if err != nil { panic(err) } Example to Remove a network from a DHCP Agent agentID := "76af7b1f-d61b-4526-94f7-d2e14e2698df" networkID := "1ae075ca-708b-4e66-b4a7-b7698632f05f" err := agents.RemoveDHCPNetwork(networkClient, agentID, networkID).ExtractErr() if err != nil { panic(err) } */ package agents golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/agents/requests.go000066400000000000000000000123121367513235700335450ustar00rootroot00000000000000package agents import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // ListOptsBuilder allows extensions to add additional parameters to the // List request. type ListOptsBuilder interface { ToAgentListQuery() (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 agent attributes you want to see returned. // SortKey allows you to sort by a particular agent 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"` AgentType string `q:"agent_type"` Alive *bool `q:"alive"` AvailabilityZone string `q:"availability_zone"` Binary string `q:"binary"` Description string `q:"description"` Host string `q:"host"` Topic string `q:"topic"` Limit int `q:"limit"` Marker string `q:"marker"` SortKey string `q:"sort_key"` SortDir string `q:"sort_dir"` } // ToAgentListQuery formats a ListOpts into a query string. func (opts ListOpts) ToAgentListQuery() (string, error) { q, err := gophercloud.BuildQueryString(opts) return q.String(), err } // List returns a Pager which allows you to iterate over a collection of // agents. It accepts a ListOpts struct, which allows you to filter and // sort the returned collection for greater efficiency. // // Default policy settings return only the agents 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.ToAgentListQuery() if err != nil { return pagination.Pager{Err: err} } url += query } return pagination.NewPager(c, url, func(r pagination.PageResult) pagination.Page { return AgentPage{pagination.LinkedPageBase{PageResult: r}} }) } // Get retrieves a specific agent based on its ID. func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { resp, err := c.Get(getURL(c, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // UpdateOptsBuilder allows extensions to add additional parameters to the // Update request. type UpdateOptsBuilder interface { ToAgentUpdateMap() (map[string]interface{}, error) } // UpdateOpts represents the attributes used when updating an existing agent. type UpdateOpts struct { Description *string `json:"description,omitempty"` AdminStateUp *bool `json:"admin_state_up,omitempty"` } // ToAgentUpdateMap builds a request body from UpdateOpts. func (opts UpdateOpts) ToAgentUpdateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "agent") } // Update updates a specific agent based on its ID. func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToAgentUpdateMap() if err != nil { r.Err = err return } resp, err := c.Put(updateURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete deletes a specific agent based on its ID. func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { resp, err := c.Delete(getURL(c, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // ListDHCPNetworks returns a list of networks scheduled to a specific // dhcp agent. func ListDHCPNetworks(c *gophercloud.ServiceClient, id string) (r ListDHCPNetworksResult) { resp, err := c.Get(listDHCPNetworksURL(c, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // ScheduleDHCPNetworkOptsBuilder allows extensions to add additional parameters // to the ScheduleDHCPNetwork request. type ScheduleDHCPNetworkOptsBuilder interface { ToAgentScheduleDHCPNetworkMap() (map[string]interface{}, error) } // ScheduleDHCPNetworkOpts represents the attributes used when scheduling a // network to a DHCP agent. type ScheduleDHCPNetworkOpts struct { NetworkID string `json:"network_id" required:"true"` } // ToAgentScheduleDHCPNetworkMap builds a request body from ScheduleDHCPNetworkOpts. func (opts ScheduleDHCPNetworkOpts) ToAgentScheduleDHCPNetworkMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "") } // ScheduleDHCPNetwork schedule a network to a DHCP agent. func ScheduleDHCPNetwork(c *gophercloud.ServiceClient, id string, opts ScheduleDHCPNetworkOptsBuilder) (r ScheduleDHCPNetworkResult) { b, err := opts.ToAgentScheduleDHCPNetworkMap() if err != nil { r.Err = err return } resp, err := c.Post(scheduleDHCPNetworkURL(c, id), b, nil, &gophercloud.RequestOpts{ OkCodes: []int{201}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // RemoveDHCPNetwork removes a network from a DHCP agent. func RemoveDHCPNetwork(c *gophercloud.ServiceClient, id string, networkID string) (r RemoveDHCPNetworkResult) { resp, err := c.Delete(removeDHCPNetworkURL(c, id, networkID), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/agents/results.go000066400000000000000000000115011367513235700333720ustar00rootroot00000000000000package agents import ( "encoding/json" "time" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack/networking/v2/networks" "github.com/gophercloud/gophercloud/pagination" ) type commonResult struct { gophercloud.Result } // Extract is a function that accepts a result and extracts an agent resource. func (r commonResult) Extract() (*Agent, error) { var s struct { Agent *Agent `json:"agent"` } err := r.ExtractInto(&s) return s.Agent, err } // GetResult represents the result of a get operation. Call its Extract // method to interpret it as an Agent. type GetResult struct { commonResult } // UpdateResult represents the result of a get operation. Call its Extract // method to interpret it as an Agent. 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 } // ScheduleDHCPNetworkResult represents the result of a schedule a network to // a DHCP agent operation. ExtractErr method to determine if the request // succeeded or failed. type ScheduleDHCPNetworkResult struct { gophercloud.ErrResult } // RemoveDHCPNetworkResult represents the result of a remove a network from a // DHCP agent operation. ExtractErr method to determine if the request succeeded // or failed. type RemoveDHCPNetworkResult struct { gophercloud.ErrResult } // Agent represents a Neutron agent. type Agent struct { // ID is the id of the agent. ID string `json:"id"` // AdminStateUp is an administrative state of the agent. AdminStateUp bool `json:"admin_state_up"` // AgentType is a type of the agent. AgentType string `json:"agent_type"` // Alive indicates whether agent is alive or not. Alive bool `json:"alive"` // ResourcesSynced indicates whether agent is synced or not. // Not all agent types track resources via Placement. ResourcesSynced bool `json:"resources_synced"` // AvailabilityZone is a zone of the agent. AvailabilityZone string `json:"availability_zone"` // Binary is an executable binary of the agent. Binary string `json:"binary"` // Configurations is a configuration specific key/value pairs that are // determined by the agent binary and type. Configurations map[string]interface{} `json:"configurations"` // CreatedAt is a creation timestamp. CreatedAt time.Time `json:"-"` // StartedAt is a starting timestamp. StartedAt time.Time `json:"-"` // HeartbeatTimestamp is a last heartbeat timestamp. HeartbeatTimestamp time.Time `json:"-"` // Description contains agent description. Description string `json:"description"` // Host is a hostname of the agent system. Host string `json:"host"` // Topic contains name of AMQP topic. Topic string `json:"topic"` } // UnmarshalJSON helps to convert the timestamps into the time.Time type. func (r *Agent) UnmarshalJSON(b []byte) error { type tmp Agent var s struct { tmp CreatedAt gophercloud.JSONRFC3339ZNoTNoZ `json:"created_at"` StartedAt gophercloud.JSONRFC3339ZNoTNoZ `json:"started_at"` HeartbeatTimestamp gophercloud.JSONRFC3339ZNoTNoZ `json:"heartbeat_timestamp"` } err := json.Unmarshal(b, &s) if err != nil { return err } *r = Agent(s.tmp) r.CreatedAt = time.Time(s.CreatedAt) r.StartedAt = time.Time(s.StartedAt) r.HeartbeatTimestamp = time.Time(s.HeartbeatTimestamp) return nil } // AgentPage stores a single page of Agents from a List() API call. type AgentPage struct { pagination.LinkedPageBase } // NextPageURL is invoked when a paginated collection of agent 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 AgentPage) NextPageURL() (string, error) { var s struct { Links []gophercloud.Link `json:"agents_links"` } err := r.ExtractInto(&s) if err != nil { return "", err } return gophercloud.ExtractNextURL(s.Links) } // IsEmpty determines whether or not a AgentPage is empty. func (r AgentPage) IsEmpty() (bool, error) { agents, err := ExtractAgents(r) return len(agents) == 0, err } // ExtractAgents interprets the results of a single page from a List() // API call, producing a slice of Agents structs. func ExtractAgents(r pagination.Page) ([]Agent, error) { var s struct { Agents []Agent `json:"agents"` } err := (r.(AgentPage)).ExtractInto(&s) return s.Agents, err } // ListDHCPNetworksResult is the response from a List operation. // Call its Extract method to interpret it as networks. type ListDHCPNetworksResult struct { gophercloud.Result } // Extract interprets any ListDHCPNetworksResult as an array of networks. func (r ListDHCPNetworksResult) Extract() ([]networks.Network, error) { var s struct { Networks []networks.Network `json:"networks"` } err := r.ExtractInto(&s) return s.Networks, err } golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/agents/testing/000077500000000000000000000000001367513235700330215ustar00rootroot00000000000000doc.go000066400000000000000000000000451367513235700340350ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/agents/testing// agents unit tests package testing fixtures.go000066400000000000000000000163071367513235700351510ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/agents/testingpackage testing import ( "time" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/agents" ) // AgentsListResult represents raw response for the List request. const AgentsListResult = ` { "agents": [ { "admin_state_up": true, "agent_type": "Open vSwitch agent", "alive": true, "availability_zone": null, "binary": "neutron-openvswitch-agent", "configurations": { "datapath_type": "system", "extensions": [ "qos" ] }, "created_at": "2017-07-26 23:15:44", "description": null, "heartbeat_timestamp": "2019-01-09 10:28:53", "host": "compute1", "id": "59186d7b-b512-4fdf-bbaf-5804ffde8811", "started_at": "2018-06-26 21:46:19", "topic": "N/A" }, { "admin_state_up": true, "agent_type": "Open vSwitch agent", "alive": true, "availability_zone": null, "binary": "neutron-openvswitch-agent", "configurations": { "datapath_type": "system", "extensions": [ "qos" ] }, "created_at": "2017-01-22 14:00:50", "description": null, "heartbeat_timestamp": "2019-01-09 10:28:50", "host": "compute2", "id": "76af7b1f-d61b-4526-94f7-d2e14e2698df", "started_at": "2018-11-06 12:09:17", "topic": "N/A" } ] } ` // AgentUpdateRequest represents raw request to update an Agent. const AgentUpdateRequest = ` { "agent": { "description": "My OVS agent for OpenStack", "admin_state_up": true } } ` // Agent represents a sample Agent struct. var Agent = agents.Agent{ ID: "43583cf5-472e-4dc8-af5b-6aed4c94ee3a", AdminStateUp: true, AgentType: "Open vSwitch agent", Description: "My OVS agent for OpenStack", Alive: true, ResourcesSynced: true, Binary: "neutron-openvswitch-agent", Configurations: map[string]interface{}{ "ovs_hybrid_plug": false, "datapath_type": "system", "vhostuser_socket_dir": "/var/run/openvswitch", "log_agent_heartbeats": false, "l2_population": true, "enable_distributed_routing": false, }, CreatedAt: time.Date(2017, 7, 26, 23, 2, 5, 0, time.UTC), StartedAt: time.Date(2018, 6, 26, 21, 46, 20, 0, time.UTC), HeartbeatTimestamp: time.Date(2019, 1, 9, 11, 43, 01, 0, time.UTC), Host: "compute3", Topic: "N/A", } // Agent1 represents first unmarshalled address scope from the // AgentsListResult. var Agent1 = agents.Agent{ ID: "59186d7b-b512-4fdf-bbaf-5804ffde8811", AdminStateUp: true, AgentType: "Open vSwitch agent", Alive: true, Binary: "neutron-openvswitch-agent", Configurations: map[string]interface{}{ "datapath_type": "system", "extensions": []interface{}{ "qos", }, }, CreatedAt: time.Date(2017, 7, 26, 23, 15, 44, 0, time.UTC), StartedAt: time.Date(2018, 6, 26, 21, 46, 19, 0, time.UTC), HeartbeatTimestamp: time.Date(2019, 1, 9, 10, 28, 53, 0, time.UTC), Host: "compute1", Topic: "N/A", } // Agent2 represents second unmarshalled address scope from the // AgentsListResult. var Agent2 = agents.Agent{ ID: "76af7b1f-d61b-4526-94f7-d2e14e2698df", AdminStateUp: true, AgentType: "Open vSwitch agent", Alive: true, Binary: "neutron-openvswitch-agent", Configurations: map[string]interface{}{ "datapath_type": "system", "extensions": []interface{}{ "qos", }, }, CreatedAt: time.Date(2017, 1, 22, 14, 00, 50, 0, time.UTC), StartedAt: time.Date(2018, 11, 6, 12, 9, 17, 0, time.UTC), HeartbeatTimestamp: time.Date(2019, 1, 9, 10, 28, 50, 0, time.UTC), Host: "compute2", Topic: "N/A", } // AgentsGetResult represents raw response for the Get request. const AgentsGetResult = ` { "agent": { "binary": "neutron-openvswitch-agent", "description": null, "availability_zone": null, "heartbeat_timestamp": "2019-01-09 11:43:01", "admin_state_up": true, "alive": true, "id": "43583cf5-472e-4dc8-af5b-6aed4c94ee3a", "topic": "N/A", "host": "compute3", "agent_type": "Open vSwitch agent", "started_at": "2018-06-26 21:46:20", "created_at": "2017-07-26 23:02:05", "configurations": { "ovs_hybrid_plug": false, "datapath_type": "system", "vhostuser_socket_dir": "/var/run/openvswitch", "log_agent_heartbeats": false, "l2_population": true, "enable_distributed_routing": false } } } ` // AgentsUpdateResult represents raw response for the Update request. const AgentsUpdateResult = ` { "agent": { "binary": "neutron-openvswitch-agent", "description": "My OVS agent for OpenStack", "availability_zone": null, "heartbeat_timestamp": "2019-01-09 11:43:01", "admin_state_up": true, "alive": true, "id": "43583cf5-472e-4dc8-af5b-6aed4c94ee3a", "topic": "N/A", "host": "compute3", "agent_type": "Open vSwitch agent", "started_at": "2018-06-26 21:46:20", "created_at": "2017-07-26 23:02:05", "resources_synced": true, "configurations": { "ovs_hybrid_plug": false, "datapath_type": "system", "vhostuser_socket_dir": "/var/run/openvswitch", "log_agent_heartbeats": false, "l2_population": true, "enable_distributed_routing": false } } } ` // AgentDHCPNetworksListResult represents raw response for the ListDHCPNetworks request. const AgentDHCPNetworksListResult = ` { "networks": [ { "admin_state_up": true, "availability_zone_hints": [], "availability_zones": [ "nova" ], "created_at": "2016-03-08T20:19:41", "dns_domain": "my-domain.org.", "id": "d32019d3-bc6e-4319-9c1d-6722fc136a22", "ipv4_address_scope": null, "ipv6_address_scope": null, "l2_adjacency": false, "mtu": 1500, "name": "net1", "port_security_enabled": true, "project_id": "4fd44f30292945e481c7b8a0c8908869", "qos_policy_id": "6a8454ade84346f59e8d40665f878b2e", "revision_number": 1, "router:external": false, "shared": false, "status": "ACTIVE", "subnets": [ "54d6f61d-db07-451c-9ab3-b9609b6b6f0b" ], "tenant_id": "4fd44f30292945e481c7b8a0c8908869", "updated_at": "2016-03-08T20:19:41", "vlan_transparent": true, "description": "", "is_default": false } ] } ` // ScheduleDHCPNetworkRequest represents raw request for the ScheduleDHCPNetwork request. const ScheduleDHCPNetworkRequest = ` { "network_id": "1ae075ca-708b-4e66-b4a7-b7698632f05f" } ` requests_test.go000066400000000000000000000150141367513235700362040ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/agents/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/agents" "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/agents", 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, AgentsListResult) }) count := 0 agents.List(fake.ServiceClient(), agents.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { count++ actual, err := agents.ExtractAgents(page) if err != nil { t.Errorf("Failed to extract agents: %v", err) return false, nil } expected := []agents.Agent{ Agent1, Agent2, } 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/agents/43583cf5-472e-4dc8-af5b-6aed4c94ee3a", 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, AgentsGetResult) }) s, err := agents.Get(fake.ServiceClient(), "43583cf5-472e-4dc8-af5b-6aed4c94ee3a").Extract() th.AssertNoErr(t, err) th.AssertEquals(t, s.ID, "43583cf5-472e-4dc8-af5b-6aed4c94ee3a") th.AssertEquals(t, s.Binary, "neutron-openvswitch-agent") th.AssertEquals(t, s.AdminStateUp, true) th.AssertEquals(t, s.Alive, true) th.AssertEquals(t, s.Topic, "N/A") th.AssertEquals(t, s.Host, "compute3") th.AssertEquals(t, s.AgentType, "Open vSwitch agent") th.AssertEquals(t, s.HeartbeatTimestamp, time.Date(2019, 1, 9, 11, 43, 01, 0, time.UTC)) th.AssertEquals(t, s.StartedAt, time.Date(2018, 6, 26, 21, 46, 20, 0, time.UTC)) th.AssertEquals(t, s.CreatedAt, time.Date(2017, 7, 26, 23, 2, 5, 0, time.UTC)) th.AssertDeepEquals(t, s.Configurations, map[string]interface{}{ "ovs_hybrid_plug": false, "datapath_type": "system", "vhostuser_socket_dir": "/var/run/openvswitch", "log_agent_heartbeats": false, "l2_population": true, "enable_distributed_routing": false, }) } func TestUpdate(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/agents/43583cf5-472e-4dc8-af5b-6aed4c94ee3a", 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, AgentUpdateRequest) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, AgentsUpdateResult) }) iTrue := true description := "My OVS agent for OpenStack" updateOpts := &agents.UpdateOpts{ Description: &description, AdminStateUp: &iTrue, } s, err := agents.Update(fake.ServiceClient(), "43583cf5-472e-4dc8-af5b-6aed4c94ee3a", updateOpts).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, *s, Agent) } func TestDelete(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/agents/43583cf5-472e-4dc8-af5b-6aed4c94ee3a", 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().Add("Content-Type", "application/json") w.WriteHeader(http.StatusNoContent) }) err := agents.Delete(fake.ServiceClient(), "43583cf5-472e-4dc8-af5b-6aed4c94ee3a").ExtractErr() th.AssertNoErr(t, err) } func TestListDHCPNetworks(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/agents/43583cf5-472e-4dc8-af5b-6aed4c94ee3a/dhcp-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, AgentDHCPNetworksListResult) }) s, err := agents.ListDHCPNetworks(fake.ServiceClient(), "43583cf5-472e-4dc8-af5b-6aed4c94ee3a").Extract() th.AssertNoErr(t, err) var nilSlice []string th.AssertEquals(t, len(s), 1) th.AssertEquals(t, s[0].ID, "d32019d3-bc6e-4319-9c1d-6722fc136a22") th.AssertEquals(t, s[0].AdminStateUp, true) th.AssertEquals(t, s[0].ProjectID, "4fd44f30292945e481c7b8a0c8908869") th.AssertEquals(t, s[0].Shared, false) th.AssertEquals(t, s[0].Name, "net1") th.AssertEquals(t, s[0].Status, "ACTIVE") th.AssertDeepEquals(t, s[0].Tags, nilSlice) th.AssertEquals(t, s[0].TenantID, "4fd44f30292945e481c7b8a0c8908869") th.AssertDeepEquals(t, s[0].AvailabilityZoneHints, []string{}) th.AssertDeepEquals(t, s[0].Subnets, []string{"54d6f61d-db07-451c-9ab3-b9609b6b6f0b"}) } func TestScheduleDHCPNetwork(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/agents/43583cf5-472e-4dc8-af5b-6aed4c94ee3a/dhcp-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, ScheduleDHCPNetworkRequest) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) }) opts := &agents.ScheduleDHCPNetworkOpts{ NetworkID: "1ae075ca-708b-4e66-b4a7-b7698632f05f", } err := agents.ScheduleDHCPNetwork(fake.ServiceClient(), "43583cf5-472e-4dc8-af5b-6aed4c94ee3a", opts).ExtractErr() th.AssertNoErr(t, err) } func TestRemoveDHCPNetwork(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/agents/43583cf5-472e-4dc8-af5b-6aed4c94ee3a/dhcp-networks/1ae075ca-708b-4e66-b4a7-b7698632f05f", 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().Add("Content-Type", "application/json") w.WriteHeader(http.StatusNoContent) }) err := agents.RemoveDHCPNetwork(fake.ServiceClient(), "43583cf5-472e-4dc8-af5b-6aed4c94ee3a", "1ae075ca-708b-4e66-b4a7-b7698632f05f").ExtractErr() th.AssertNoErr(t, err) } golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/agents/urls.go000066400000000000000000000023141367513235700326600ustar00rootroot00000000000000package agents import "github.com/gophercloud/gophercloud" const resourcePath = "agents" const dhcpNetworksResourcePath = "dhcp-networks" 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 updateURL(c *gophercloud.ServiceClient, id string) string { return resourceURL(c, id) } func deleteURL(c *gophercloud.ServiceClient, id string) string { return resourceURL(c, id) } func dhcpNetworksURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL(resourcePath, id, dhcpNetworksResourcePath) } func listDHCPNetworksURL(c *gophercloud.ServiceClient, id string) string { return dhcpNetworksURL(c, id) } func scheduleDHCPNetworkURL(c *gophercloud.ServiceClient, id string) string { return dhcpNetworksURL(c, id) } func removeDHCPNetworkURL(c *gophercloud.ServiceClient, id string, networkID string) string { return c.ServiceURL(resourcePath, id, dhcpNetworksResourcePath, networkID) } golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/attributestags/000077500000000000000000000000001367513235700331305ustar00rootroot00000000000000doc.go000066400000000000000000000023241367513235700341460ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/attributestags/* Package attributestags manages Tags on Resources created by the OpenStack Neutron Service. This enables tagging via a standard interface for resources types which support it. See https://developer.openstack.org/api-ref/network/v2/#standard-attributes-tag-extension for more information on the underlying API. Example to ReplaceAll Resource Tags network, err := networks.Create(conn, createOpts).Extract() tagReplaceAllOpts := attributestags.ReplaceAllOpts{ Tags: []string{"abc", "123"}, } attributestags.ReplaceAll(conn, "networks", network.ID, tagReplaceAllOpts) Example to List all Resource Tags tags, err = attributestags.List(conn, "networks", network.ID).Extract() Example to Delete all Resource Tags err = attributestags.DeleteAll(conn, "networks", network.ID).ExtractErr() Example to Add a tag to a Resource err = attributestags.Add(client, "networks", network.ID, "atag").ExtractErr() Example to Delete a tag from a Resource err = attributestags.Delete(client, "networks", network.ID, "atag").ExtractErr() Example to confirm if a tag exists on a resource exists, _ := attributestags.Confirm(client, "networks", network.ID, "atag").Extract() */ package attributestags requests.go000066400000000000000000000056621367513235700352640ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/attributestagspackage attributestags import ( "github.com/gophercloud/gophercloud" ) // ReplaceAllOptsBuilder allows extensions to add additional parameters to // the ReplaceAll request. type ReplaceAllOptsBuilder interface { ToAttributeTagsReplaceAllMap() (map[string]interface{}, error) } // ReplaceAllOpts provides options used to create Tags on a Resource type ReplaceAllOpts struct { Tags []string `json:"tags" required:"true"` } // ToAttributeTagsReplaceAllMap formats a ReplaceAllOpts into the body of the // replace request func (opts ReplaceAllOpts) ToAttributeTagsReplaceAllMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "") } // ReplaceAll updates all tags on a resource, replacing any existing tags func ReplaceAll(client *gophercloud.ServiceClient, resourceType string, resourceID string, opts ReplaceAllOptsBuilder) (r ReplaceAllResult) { b, err := opts.ToAttributeTagsReplaceAllMap() url := replaceURL(client, resourceType, resourceID) if err != nil { r.Err = err return } resp, err := client.Put(url, &b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // List all tags on a resource func List(client *gophercloud.ServiceClient, resourceType string, resourceID string) (r ListResult) { url := listURL(client, resourceType, resourceID) resp, err := client.Get(url, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // DeleteAll deletes all tags on a resource func DeleteAll(client *gophercloud.ServiceClient, resourceType string, resourceID string) (r DeleteResult) { url := deleteAllURL(client, resourceType, resourceID) resp, err := client.Delete(url, &gophercloud.RequestOpts{ OkCodes: []int{204}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Add a tag on a resource func Add(client *gophercloud.ServiceClient, resourceType string, resourceID string, tag string) (r AddResult) { url := addURL(client, resourceType, resourceID, tag) resp, err := client.Put(url, nil, nil, &gophercloud.RequestOpts{ OkCodes: []int{201}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete a tag on a resource func Delete(client *gophercloud.ServiceClient, resourceType string, resourceID string, tag string) (r DeleteResult) { url := deleteURL(client, resourceType, resourceID, tag) resp, err := client.Delete(url, &gophercloud.RequestOpts{ OkCodes: []int{204}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Confirm if a tag exists on a resource func Confirm(client *gophercloud.ServiceClient, resourceType string, resourceID string, tag string) (r ConfirmResult) { url := confirmURL(client, resourceType, resourceID, tag) resp, err := client.Get(url, nil, &gophercloud.RequestOpts{ OkCodes: []int{204}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } results.go000066400000000000000000000023001367513235700350740ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/attributestagspackage attributestags import ( "github.com/gophercloud/gophercloud" ) type tagResult struct { gophercloud.Result } // Extract interprets tagResult to return the list of tags func (r tagResult) Extract() ([]string, error) { var s struct { Tags []string `json:"tags"` } err := r.ExtractInto(&s) return s.Tags, err } // ReplaceAllResult represents the result of a replace operation. // Call its Extract method to interpret it as a slice of strings. type ReplaceAllResult struct { tagResult } type ListResult struct { tagResult } // DeleteResult is the result from a Delete/DeleteAll operation. // Call its ExtractErr method to determine if the call succeeded or failed. type DeleteResult struct { gophercloud.ErrResult } // AddResult is the result from an Add operation. // Call its ExtractErr method to determine if the call succeeded or failed. type AddResult struct { gophercloud.ErrResult } // ConfirmResult is the result from an Confirm operation. type ConfirmResult struct { gophercloud.Result } func (r ConfirmResult) Extract() (bool, error) { exists := r.Err == nil if r.Err != nil { if _, ok := r.Err.(gophercloud.ErrDefault404); ok { r.Err = nil } } return exists, r.Err } testing/000077500000000000000000000000001367513235700345265ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/attributestagsfixtures.go000066400000000000000000000003431367513235700367260ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/attributestags/testingpackage testing const attributestagsReplaceAllRequest = ` { "tags": ["abc", "xyz"] } ` const attributestagsReplaceAllResult = ` { "tags": ["abc", "xyz"] } ` const attributestagsListResult = ` { "tags": ["abc", "xyz"] } ` requests_test.go000066400000000000000000000077531367513235700400030ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/attributestags/testingpackage testing import ( "fmt" "net/http" "testing" fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/attributestags" th "github.com/gophercloud/gophercloud/testhelper" ) func TestReplaceAll(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/networks/fakeid/tags", 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, attributestagsReplaceAllRequest) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, attributestagsReplaceAllResult) }) opts := attributestags.ReplaceAllOpts{ Tags: []string{"abc", "xyz"}, } res, err := attributestags.ReplaceAll(fake.ServiceClient(), "networks", "fakeid", opts).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, res, []string{"abc", "xyz"}) } func TestList(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/networks/fakeid/tags", 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, attributestagsListResult) }) res, err := attributestags.List(fake.ServiceClient(), "networks", "fakeid").Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, res, []string{"abc", "xyz"}) } func TestDeleteAll(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/networks/fakeid/tags", 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) }) err := attributestags.DeleteAll(fake.ServiceClient(), "networks", "fakeid").ExtractErr() th.AssertNoErr(t, err) } func TestAdd(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/networks/fakeid/tags/atag", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "PUT") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) }) err := attributestags.Add(fake.ServiceClient(), "networks", "fakeid", "atag").ExtractErr() th.AssertNoErr(t, err) } func TestDelete(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/networks/fakeid/tags/atag", 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) }) err := attributestags.Delete(fake.ServiceClient(), "networks", "fakeid", "atag").ExtractErr() th.AssertNoErr(t, err) } func TestConfirmTrue(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/networks/fakeid/tags/atag", 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.StatusNoContent) }) exists, err := attributestags.Confirm(fake.ServiceClient(), "networks", "fakeid", "atag").Extract() th.AssertNoErr(t, err) th.AssertEquals(t, true, exists) } func TestConfirmFalse(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/networks/fakeid/tags/atag", 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.StatusNotFound) }) exists, _ := attributestags.Confirm(fake.ServiceClient(), "networks", "fakeid", "atag").Extract() th.AssertEquals(t, false, exists) } urls.go000066400000000000000000000016111367513235700343640ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/attributestagspackage attributestags import "github.com/gophercloud/gophercloud" const ( tagsPath = "tags" ) func replaceURL(c *gophercloud.ServiceClient, r_type string, id string) string { return c.ServiceURL(r_type, id, tagsPath) } func listURL(c *gophercloud.ServiceClient, r_type string, id string) string { return c.ServiceURL(r_type, id, tagsPath) } func deleteAllURL(c *gophercloud.ServiceClient, r_type string, id string) string { return c.ServiceURL(r_type, id, tagsPath) } func addURL(c *gophercloud.ServiceClient, r_type string, id string, tag string) string { return c.ServiceURL(r_type, id, tagsPath, tag) } func deleteURL(c *gophercloud.ServiceClient, r_type string, id string, tag string) string { return c.ServiceURL(r_type, id, tagsPath, tag) } func confirmURL(c *gophercloud.ServiceClient, r_type string, id string, tag string) string { return c.ServiceURL(r_type, id, tagsPath, tag) } golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/delegate.go000066400000000000000000000021321367513235700321620ustar00rootroot00000000000000package 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) } golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/dns/000077500000000000000000000000001367513235700306475ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/dns/requests.go000066400000000000000000000112231367513235700330500ustar00rootroot00000000000000package dns import ( "net/url" "github.com/gophercloud/gophercloud" "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/ports" ) // PortListOptsExt adds the DNS options to the base port ListOpts. type PortListOptsExt struct { ports.ListOptsBuilder DNSName string `q:"dns_name"` } // ToPortListQuery adds the DNS options to the base port list options. func (opts PortListOptsExt) ToPortListQuery() (string, error) { q, err := gophercloud.BuildQueryString(opts.ListOptsBuilder) if err != nil { return "", err } params := q.Query() if opts.DNSName != "" { params.Add("dns_name", opts.DNSName) } q = &url.URL{RawQuery: params.Encode()} return q.String(), err } // PortCreateOptsExt adds port DNS options to the base ports.CreateOpts. type PortCreateOptsExt 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 // Set DNS name to the port DNSName string `json:"dns_name,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.DNSName != "" { port["dns_name"] = opts.DNSName } return base, nil } // PortUpdateOptsExt adds DNS options to the base ports.UpdateOpts type PortUpdateOptsExt 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 // Set DNS name to the port DNSName *string `json:"dns_name,omitempty"` } // ToPortUpdateMap casts an 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.DNSName != nil { port["dns_name"] = *opts.DNSName } return base, nil } // FloatingIPCreateOptsExt adds floating IP DNS options to the base floatingips.CreateOpts. type FloatingIPCreateOptsExt struct { // CreateOptsBuilder is the interface options structs have to satisfy in order // to be used in the main Create operation in this package. floatingips.CreateOptsBuilder // Set DNS name to the floating IPs DNSName string `json:"dns_name,omitempty"` // Set DNS domain to the floating IPs DNSDomain string `json:"dns_domain,omitempty"` } // ToFloatingIPCreateMap casts a CreateOpts struct to a map. func (opts FloatingIPCreateOptsExt) ToFloatingIPCreateMap() (map[string]interface{}, error) { base, err := opts.CreateOptsBuilder.ToFloatingIPCreateMap() if err != nil { return nil, err } floatingip := base["floatingip"].(map[string]interface{}) if opts.DNSName != "" { floatingip["dns_name"] = opts.DNSName } if opts.DNSDomain != "" { floatingip["dns_domain"] = opts.DNSDomain } return base, nil } // NetworkCreateOptsExt adds network DNS options to the base networks.CreateOpts. type NetworkCreateOptsExt struct { // CreateOptsBuilder is the interface options structs have to satisfy in order // to be used in the main Create operation in this package. networks.CreateOptsBuilder // Set DNS domain to the network DNSDomain string `json:"dns_domain,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.DNSDomain != "" { network["dns_domain"] = opts.DNSDomain } return base, nil } // NetworkUpdateOptsExt adds network DNS options to the base networks.UpdateOpts type NetworkUpdateOptsExt struct { // UpdateOptsBuilder is the interface options structs have to satisfy in order // to be used in the main Update operation in this package. networks.UpdateOptsBuilder // Set DNS domain to the network DNSDomain *string `json:"dns_domain,omitempty"` } // ToNetworkUpdateMap casts an 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.DNSDomain != nil { network["dns_domain"] = *opts.DNSDomain } return base, nil } golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/dns/results.go000066400000000000000000000015731367513235700327050ustar00rootroot00000000000000package dns // PortDNSExt represents a decorated form of a Port with the additional // Port DNS information. type PortDNSExt struct { // The DNS name of the port. DNSName string `json:"dns_name"` // The DNS assignment of the port. DNSAssignment []map[string]string `json:"dns_assignment"` } // FloatingIPDNSExt represents a decorated form of a Floating IP with the // additional Floating IP DNS information. type FloatingIPDNSExt struct { // The DNS name of the floating IP, assigned to the external DNS // service. DNSName string `json:"dns_name"` // The DNS domain of the floating IP, assigned to the external DNS // service. DNSDomain string `json:"dns_domain"` } // NetworkDNSExt represents a decorated form of a Network with the additional // Network DNS information. type NetworkDNSExt struct { // The DNS domain of the network. DNSDomain string `json:"dns_domain"` } golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/dns/testing/000077500000000000000000000000001367513235700323245ustar00rootroot00000000000000fixtures.go000066400000000000000000000226501367513235700344520ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/dns/testingpackage testing import ( "fmt" "net/http" "testing" fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common" floatingiptest "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/layer3/floatingips/testing" networktest "github.com/gophercloud/gophercloud/openstack/networking/v2/networks/testing" porttest "github.com/gophercloud/gophercloud/openstack/networking/v2/ports/testing" th "github.com/gophercloud/gophercloud/testhelper" ) const NetworkCreateRequest = ` { "network": { "name": "private", "admin_state_up": true, "dns_domain": "local." } }` const NetworkCreateResponse = ` { "network": { "status": "ACTIVE", "subnets": ["08eae331-0402-425a-923c-34f7cfe39c1b"], "name": "private", "admin_state_up": true, "tenant_id": "26a7980765d0414dbc1fc1f88cdb7e6e", "created_at": "2019-06-30T04:15:37Z", "updated_at": "2019-06-30T05:18:49Z", "shared": false, "id": "db193ab3-96e3-4cb3-8fc5-05f4296d0324", "provider:segmentation_id": 9876543210, "provider:physical_network": null, "provider:network_type": "local.", "dns_domain": "local." } }` const NetworkUpdateRequest = ` { "network": { "name": "new_network_name", "admin_state_up": false, "dns_domain": "" } }` const NetworkUpdateResponse = ` { "network": { "status": "ACTIVE", "subnets": ["08eae331-0402-425a-923c-34f7cfe39c1b"], "name": "new_network_name", "admin_state_up": false, "tenant_id": "26a7980765d0414dbc1fc1f88cdb7e6e", "created_at": "2019-06-30T04:15:37Z", "updated_at": "2019-06-30T05:18:49Z", "shared": false, "id": "db193ab3-96e3-4cb3-8fc5-05f4296d0324", "provider:segmentation_id": 9876543210, "provider:physical_network": null, "provider:network_type": "local.", "dns_domain": "" } }` func PortHandleListSuccessfully(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) th.AssertEquals(t, r.RequestURI, "/v2.0/ports?dns_name=test-port") w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, porttest.ListResponse) }) } func PortHandleGet(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 PortHandleCreate(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"], "dns_name": "test-port" } } `) 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" } ], "dns_name": "test-port", "dns_assignment": [ { "hostname": "test-port", "ip_address": "172.24.4.2", "fqdn": "test-port.openstack.local." } ], "id": "65c0ee9f-d634-4522-8954-51021b570b0d", "security_groups": [ "f0ac4394-7e4a-4409-9701-ba8be283dbc3" ], "device_id": "" } } `) }) } func PortHandleUpdate(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" ], "dns_name": "test-port1" } } `) 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": "", "dns_name": "test-port1", "dns_assignment": [ { "hostname": "test-port1", "ip_address": "172.24.4.2", "fqdn": "test-port1.openstack.local." } ] } } `) }) } func FloatingIPHandleList(t *testing.T) { 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) th.AssertEquals(t, r.RequestURI, "/v2.0/floatingips?dns_domain=local.&dns_name=test-fip") w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, floatingiptest.ListResponseDNS) }) } func FloatingIPHandleGet(t *testing.T) { th.Mux.HandleFunc("/v2.0/floatingips/2f95fd2b-9f6a-4e8e-9e9a-2cbe286cbf9e", 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, fmt.Sprintf(`{"floatingip": %s}`, floatingiptest.FipDNS)) }) } func FloatingIPHandleCreate(t *testing.T) { 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": "6d67c30a-ddb4-49a1-bec3-a65b286b4170", "dns_name": "test-fip", "dns_domain": "local." } } `) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) fmt.Fprintf(w, fmt.Sprintf(`{"floatingip": %s}`, floatingiptest.FipDNS)) }) } func NetworkHandleList(t *testing.T) { 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) th.AssertEquals(t, r.RequestURI, "/v2.0/networks?dns_domain=local.") w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, networktest.ListResponse) }) } func NetworkHandleGet(t *testing.T) { 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, networktest.GetResponse) }) } func NetworkHandleCreate(t *testing.T) { 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, NetworkCreateRequest) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) fmt.Fprintf(w, NetworkCreateResponse) }) } func NetworkHandleUpdate(t *testing.T) { th.Mux.HandleFunc("/v2.0/networks/db193ab3-96e3-4cb3-8fc5-05f4296d0324", 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, NetworkUpdateRequest) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, NetworkUpdateResponse) }) } requests_test.go000066400000000000000000000253351367513235700355160ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/dns/testingpackage testing import ( "testing" "time" fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/dns" "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/ports" th "github.com/gophercloud/gophercloud/testhelper" ) type PortDNS struct { ports.Port dns.PortDNSExt } type FloatingIPDNS struct { floatingips.FloatingIP dns.FloatingIPDNSExt } type NetworkDNS struct { networks.Network dns.NetworkDNSExt } var createdTime, _ = time.Parse(time.RFC3339, "2019-06-30T04:15:37Z") var updatedTime, _ = time.Parse(time.RFC3339, "2019-06-30T05:18:49Z") func TestPortList(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() PortHandleListSuccessfully(t) var actual []PortDNS expected := []PortDNS{ { 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", }, PortDNSExt: dns.PortDNSExt{ DNSName: "test-port", DNSAssignment: []map[string]string{ { "hostname": "test-port", "ip_address": "172.24.4.2", "fqdn": "test-port.openstack.local.", }, }, }, }, } var listOptsBuilder ports.ListOptsBuilder listOptsBuilder = dns.PortListOptsExt{ ListOptsBuilder: ports.ListOpts{}, DNSName: "test-port", } allPages, err := ports.List(fake.ServiceClient(), listOptsBuilder).AllPages() th.AssertNoErr(t, err) err = ports.ExtractPortsInto(allPages, &actual) th.AssertNoErr(t, err) th.CheckDeepEquals(t, expected, actual) } func TestPortGet(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() PortHandleGet(t) var s PortDNS 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.DNSName, "test-port") th.AssertDeepEquals(t, s.DNSAssignment, []map[string]string{ { "hostname": "test-port", "ip_address": "172.24.4.2", "fqdn": "test-port.openstack.local.", }, }) } func TestPortCreate(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() PortHandleCreate(t) var s PortDNS 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 := dns.PortCreateOptsExt{ CreateOptsBuilder: portCreateOpts, DNSName: "test-port", } 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.DNSName, "test-port") th.AssertDeepEquals(t, s.DNSAssignment, []map[string]string{ { "hostname": "test-port", "ip_address": "172.24.4.2", "fqdn": "test-port.openstack.local.", }, }) } func TestPortRequiredCreateOpts(t *testing.T) { res := ports.Create(fake.ServiceClient(), dns.PortCreateOptsExt{CreateOptsBuilder: ports.CreateOpts{}}) if res.Err == nil { t.Fatalf("Expected error, got none") } } func TestPortUpdate(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() PortHandleUpdate(t) var s PortDNS name := "new_port_name" portUpdateOpts := ports.UpdateOpts{ Name: &name, FixedIPs: []ports.IP{ {SubnetID: "a0304c3a-4f08-4c43-88af-d796509c97d2", IPAddress: "10.0.0.3"}, }, SecurityGroups: &[]string{"f0ac4394-7e4a-4409-9701-ba8be283dbc3"}, } dnsName := "test-port1" updateOpts := dns.PortUpdateOptsExt{ UpdateOptsBuilder: portUpdateOpts, DNSName: &dnsName, } 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.DNSName, "test-port1") th.AssertDeepEquals(t, s.DNSAssignment, []map[string]string{ { "hostname": "test-port1", "ip_address": "172.24.4.2", "fqdn": "test-port1.openstack.local.", }, }) } func TestFloatingIPGet(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() FloatingIPHandleGet(t) var actual FloatingIPDNS err := floatingips.Get(fake.ServiceClient(), "2f95fd2b-9f6a-4e8e-9e9a-2cbe286cbf9e").ExtractInto(&actual) th.AssertNoErr(t, err) expected := FloatingIPDNS{ FloatingIP: floatingips.FloatingIP{ FloatingNetworkID: "6d67c30a-ddb4-49a1-bec3-a65b286b4170", FixedIP: "", FloatingIP: "192.0.0.4", TenantID: "017d8de156df4177889f31a9bd6edc00", CreatedAt: createdTime, UpdatedAt: updatedTime, Status: "DOWN", PortID: "", ID: "2f95fd2b-9f6a-4e8e-9e9a-2cbe286cbf9e", RouterID: "1117c30a-ddb4-49a1-bec3-a65b286b4170", }, FloatingIPDNSExt: dns.FloatingIPDNSExt{ DNSName: "test-fip", DNSDomain: "local.", }, } th.CheckDeepEquals(t, expected, actual) } func TestFloatingIPCreate(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() FloatingIPHandleCreate(t) var actual FloatingIPDNS fipCreateOpts := floatingips.CreateOpts{ FloatingNetworkID: "6d67c30a-ddb4-49a1-bec3-a65b286b4170", } options := dns.FloatingIPCreateOptsExt{ CreateOptsBuilder: fipCreateOpts, DNSName: "test-fip", DNSDomain: "local.", } err := floatingips.Create(fake.ServiceClient(), options).ExtractInto(&actual) th.AssertNoErr(t, err) expected := FloatingIPDNS{ FloatingIP: floatingips.FloatingIP{ FloatingNetworkID: "6d67c30a-ddb4-49a1-bec3-a65b286b4170", FixedIP: "", FloatingIP: "192.0.0.4", TenantID: "017d8de156df4177889f31a9bd6edc00", CreatedAt: createdTime, UpdatedAt: updatedTime, Status: "DOWN", PortID: "", ID: "2f95fd2b-9f6a-4e8e-9e9a-2cbe286cbf9e", RouterID: "1117c30a-ddb4-49a1-bec3-a65b286b4170", }, FloatingIPDNSExt: dns.FloatingIPDNSExt{ DNSName: "test-fip", DNSDomain: "local.", }, } th.CheckDeepEquals(t, expected, actual) } func TestNetworkGet(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() NetworkHandleGet(t) var actual NetworkDNS err := networks.Get(fake.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").ExtractInto(&actual) th.AssertNoErr(t, err) expected := NetworkDNS{ Network: networks.Network{ Name: "public", Subnets: []string{"54d6f61d-db07-451c-9ab3-b9609b6b6f0b"}, Status: "ACTIVE", TenantID: "4fd44f30292945e481c7b8a0c8908869", CreatedAt: createdTime, UpdatedAt: updatedTime, AdminStateUp: true, Shared: true, ID: "d32019d3-bc6e-4319-9c1d-6722fc136a22", }, NetworkDNSExt: dns.NetworkDNSExt{ DNSDomain: "local.", }, } th.CheckDeepEquals(t, expected, actual) } func TestNetworkCreate(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() NetworkHandleCreate(t) var actual NetworkDNS iTrue := true networkCreateOpts := networks.CreateOpts{Name: "private", AdminStateUp: &iTrue} createOpts := dns.NetworkCreateOptsExt{ CreateOptsBuilder: networkCreateOpts, DNSDomain: "local.", } err := networks.Create(fake.ServiceClient(), createOpts).ExtractInto(&actual) th.AssertNoErr(t, err) expected := NetworkDNS{ Network: networks.Network{ Name: "private", Subnets: []string{"08eae331-0402-425a-923c-34f7cfe39c1b"}, Status: "ACTIVE", TenantID: "26a7980765d0414dbc1fc1f88cdb7e6e", CreatedAt: createdTime, UpdatedAt: updatedTime, AdminStateUp: true, Shared: false, ID: "db193ab3-96e3-4cb3-8fc5-05f4296d0324", }, NetworkDNSExt: dns.NetworkDNSExt{ DNSDomain: "local.", }, } th.CheckDeepEquals(t, expected, actual) } func TestNetworkUpdate(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() NetworkHandleUpdate(t) var actual NetworkDNS name := "new_network_name" networkUpdateOpts := networks.UpdateOpts{Name: &name, AdminStateUp: new(bool)} updateOpts := dns.NetworkUpdateOptsExt{ UpdateOptsBuilder: networkUpdateOpts, DNSDomain: new(string), } err := networks.Update(fake.ServiceClient(), "db193ab3-96e3-4cb3-8fc5-05f4296d0324", updateOpts).ExtractInto(&actual) th.AssertNoErr(t, err) expected := NetworkDNS{ Network: networks.Network{ Name: "new_network_name", Subnets: []string{"08eae331-0402-425a-923c-34f7cfe39c1b"}, Status: "ACTIVE", TenantID: "26a7980765d0414dbc1fc1f88cdb7e6e", CreatedAt: createdTime, UpdatedAt: updatedTime, AdminStateUp: false, Shared: false, ID: "db193ab3-96e3-4cb3-8fc5-05f4296d0324", }, NetworkDNSExt: dns.NetworkDNSExt{ DNSDomain: "", }, } th.CheckDeepEquals(t, expected, actual) } golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/external/000077500000000000000000000000001367513235700317055ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/external/doc.go000066400000000000000000000021101367513235700327730ustar00rootroot00000000000000/* 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 golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/external/requests.go000066400000000000000000000043741367513235700341170ustar00rootroot00000000000000package 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 } golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/external/results.go000066400000000000000000000004141367513235700337340ustar00rootroot00000000000000package 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"` } golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/external/testing/000077500000000000000000000000001367513235700333625ustar00rootroot00000000000000doc.go000066400000000000000000000000471367513235700344000ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/external/testing// external unit tests package testing fixtures.go000066400000000000000000000031411367513235700355020ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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.go000066400000000000000000000011511367513235700365420ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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.go000066400000000000000000000073771367513235700364100ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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 name := "new_network_name" networkUpdateOpts := networks.UpdateOpts{ Name: &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) } golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/extradhcpopts/000077500000000000000000000000001367513235700327535ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/extradhcpopts/doc.go000066400000000000000000000033241367513235700340510ustar00rootroot00000000000000/* 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.go000066400000000000000000000061471367513235700351060ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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.go000066400000000000000000000011631367513235700347250ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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"` } golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/fwaas/000077500000000000000000000000001367513235700311645ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/fwaas/doc.go000066400000000000000000000002261367513235700322600ustar00rootroot00000000000000// Package fwaas provides information and interaction with the Firewall // as a Service extension for the OpenStack Networking service. package fwaas golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/fwaas/firewalls/000077500000000000000000000000001367513235700331545ustar00rootroot00000000000000doc.go000066400000000000000000000023641367513235700341760ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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.go000066400000000000000000000002331367513235700347360ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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.go000066400000000000000000000120241367513235700352760ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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 } resp, err := c.Post(rootURL(c), b, &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get retrieves a particular firewall based on its unique ID. func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { resp, err := c.Get(resourceURL(c, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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 } resp, err := c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete will permanently delete a particular firewall based on its unique ID. func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { resp, err := c.Delete(resourceURL(c, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } results.go000066400000000000000000000053461367513235700351350ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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/000077500000000000000000000000001367513235700345525ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/fwaas/firewallsdoc.go000066400000000000000000000000501367513235700356410ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/fwaas/firewalls/testing// firewalls unit tests package testing requests_test.go000066400000000000000000000236731367513235700400260ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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" } } `) }) var name = "fw" var description = "updated fw" options := firewalls.UpdateOpts{ Name: &name, Description: &description, 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.go000066400000000000000000000005261367513235700344140ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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) } golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/fwaas/policies/000077500000000000000000000000001367513235700327735ustar00rootroot00000000000000doc.go000066400000000000000000000034421367513235700340130ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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.go000066400000000000000000000146661367513235700351330ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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 } resp, err := c.Post(rootURL(c), b, &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get retrieves a particular firewall policy based on its unique ID. func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { resp, err := c.Get(resourceURL(c, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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 } resp, err := c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete will permanently delete a particular firewall policy based on its // unique ID. func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { resp, err := c.Delete(resourceURL(c, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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 } resp, err := c.Put(insertURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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} resp, err := c.Put(removeURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } results.go000066400000000000000000000056641367513235700347570ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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/000077500000000000000000000000001367513235700343715ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/fwaas/policiesdoc.go000066400000000000000000000000471367513235700354660ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/fwaas/policies/testing// policies unit tests package testing requests_test.go000066400000000000000000000173021367513235700376350ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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" } } `) }) var name = "policy" var description = "Firewall policy" options := policies.UpdateOpts{ Name: &name, Description: &description, 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.go000066400000000000000000000012331367513235700342270ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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/000077500000000000000000000000001367513235700343605ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/fwaasdoc.go000066400000000000000000000030421367513235700354530ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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.go000066400000000000000000000023051367513235700365620ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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.go000066400000000000000000000003301367513235700364040ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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/000077500000000000000000000000001367513235700360355ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/fwaas/routerinsertiondoc.go000066400000000000000000000000561367513235700371320ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/fwaas/routerinsertion/testing// routerinsertion unit tests package testing requests_test.go000066400000000000000000000154331367513235700413040ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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" } } `) }) var name = "fw" var description = "updated fw" firewallUpdateOpts := firewalls.UpdateOpts{ Name: &name, Description: &description, 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" } } `) }) var name = "fw" var description = "updated fw" firewallUpdateOpts := firewalls.UpdateOpts{ Name: &name, Description: &description, 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) } golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/fwaas/rules/000077500000000000000000000000001367513235700323165ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/fwaas/rules/doc.go000066400000000000000000000023751367513235700334210ustar00rootroot00000000000000/* 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.go000066400000000000000000000003641367513235700341050ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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.go000066400000000000000000000160301367513235700344410ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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 } resp, err := c.Post(rootURL(c), b, &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get retrieves a particular firewall rule based on its unique ID. func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { resp, err := c.Get(resourceURL(c, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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 } resp, err := c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete will permanently delete a particular firewall rule based on its // unique ID. func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { resp, err := c.Delete(resourceURL(c, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } results.go000066400000000000000000000060771367513235700343010ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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 } golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/fwaas/rules/testing/000077500000000000000000000000001367513235700337735ustar00rootroot00000000000000doc.go000066400000000000000000000000441367513235700350060ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/fwaas/rules/testing// rules unit tests package testing requests_test.go000066400000000000000000000252521367513235700371630ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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) } golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/fwaas/rules/urls.go000066400000000000000000000005271367513235700336360ustar00rootroot00000000000000package 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) } golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/fwaas_v2/000077500000000000000000000000001367513235700315735ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/fwaas_v2/doc.go000066400000000000000000000002311367513235700326630ustar00rootroot00000000000000// Package fwaas provides information and interaction with the Firewall // as a Service extension for the OpenStack Networking service. package fwaas_v2 golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/fwaas_v2/rules/000077500000000000000000000000001367513235700327255ustar00rootroot00000000000000requests.go000066400000000000000000000171501367513235700350540ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/fwaas_v2/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" ) type ( // Action represents a valid rule protocol Action string ) const ( // ActionAllow is to allow traffic ActionAllow Action = "allow" // ActionDeny is to deny traffic ActionDeny Action = "deny" // ActionTCP is to reject traffic ActionReject Action = "reject" ) // 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"` Name string `q:"name"` Description string `q:"description"` Protocol Protocol `q:"protocol"` Action Action `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"` Shared *bool `q:"shared"` ProjectID string `q:"project_id"` FirewallPolicyID string `q:"firewall_policy_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 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 { 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 Action `json:"action" required:"true"` TenantID string `json:"tenant_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 } resp, err := c.Post(rootURL(c), b, &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get retrieves a particular firewall rule based on its unique ID. func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { resp, err := c.Get(resourceURL(c, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // UpdateOptsBuilder is the interface options structs have to satisfy in order // to be used in the main Update 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 UpdateOptsBuilder interface { ToRuleUpdateMap() (map[string]interface{}, error) } // UpdateOpts contains the values used when updating a firewall rule. type UpdateOpts struct { Protocol *Protocol `json:"protocol,omitempty"` Action *Action `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 } resp, err := c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete will permanently delete a particular firewall rule based on its unique ID. func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { resp, err := c.Delete(resourceURL(c, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } results.go000066400000000000000000000054421367513235700347030ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/fwaas_v2/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"` FirewallPolicyID string `json:"firewall_policy_id"` 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 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 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. type GetResult struct { commonResult } // UpdateResult represents the result of an update operation. type UpdateResult struct { commonResult } // DeleteResult represents the result of a delete operation. type DeleteResult struct { gophercloud.ErrResult } // CreateResult represents the result of a create operation. type CreateResult struct { commonResult } testing/000077500000000000000000000000001367513235700343235ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/fwaas_v2/rulesdoc.go000066400000000000000000000000701367513235700354140ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/fwaas_v2/rules/testing// networking_extensions_fwaas_rules_v2 package testing requests_test.go000066400000000000000000000251541367513235700375730ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/fwaas_v2/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_v2/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/fwaas/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", "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", "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: string(rules.ProtocolTCP), Description: "ssh rule", SourcePort: "", SourceIPAddress: "", DestinationIPAddress: "192.168.1.0/24", FirewallPolicyID: "e2a5fb51-698c-4898-87e8-f1eee6b50919", DestinationPort: "22", ID: "f03bd950-6c56-4f5e-a307-45967078f507", Name: "ssh_form_any", TenantID: "80cf934d6ffb4ef5b244f1c512ad1e61", Enabled: true, Action: string(rules.ActionAllow), IPVersion: 4, Shared: false, }, { Protocol: "udp", Description: "udp rule", SourcePort: "", SourceIPAddress: "", DestinationIPAddress: "", FirewallPolicyID: "98d7fb51-698c-4123-87e8-f1eee6b5ab7e", 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/fwaas/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/fwaas/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/fwaas/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.FirewallPolicyID) 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/fwaas/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 := rules.ProtocolTCP newDescription := "ssh rule" newDestinationIP := "192.168.1.0/24" newDestintionPort := "22" newName := "ssh_form_any" newAction := rules.ActionAllow 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/fwaas/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.go000066400000000000000000000005321367513235700341620ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/fwaas_v2/rulespackage rules import "github.com/gophercloud/gophercloud" const ( rootPath = "fwaas" 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) } golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/layer3/000077500000000000000000000000001367513235700312625ustar00rootroot00000000000000addressscopes/000077500000000000000000000000001367513235700340455ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/layer3doc.go000066400000000000000000000031661367513235700351470ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/layer3/addressscopes/* Package addressscopes provides the ability to retrieve and manage Address scopes through the Neutron API. Example of Listing Address scopes listOpts := addressscopes.ListOpts{ IPVersion: 6, } allPages, err := addressscopes.List(networkClient, listOpts).AllPages() if err != nil { panic(err) } allAddressScopes, err := addressscopes.ExtractAddressScopes(allPages) if err != nil { panic(err) } for _, addressScope := range allAddressScopes { fmt.Printf("%+v\n", addressScope) } Example to Get an Address scope addressScopeID = "9cc35860-522a-4d35-974d-51d4b011801e" addressScope, err := addressscopes.Get(networkClient, addressScopeID).Extract() if err != nil { panic(err) } Example to Create a new Address scope addressScopeOpts := addressscopes.CreateOpts{ Name: "my_address_scope", IPVersion: 6, } addressScope, err := addressscopes.Create(networkClient, addressScopeOpts).Extract() if err != nil { panic(err) } Example to Update an Address scope addressScopeID = "9cc35860-522a-4d35-974d-51d4b011801e" newName := "awesome_name" updateOpts := addressscopes.UpdateOpts{ Name: &newName, } addressScope, err := addressscopes.Update(networkClient, addressScopeID, updateOpts).Extract() if err != nil { panic(err) } Example to Delete an Address scope addressScopeID = "9cc35860-522a-4d35-974d-51d4b011801e" err := addressscopes.Delete(networkClient, addressScopeID).ExtractErr() if err != nil { panic(err) } */ package addressscopes requests.go000066400000000000000000000120661367513235700362540ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/layer3/addressscopespackage addressscopes import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // ListOptsBuilder allows extensions to add additional parameters to the // List request. type ListOptsBuilder interface { ToAddressScopeListQuery() (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 address-scope attributes you want to see returned. // SortKey allows you to sort by a particular address-scope 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"` TenantID string `q:"tenant_id"` ProjectID string `q:"project_id"` IPVersion int `q:"ip_version"` Shared *bool `q:"shared"` Description string `q:"description"` Limit int `q:"limit"` Marker string `q:"marker"` SortKey string `q:"sort_key"` SortDir string `q:"sort_dir"` } // ToAddressScopeListQuery formats a ListOpts into a query string. func (opts ListOpts) ToAddressScopeListQuery() (string, error) { q, err := gophercloud.BuildQueryString(opts) return q.String(), err } // List returns a Pager which allows you to iterate over a collection of // address-scopes. It accepts a ListOpts struct, which allows you to filter and // sort the returned collection for greater efficiency. // // Default policy settings return only the address-scopes 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.ToAddressScopeListQuery() if err != nil { return pagination.Pager{Err: err} } url += query } return pagination.NewPager(c, url, func(r pagination.PageResult) pagination.Page { return AddressScopePage{pagination.LinkedPageBase{PageResult: r}} }) } // Get retrieves a specific address-scope based on its ID. func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { resp, err := c.Get(getURL(c, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // CreateOptsBuilder allows to add additional parameters to the // Create request. type CreateOptsBuilder interface { ToAddressScopeCreateMap() (map[string]interface{}, error) } // CreateOpts specifies parameters of a new address-scope. type CreateOpts struct { // Name is the human-readable name of the address-scope. Name string `json:"name"` // 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"` // IPVersion is the IP protocol version. IPVersion int `json:"ip_version"` // Shared indicates whether this address-scope is shared across all projects. Shared bool `json:"shared,omitempty"` } // ToAddressScopeCreateMap constructs a request body from CreateOpts. func (opts CreateOpts) ToAddressScopeCreateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "address_scope") } // Create requests the creation of a new address-scope on the server. func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToAddressScopeCreateMap() if err != nil { r.Err = err return } resp, err := client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{201}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // UpdateOptsBuilder allows extensions to add additional parameters to the // Update request. type UpdateOptsBuilder interface { ToAddressScopeUpdateMap() (map[string]interface{}, error) } // UpdateOpts represents options used to update an address-scope. type UpdateOpts struct { // Name is the human-readable name of the address-scope. Name *string `json:"name,omitempty"` // Shared indicates whether this address-scope is shared across all projects. Shared *bool `json:"shared,omitempty"` } // ToAddressScopeUpdateMap builds a request body from UpdateOpts. func (opts UpdateOpts) ToAddressScopeUpdateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "address_scope") } // Update accepts a UpdateOpts struct and updates an existing address-scope // using the values provided. func Update(c *gophercloud.ServiceClient, addressScopeID string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToAddressScopeUpdateMap() if err != nil { r.Err = err return } resp, err := c.Put(updateURL(c, addressScopeID), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete accepts a unique ID and deletes the address-scope associated with it. func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { resp, err := c.Delete(deleteURL(c, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } results.go000066400000000000000000000055061367513235700361030ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/layer3/addressscopespackage addressscopes 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 an address-scope resource. func (r commonResult) Extract() (*AddressScope, error) { var s struct { AddressScope *AddressScope `json:"address_scope"` } err := r.ExtractInto(&s) return s.AddressScope, 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 an AddressScope. 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 } // AddressScope represents a Neutron address-scope. type AddressScope struct { // ID is the id of the address-scope. ID string `json:"id"` // Name is the human-readable name of the address-scope. Name string `json:"name"` // 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"` // IPVersion is the IP protocol version. IPVersion int `json:"ip_version"` // Shared indicates whether this address-scope is shared across all projects. Shared bool `json:"shared"` } // AddressScopePage stores a single page of AddressScopes from a List() API call. type AddressScopePage struct { pagination.LinkedPageBase } // NextPageURL is invoked when a paginated collection of address-scope 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 AddressScopePage) NextPageURL() (string, error) { var s struct { Links []gophercloud.Link `json:"address_scopes_links"` } err := r.ExtractInto(&s) if err != nil { return "", err } return gophercloud.ExtractNextURL(s.Links) } // IsEmpty determines whether or not a AddressScopePage is empty. func (r AddressScopePage) IsEmpty() (bool, error) { addressScopes, err := ExtractAddressScopes(r) return len(addressScopes) == 0, err } // ExtractAddressScopes interprets the results of a single page from a List() // API call, producing a slice of AddressScopes structs. func ExtractAddressScopes(r pagination.Page) ([]AddressScope, error) { var s struct { AddressScopes []AddressScope `json:"address_scopes"` } err := (r.(AddressScopePage)).ExtractInto(&s) return s.AddressScopes, err } testing/000077500000000000000000000000001367513235700355225ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/layer3/addressscopesdoc.go000066400000000000000000000000521367513235700366130ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/layer3/addressscopes/testing// subnetpools unit tests package testing fixtures.go000066400000000000000000000057671367513235700377410ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/layer3/addressscopes/testingpackage testing import "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/layer3/addressscopes" // AddressScopesListResult represents raw response for the List request. const AddressScopesListResult = ` { "address_scopes": [ { "name": "scopev4", "tenant_id": "4a9807b773404e979b19633f38370643", "ip_version": 4, "shared": false, "project_id": "4a9807b773404e979b19633f38370643", "id": "9cc35860-522a-4d35-974d-51d4b011801e" }, { "name": "scopev6", "tenant_id": "4a9807b773404e979b19633f38370643", "ip_version": 6, "shared": true, "project_id": "4a9807b773404e979b19633f38370643", "id": "be992b82-bf42-4ab7-bf7b-6baa8759d388" } ] } ` // AddressScope1 represents first unmarshalled address scope from the // AddressScopesListResult. var AddressScope1 = addressscopes.AddressScope{ ID: "9cc35860-522a-4d35-974d-51d4b011801e", Name: "scopev4", TenantID: "4a9807b773404e979b19633f38370643", ProjectID: "4a9807b773404e979b19633f38370643", IPVersion: 4, Shared: false, } // AddressScope2 represents second unmarshalled address scope from the // AddressScopesListResult. var AddressScope2 = addressscopes.AddressScope{ ID: "be992b82-bf42-4ab7-bf7b-6baa8759d388", Name: "scopev6", TenantID: "4a9807b773404e979b19633f38370643", ProjectID: "4a9807b773404e979b19633f38370643", IPVersion: 6, Shared: true, } // AddressScopesGetResult represents raw response for the Get request. const AddressScopesGetResult = ` { "address_scope": { "name": "scopev4", "tenant_id": "4a9807b773404e979b19633f38370643", "ip_version": 4, "shared": false, "project_id": "4a9807b773404e979b19633f38370643", "id": "9cc35860-522a-4d35-974d-51d4b011801e" } } ` // AddressScopeCreateRequest represents raw Create request. const AddressScopeCreateRequest = ` { "address_scope": { "ip_version": 4, "shared": true, "name": "test0" } } ` // AddressScopeCreateResult represents raw Create response. const AddressScopeCreateResult = ` { "address_scope": { "name": "test0", "tenant_id": "4a9807b773404e979b19633f38370643", "ip_version": 4, "shared": true, "project_id": "4a9807b773404e979b19633f38370643", "id": "9cc35860-522a-4d35-974d-51d4b011801e" } } ` // AddressScopeUpdateRequest represents raw Update request. const AddressScopeUpdateRequest = ` { "address_scope": { "name": "test1", "shared": true } } ` // AddressScopeUpdateResult represents raw Update response. const AddressScopeUpdateResult = ` { "address_scope": { "name": "test1", "tenant_id": "4a9807b773404e979b19633f38370643", "ip_version": 4, "shared": true, "project_id": "4a9807b773404e979b19633f38370643", "id": "9cc35860-522a-4d35-974d-51d4b011801e" } } ` requests_test.go000066400000000000000000000106241367513235700407660ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/layer3/addressscopes/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/addressscopes" "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/address-scopes", 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, AddressScopesListResult) }) count := 0 addressscopes.List(fake.ServiceClient(), addressscopes.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { count++ actual, err := addressscopes.ExtractAddressScopes(page) if err != nil { t.Errorf("Failed to extract addressscopes: %v", err) return false, nil } expected := []addressscopes.AddressScope{ AddressScope1, AddressScope2, } 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/address-scopes/9cc35860-522a-4d35-974d-51d4b011801e", 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, AddressScopesGetResult) }) s, err := addressscopes.Get(fake.ServiceClient(), "9cc35860-522a-4d35-974d-51d4b011801e").Extract() th.AssertNoErr(t, err) th.AssertEquals(t, s.ID, "9cc35860-522a-4d35-974d-51d4b011801e") th.AssertEquals(t, s.Name, "scopev4") th.AssertEquals(t, s.TenantID, "4a9807b773404e979b19633f38370643") th.AssertEquals(t, s.ProjectID, "4a9807b773404e979b19633f38370643") th.AssertEquals(t, s.IPVersion, 4) th.AssertEquals(t, s.Shared, false) } func TestCreate(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/address-scopes", 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, AddressScopeCreateRequest) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) fmt.Fprintf(w, AddressScopeCreateResult) }) opts := addressscopes.CreateOpts{ IPVersion: 4, Shared: true, Name: "test0", } s, err := addressscopes.Create(fake.ServiceClient(), opts).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, s.Name, "test0") th.AssertEquals(t, s.Shared, true) th.AssertEquals(t, s.IPVersion, 4) th.AssertEquals(t, s.TenantID, "4a9807b773404e979b19633f38370643") th.AssertEquals(t, s.ProjectID, "4a9807b773404e979b19633f38370643") th.AssertEquals(t, s.ID, "9cc35860-522a-4d35-974d-51d4b011801e") } func TestUpdate(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/address-scopes/9cc35860-522a-4d35-974d-51d4b011801e", 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, AddressScopeUpdateRequest) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, AddressScopeUpdateResult) }) shared := true newName := "test1" updateOpts := addressscopes.UpdateOpts{ Name: &newName, Shared: &shared, } s, err := addressscopes.Update(fake.ServiceClient(), "9cc35860-522a-4d35-974d-51d4b011801e", updateOpts).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, s.Name, "test1") th.AssertEquals(t, s.Shared, true) } func TestDelete(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/address-scopes/9cc35860-522a-4d35-974d-51d4b011801e", 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 := addressscopes.Delete(fake.ServiceClient(), "9cc35860-522a-4d35-974d-51d4b011801e") th.AssertNoErr(t, res.Err) } urls.go000066400000000000000000000013411367513235700353600ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/layer3/addressscopespackage addressscopes import "github.com/gophercloud/gophercloud" const resourcePath = "address-scopes" 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) } golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/layer3/doc.go000066400000000000000000000004761367513235700323650ustar00rootroot00000000000000// 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 golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/layer3/floatingips/000077500000000000000000000000001367513235700336015ustar00rootroot00000000000000doc.go000066400000000000000000000027741367513235700346300ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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: new(string), } 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.go000066400000000000000000000165141367513235700357330ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/layer3/floatingipspackage floatingips import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // ListOptsBuilder allows extensions to add additional parameters to the // List request. type ListOptsBuilder interface { ToFloatingIPListQuery() (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 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"` Description string `q:"description"` 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"` Tags string `q:"tags"` TagsAny string `q:"tags-any"` NotTags string `q:"not-tags"` NotTagsAny string `q:"not-tags-any"` } // ToNetworkListQuery formats a ListOpts into a query string. func (opts ListOpts) ToFloatingIPListQuery() (string, error) { q, err := gophercloud.BuildQueryString(opts) return q.String(), err } // 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 ListOptsBuilder) pagination.Pager { url := rootURL(c) if opts != nil { query, err := opts.ToFloatingIPListQuery() if err != nil { return pagination.Pager{Err: err} } url += query } return pagination.NewPager(c, url, 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 { Description string `json:"description,omitempty"` 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 } resp, err := c.Post(rootURL(c), b, &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get retrieves a particular floating IP resource based on its unique ID. func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { resp, err := c.Get(resourceURL(c, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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 { Description *string `json:"description,omitempty"` PortID *string `json:"port_id,omitempty"` FixedIP string `json:"fixed_ip_address,omitempty"` } // ToFloatingIPUpdateMap allows UpdateOpts to satisfy the UpdateOptsBuilder // interface func (opts UpdateOpts) ToFloatingIPUpdateMap() (map[string]interface{}, error) { b, err := gophercloud.BuildRequestBody(opts, "floatingip") if err != nil { return nil, err } if m := b["floatingip"].(map[string]interface{}); m["port_id"] == "" { m["port_id"] = nil } return b, nil } // 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 } resp, err := c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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) { resp, err := c.Delete(resourceURL(c, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } results.go000066400000000000000000000122211367513235700355500ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/layer3/floatingipspackage floatingips import ( "encoding/json" "time" "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"` // Description for the floating IP instance. Description string `json:"description"` // 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"` // UpdatedAt and CreatedAt contain ISO-8601 timestamps of when the state of // the floating ip last changed, and when it was created. UpdatedAt time.Time `json:"-"` CreatedAt time.Time `json:"-"` // 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"` // Tags optionally set via extensions/attributestags Tags []string `json:"tags"` } func (r *FloatingIP) UnmarshalJSON(b []byte) error { type tmp FloatingIP // Support for older neutron time format var s1 struct { tmp CreatedAt gophercloud.JSONRFC3339NoZ `json:"created_at"` UpdatedAt gophercloud.JSONRFC3339NoZ `json:"updated_at"` } err := json.Unmarshal(b, &s1) if err == nil { *r = FloatingIP(s1.tmp) r.CreatedAt = time.Time(s1.CreatedAt) r.UpdatedAt = time.Time(s1.UpdatedAt) return nil } // Support for newer neutron time format var s2 struct { tmp CreatedAt time.Time `json:"created_at"` UpdatedAt time.Time `json:"updated_at"` } err = json.Unmarshal(b, &s2) if err != nil { return err } *r = FloatingIP(s2.tmp) r.CreatedAt = time.Time(s2.CreatedAt) r.UpdatedAt = time.Time(s2.UpdatedAt) return nil } type commonResult struct { gophercloud.Result } // Extract will extract a FloatingIP resource from a result. func (r commonResult) Extract() (*FloatingIP, error) { var s FloatingIP err := r.ExtractInto(&s) return &s, err } func (r commonResult) ExtractInto(v interface{}) error { return r.Result.ExtractIntoStructPtr(v, "floatingip") } // 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 } func ExtractFloatingIPsInto(r pagination.Page, v interface{}) error { return r.(FloatingIPPage).Result.ExtractIntoSlicePtr(v, "floatingips") } testing/000077500000000000000000000000001367513235700351775ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/layer3/floatingipsdoc.go000066400000000000000000000000521367513235700362700ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/layer3/floatingips/testing// floatingips unit tests package testing fixtures.go000066400000000000000000000026261367513235700374050ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/layer3/floatingips/testingpackage testing import ( "fmt" ) const FipDNS = `{ "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", "created_at": "2019-06-30T04:15:37", "updated_at": "2019-06-30T05:18:49", "status": "DOWN", "port_id": null, "id": "2f95fd2b-9f6a-4e8e-9e9a-2cbe286cbf9e", "router_id": "1117c30a-ddb4-49a1-bec3-a65b286b4170", "dns_domain": "local.", "dns_name": "test-fip" }` const FipNoDNS = `{ "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", "created_at": "2019-06-30T04:15:37Z", "updated_at": "2019-06-30T05:18:49Z", "status": "DOWN", "port_id": "74a342ce-8e07-4e91-880c-9f834b68fa25", "id": "ada25a95-f321-4f59-b0e0-f3a970dd3d63", "router_id": "2227c30a-ddb4-49a1-bec3-a65b286b4170", "dns_domain": "", "dns_name": "" }` var ListResponse = fmt.Sprintf(` { "floatingips": [ %s, %s ] } `, FipDNS, FipNoDNS) var ListResponseDNS = fmt.Sprintf(` { "floatingips": [ %s ] } `, FipDNS) requests_test.go000066400000000000000000000322251367513235700404440ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/layer3/floatingips/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/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, ListResponse) }) count := 0 err := 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 } createdTime, err := time.Parse(time.RFC3339, "2019-06-30T04:15:37Z") th.AssertNoErr(t, err) updatedTime, err := time.Parse(time.RFC3339, "2019-06-30T05:18:49Z") th.AssertNoErr(t, err) expected := []floatingips.FloatingIP{ { FloatingNetworkID: "6d67c30a-ddb4-49a1-bec3-a65b286b4170", FixedIP: "", FloatingIP: "192.0.0.4", TenantID: "017d8de156df4177889f31a9bd6edc00", CreatedAt: createdTime, UpdatedAt: updatedTime, 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", CreatedAt: createdTime, UpdatedAt: updatedTime, 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 }) th.AssertNoErr(t, err) 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", "created_at": "2019-06-30T04:15:37Z", "updated_at": "2019-06-30T05:18:49Z", "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, "2019-06-30T04:15:37Z", ip.CreatedAt.Format(time.RFC3339)) th.AssertEquals(t, "2019-06-30T05:18:49Z", ip.UpdatedAt.Format(time.RFC3339)) 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", "created_at": "2019-06-30T04:15:37Z", "updated_at": "2019-06-30T05:18:49Z", "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, "2019-06-30T04:15:37Z", ip.CreatedAt.Format(time.RFC3339)) th.AssertEquals(t, "2019-06-30T05:18:49Z", ip.UpdatedAt.Format(time.RFC3339)) 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", "created_at": "2019-06-30T04:15:37Z", "updated_at": "2019-06-30T05:18:49Z", "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, "2019-06-30T04:15:37Z", ip.CreatedAt.Format(time.RFC3339)) th.AssertEquals(t, "2019-06-30T05:18:49Z", ip.UpdatedAt.Format(time.RFC3339)) 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", "created_at": "2019-06-30T04:15:37Z", "updated_at": "2019-06-30T05:18:49Z", "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, "2019-06-30T04:15:37Z", ip.CreatedAt.Format(time.RFC3339)) th.AssertEquals(t, "2019-06-30T05:18:49Z", ip.UpdatedAt.Format(time.RFC3339)) 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: new(string)}).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.go000066400000000000000000000004541367513235700350410ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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) } portforwarding/000077500000000000000000000000001367513235700342525ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/layer3doc.go000066400000000000000000000033441367513235700353520ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/layer3/portforwarding/* package portforwarding enables management and retrieval of port forwarding resources for Floating IPs from the OpenStack Networking service. Example to list all Port Forwardings for a floating IP fipID := "2f245a7b-796b-4f26-9cf9-9e82d248fda7" allPages, err := portforwarding.List(client, portforwarding.ListOpts{}, fipID).AllPages() if err != nil { panic(err) } allPFs, err := portforwarding.ExtractPortForwardings(allPages) if err != nil { panic(err) } for _, pf := range allPFs { fmt.Printf("%+v\n", pf) } Example to Get a Port Forwarding with a certain ID fipID := "2f245a7b-796b-4f26-9cf9-9e82d248fda7" pfID := "725ade3c-9760-4880-8080-8fc2dbab9acc" pf, err := portforwarding.Get(client, fipID, pfID).Extract() if err != nil { panic(err) } Example to Create a Port Forwarding for a floating IP createOpts := &portforwarding.CreateOpts{ Protocol: "tcp", InternalPort: 25, ExternalPort: 2230, InternalIPAddress: internalIP, InternalPortID: portID, } pf, err := portforwarding.Create(networkingClient, floatingIPID, createOpts).Extract() if err != nil { panic(err) } Example to Update a Port Forwarding updateOpts := portforwarding.UpdateOpts{ Protocol: "udp", InternalPort: 30, ExternalPort: 678, } fipID := "2f245a7b-796b-4f26-9cf9-9e82d248fda7" pfID := "725ade3c-9760-4880-8080-8fc2dbab9acc" pf, err := portforwarding.Update(client, fipID, pfID, updateOpts).Extract() if err != nil { panic(err) } Example to Delete a Port forwarding fipID := "2f245a7b-796b-4f26-9cf9-9e82d248fda7" pfID := "725ade3c-9760-4880-8080-8fc2dbab9acc" err := portforwarding.Delete(networkClient, fipID, pfID).ExtractErr() if err != nil { panic(err) } */ package portforwarding requests.go000066400000000000000000000121751367513235700364620ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/layer3/portforwardingpackage portforwarding import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) type ListOptsBuilder interface { ToPortForwardingListQuery() (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 forwarding 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"` InternalPortID string `q:"internal_port_id"` ExternalPort string `q:"external_port"` InternalIPAddress string `q:"internal_ip_address"` Protocol string `q:"protocol"` InternalPort string `q:"internal_port"` SortKey string `q:"sort_key"` SortDir string `q:"sort_dir"` Fields string `q:"fields"` Limit int `q:"limit"` Marker string `q:"marker"` } // ToPortForwardingListQuery formats a ListOpts into a query string. func (opts ListOpts) ToPortForwardingListQuery() (string, error) { q, err := gophercloud.BuildQueryString(opts) return q.String(), err } // List returns a Pager which allows you to iterate over a collection of // Port Forwarding 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 ListOptsBuilder, id string) pagination.Pager { url := portForwardingUrl(c, id) if opts != nil { query, err := opts.ToPortForwardingListQuery() if err != nil { return pagination.Pager{Err: err} } url += query } return pagination.NewPager(c, url, func(r pagination.PageResult) pagination.Page { return PortForwardingPage{pagination.LinkedPageBase{PageResult: r}} }) } // Get retrieves a particular port forwarding resource based on its unique ID. func Get(c *gophercloud.ServiceClient, floatingIpId string, pfId string) (r GetResult) { resp, err := c.Get(singlePortForwardingUrl(c, floatingIpId, pfId), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // CreateOpts contains all the values needed to create a new port forwarding // resource. All attributes are required. type CreateOpts struct { InternalPortID string `json:"internal_port_id"` InternalIPAddress string `json:"internal_ip_address"` InternalPort int `json:"internal_port"` ExternalPort int `json:"external_port"` Protocol string `json:"protocol"` } // CreateOptsBuilder allows extensions to add additional parameters to the // Create request. type CreateOptsBuilder interface { ToPortForwardingCreateMap() (map[string]interface{}, error) } // ToPortForwardingCreateMap allows CreateOpts to satisfy the CreateOptsBuilder // interface func (opts CreateOpts) ToPortForwardingCreateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "port_forwarding") } // Create accepts a CreateOpts struct and uses the values provided to create a // new port forwarding for an existing floating IP. func Create(c *gophercloud.ServiceClient, floatingIpId string, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToPortForwardingCreateMap() if err != nil { r.Err = err return } resp, err := c.Post(portForwardingUrl(c, floatingIpId), b, &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // UpdateOpts contains the values used when updating a port forwarding resource. type UpdateOpts struct { InternalPortID string `json:"internal_port_id,omitempty"` InternalIPAddress string `json:"internal_ip_address,omitempty"` InternalPort int `json:"internal_port,omitempty"` ExternalPort int `json:"external_port,omitempty"` Protocol string `json:"protocol,omitempty"` } // ToPortForwardingUpdateMap allows UpdateOpts to satisfy the UpdateOptsBuilder // interface func (opts UpdateOpts) ToPortForwardingUpdateMap() (map[string]interface{}, error) { b, err := gophercloud.BuildRequestBody(opts, "port_forwarding") if err != nil { return nil, err } return b, nil } // UpdateOptsBuilder allows extensions to add additional parameters to the // Update request. type UpdateOptsBuilder interface { ToPortForwardingUpdateMap() (map[string]interface{}, error) } // Update allows port forwarding resources to be updated. func Update(c *gophercloud.ServiceClient, fipID string, pfID string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToPortForwardingUpdateMap() if err != nil { r.Err = err return } resp, err := c.Put(singlePortForwardingUrl(c, fipID, pfID), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete will permanently delete a particular port forwarding for a given floating ID. func Delete(c *gophercloud.ServiceClient, floatingIpId string, pfId string) (r DeleteResult) { resp, err := c.Delete(singlePortForwardingUrl(c, floatingIpId, pfId), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } results.go000066400000000000000000000062761367513235700363150ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/layer3/portforwardingpackage portforwarding import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) type PortForwarding struct { // The ID of the floating IP port forwarding ID string `json:"id"` // The ID of the Neutron port associated to the floating IP port forwarding. InternalPortID string `json:"internal_port_id"` // The TCP/UDP/other protocol port number of the port forwarding’s floating IP address. ExternalPort int `json:"external_port"` // The IP protocol used in the floating IP port forwarding. Protocol string `json:"protocol"` // The TCP/UDP/other protocol port number of the Neutron port fixed // IP address associated to the floating ip port forwarding. InternalPort int `json:"internal_port"` // The fixed IPv4 address of the Neutron port associated // to the floating IP port forwarding. InternalIPAddress string `json:"internal_ip_address"` } type commonResult struct { gophercloud.Result } // CreateResult represents the result of a create operation. Call its Extract // method to interpret it as a PortForwarding. type CreateResult struct { commonResult } // GetResult represents the result of a get operation. Call its Extract // method to interpret it as a PortForwarding. type GetResult struct { commonResult } // UpdateResult represents the result of an update operation. Call its Extract // method to interpret it as a PortForwarding. 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 } // Extract will extract a Port Forwarding resource from a result. func (r commonResult) Extract() (*PortForwarding, error) { var s PortForwarding err := r.ExtractInto(&s) return &s, err } func (r commonResult) ExtractInto(v interface{}) error { return r.Result.ExtractIntoStructPtr(v, "port_forwarding") } // PortForwardingPage is the page returned by a pager when traversing over a // collection of port forwardings. type PortForwardingPage struct { pagination.LinkedPageBase } // NextPageURL is invoked when a paginated collection of port forwardings 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 PortForwardingPage) NextPageURL() (string, error) { var s struct { Links []gophercloud.Link `json:"port_forwarding_links"` } err := r.ExtractInto(&s) if err != nil { return "", err } return gophercloud.ExtractNextURL(s.Links) } // IsEmpty checks whether a PortForwardingPage struct is empty. func (r PortForwardingPage) IsEmpty() (bool, error) { is, err := ExtractPortForwardings(r) return len(is) == 0, err } // ExtractPortForwardings accepts a Page struct, specifically a PortForwardingPage // struct, and extracts the elements into a slice of PortForwarding structs. In // other words, a generic collection is mapped into a relevant slice. func ExtractPortForwardings(r pagination.Page) ([]PortForwarding, error) { var s struct { PortForwardings []PortForwarding `json:"port_forwardings"` } err := (r.(PortForwardingPage)).ExtractInto(&s) return s.PortForwardings, err } testing/000077500000000000000000000000001367513235700357275ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/layer3/portforwardingdoc.go000066400000000000000000000000561367513235700370240ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/layer3/portforwarding/testing// port forwarding unit tests package testing fixtures.go000066400000000000000000000012301367513235700401230ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/layer3/portforwarding/testingpackage testing import "fmt" const PoFw = `{ "protocol": "tcp", "internal_ip_address": "10.0.0.24", "internal_port": 25, "internal_port_id": "070ef0b2-0175-4299-be5c-01fea8cca522", "external_port": 2229, "id": "1798dc82-c0ed-4b79-b12d-4c3c18f90eb2" }` const PoFw_second = `{ "protocol": "tcp", "internal_ip_address": "10.0.0.11", "internal_port": 25, "internal_port_id": "1238be08-a2a8-4b8d-addf-fb5e2250e480", "external_port": 2230, "id": "e0a0274e-4d19-4eab-9e12-9e77a8caf3ea" }` var ListResponse = fmt.Sprintf(` { "port_forwardings": [ %s, %s ] } `, PoFw, PoFw_second) requests_test.go000066400000000000000000000160041367513235700411710ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/layer3/portforwarding/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/portforwarding" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" ) func TestPortForwardingList(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/floatingips/2f95fd2b-9f6a-4e8e-9e9a-2cbe286cbf9e/port_forwardings", 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 portforwarding.List(fake.ServiceClient(), portforwarding.ListOpts{}, "2f95fd2b-9f6a-4e8e-9e9a-2cbe286cbf9e").EachPage(func(page pagination.Page) (bool, error) { count++ actual, err := portforwarding.ExtractPortForwardings(page) if err != nil { t.Errorf("Failed to extract port forwardings: %v", err) return false, err } expected := []portforwarding.PortForwarding{ { Protocol: "tcp", InternalIPAddress: "10.0.0.24", InternalPort: 25, InternalPortID: "070ef0b2-0175-4299-be5c-01fea8cca522", ExternalPort: 2229, ID: "1798dc82-c0ed-4b79-b12d-4c3c18f90eb2", }, { Protocol: "tcp", InternalIPAddress: "10.0.0.11", InternalPort: 25, InternalPortID: "1238be08-a2a8-4b8d-addf-fb5e2250e480", ExternalPort: 2230, ID: "e0a0274e-4d19-4eab-9e12-9e77a8caf3ea", }, } 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/floatingips/2f95fd2b-9f6a-4e8e-9e9a-2cbe286cbf9e/port_forwardings", 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_forwarding": { "protocol": "tcp", "internal_ip_address": "10.0.0.11", "internal_port": 25, "internal_port_id": "1238be08-a2a8-4b8d-addf-fb5e2250e480", "external_port": 2230 } } `) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) fmt.Fprintf(w, ` { "port_forwarding": { "protocol": "tcp", "internal_ip_address": "10.0.0.11", "internal_port": 25, "internal_port_id": "1238be08-a2a8-4b8d-addf-fb5e2250e480", "external_port": 2230, "id": "725ade3c-9760-4880-8080-8fc2dbab9acc" } }`) }) options := portforwarding.CreateOpts{ Protocol: "tcp", InternalIPAddress: "10.0.0.11", InternalPort: 25, ExternalPort: 2230, InternalPortID: "1238be08-a2a8-4b8d-addf-fb5e2250e480", } pf, err := portforwarding.Create(fake.ServiceClient(), "2f95fd2b-9f6a-4e8e-9e9a-2cbe286cbf9e", options).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, "725ade3c-9760-4880-8080-8fc2dbab9acc", pf.ID) th.AssertEquals(t, "10.0.0.11", pf.InternalIPAddress) th.AssertEquals(t, 25, pf.InternalPort) th.AssertEquals(t, "1238be08-a2a8-4b8d-addf-fb5e2250e480", pf.InternalPortID) th.AssertEquals(t, 2230, pf.ExternalPort) th.AssertEquals(t, "tcp", pf.Protocol) } func TestGet(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/floatingips/2f245a7b-796b-4f26-9cf9-9e82d248fda7/port_forwardings/725ade3c-9760-4880-8080-8fc2dbab9acc", 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, ` { "port_forwarding": { "protocol": "tcp", "internal_ip_address": "10.0.0.11", "internal_port": 25, "internal_port_id": "1238be08-a2a8-4b8d-addf-fb5e2250e480", "external_port": 2230, "id": "725ade3c-9760-4880-8080-8fc2dbab9acc" } } `) }) pf, err := portforwarding.Get(fake.ServiceClient(), "2f245a7b-796b-4f26-9cf9-9e82d248fda7", "725ade3c-9760-4880-8080-8fc2dbab9acc").Extract() th.AssertNoErr(t, err) th.AssertEquals(t, "tcp", pf.Protocol) th.AssertEquals(t, "725ade3c-9760-4880-8080-8fc2dbab9acc", pf.ID) th.AssertEquals(t, "10.0.0.11", pf.InternalIPAddress) th.AssertEquals(t, 25, pf.InternalPort) th.AssertEquals(t, "1238be08-a2a8-4b8d-addf-fb5e2250e480", pf.InternalPortID) th.AssertEquals(t, 2230, pf.ExternalPort) } func TestDelete(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/floatingips/2f245a7b-796b-4f26-9cf9-9e82d248fda7/port_forwardings/725ade3c-9760-4880-8080-8fc2dbab9acc", 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 := portforwarding.Delete(fake.ServiceClient(), "2f245a7b-796b-4f26-9cf9-9e82d248fda7", "725ade3c-9760-4880-8080-8fc2dbab9acc") th.AssertNoErr(t, res.Err) } func TestUpdate(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/floatingips/2f245a7b-796b-4f26-9cf9-9e82d248fda7/port_forwardings/725ade3c-9760-4880-8080-8fc2dbab9acc", 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_forwarding": { "protocol": "udp", "internal_port": 37, "internal_port_id": "99889dc2-19a7-4edb-b9d0-d2ace8d1e144", "external_port": 1960 } } `) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ` { "port_forwarding": { "protocol": "udp", "internal_ip_address": "10.0.0.14", "internal_port": 37, "internal_port_id": "99889dc2-19a7-4edb-b9d0-d2ace8d1e144", "external_port": 1960, "id": "725ade3c-9760-4880-8080-8fc2dbab9acc" } } `) }) updatedProtocol := "udp" updatedInternalPort := 37 updatedInternalPortID := "99889dc2-19a7-4edb-b9d0-d2ace8d1e144" updatedExternalPort := 1960 options := portforwarding.UpdateOpts{ Protocol: updatedProtocol, InternalPort: updatedInternalPort, InternalPortID: updatedInternalPortID, ExternalPort: updatedExternalPort, } actual, err := portforwarding.Update(fake.ServiceClient(), "2f245a7b-796b-4f26-9cf9-9e82d248fda7", "725ade3c-9760-4880-8080-8fc2dbab9acc", options).Extract() th.AssertNoErr(t, err) expected := portforwarding.PortForwarding{ Protocol: "udp", InternalIPAddress: "10.0.0.14", InternalPort: 37, ID: "725ade3c-9760-4880-8080-8fc2dbab9acc", InternalPortID: "99889dc2-19a7-4edb-b9d0-d2ace8d1e144", ExternalPort: 1960, } th.AssertDeepEquals(t, expected, *actual) } urls.go000066400000000000000000000007251367513235700355720ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/layer3/portforwardingpackage portforwarding import "github.com/gophercloud/gophercloud" const resourcePath = "floatingips" const portForwardingPath = "port_forwardings" func portForwardingUrl(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL(resourcePath, id, portForwardingPath) } func singlePortForwardingUrl(c *gophercloud.ServiceClient, id string, portForwardingID string) string { return c.ServiceURL(resourcePath, id, portForwardingPath, portForwardingID) } golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/layer3/routers/000077500000000000000000000000001367513235700327655ustar00rootroot00000000000000doc.go000066400000000000000000000043061367513235700340050ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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.go000066400000000000000000000227501367513235700351160ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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"` Description string `q:"description"` 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"` Tags string `q:"tags"` TagsAny string `q:"tags-any"` NotTags string `q:"not-tags"` NotTagsAny string `q:"not-tags-any"` } // 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"` Description string `json:"description,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 } resp, err := c.Post(rootURL(c), b, &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get retrieves a particular router based on its unique ID. func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { resp, err := c.Get(resourceURL(c, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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"` Description *string `json:"description,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 } resp, err := c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete will permanently delete a particular router based on its unique ID. func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { resp, err := c.Delete(resourceURL(c, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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 } resp, err := c.Put(addInterfaceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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 } resp, err := c.Put(removeInterfaceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } results.go000066400000000000000000000132151367513235700347400ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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,omitempty"` 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,omitempty"` 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"` // Description for the router. Description string `json:"description"` // 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"` // Tags optionally set via extensions/attributestags Tags []string `json:"tags"` } // 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/000077500000000000000000000000001367513235700343635ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/layer3/routersdoc.go000066400000000000000000000000461367513235700354570ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/layer3/routers/testing// routers unit tests package testing requests_test.go000066400000000000000000000351271367513235700376340ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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", "external_fixed_ips": [ {"subnet_id": "ab561bc4-1a8e-48f2-9fbd-376fcb1a1def"} ] }, "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 efi := []routers.ExternalFixedIP{ routers.ExternalFixedIP{ SubnetID: "ab561bc4-1a8e-48f2-9fbd-376fcb1a1def", }, } gwi := routers.GatewayInfo{ NetworkID: "8ca37218-28ff-41cb-9b10-039601ea7e6b", EnableSNAT: &enableSNAT, ExternalFixedIPs: efi, } 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.go000066400000000000000000000010741367513235700342240ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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") } golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/lbaas/000077500000000000000000000000001367513235700311455ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/lbaas/doc.go000066400000000000000000000002331367513235700322370ustar00rootroot00000000000000// Package lbaas provides information and interaction with the Load Balancer // as a Service extension for the OpenStack Networking service. package lbaas golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/lbaas/members/000077500000000000000000000000001367513235700325775ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/lbaas/members/doc.go000066400000000000000000000023161367513235700336750ustar00rootroot00000000000000/* 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.go000066400000000000000000000107251367513235700347270ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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 } resp, err := c.Post(rootURL(c), b, &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get retrieves a particular pool member based on its unique ID. func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { resp, err := c.Get(resourceURL(c, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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 } resp, err := c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201, 202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete will permanently delete a particular member based on its unique ID. func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { resp, err := c.Delete(resourceURL(c, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } results.go000066400000000000000000000056411367513235700345560ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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/000077500000000000000000000000001367513235700341755ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/lbaas/membersdoc.go000066400000000000000000000000461367513235700352710ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/lbaas/members/testing// members unit tests package testing requests_test.go000066400000000000000000000142701367513235700374420ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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.go000066400000000000000000000005221367513235700340330ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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) } golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/lbaas/monitors/000077500000000000000000000000001367513235700330175ustar00rootroot00000000000000doc.go000066400000000000000000000023611367513235700340360ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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.go000066400000000000000000000210231367513235700351400ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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 } resp, err := c.Post(rootURL(c), b, &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get retrieves a particular health monitor based on its unique ID. func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { resp, err := c.Get(resourceURL(c, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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 } resp, err := c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete will permanently delete a particular monitor based on its unique ID. func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { resp, err := c.Delete(resourceURL(c, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } results.go000066400000000000000000000110761367513235700347750ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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/000077500000000000000000000000001367513235700344155ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/lbaas/monitorsdoc.go000066400000000000000000000000471367513235700355120ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/lbaas/monitors/testing// monitors unit tests package testing requests_test.go000066400000000000000000000176071367513235700376710ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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.go000066400000000000000000000005331367513235700342550ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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) } golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/lbaas/pools/000077500000000000000000000000001367513235700323015ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/lbaas/pools/doc.go000066400000000000000000000034151367513235700334000ustar00rootroot00000000000000/* 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.go000066400000000000000000000150701367513235700344270ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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 } resp, err := c.Post(rootURL(c), b, &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get retrieves a particular pool based on its unique ID. func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { resp, err := c.Get(resourceURL(c, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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 } resp, err := c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete will permanently delete a particular pool based on its unique ID. func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { resp, err := c.Delete(resourceURL(c, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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}} resp, err := c.Post(associateURL(c, poolID), b, &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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) { resp, err := c.Delete(disassociateURL(c, poolID, monitorID), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } results.go000066400000000000000000000075611367513235700342630ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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 } golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/lbaas/pools/testing/000077500000000000000000000000001367513235700337565ustar00rootroot00000000000000doc.go000066400000000000000000000000441367513235700347710ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/lbaas/pools/testing// pools unit tests package testing requests_test.go000066400000000000000000000215131367513235700371420ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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 } } `) }) var name = "SuperPool" options := pools.UpdateOpts{Name: &name, 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) } golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/lbaas/pools/urls.go000066400000000000000000000012331367513235700336140ustar00rootroot00000000000000package 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) } golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/lbaas/vips/000077500000000000000000000000001367513235700321265ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/lbaas/vips/doc.go000066400000000000000000000026611367513235700332270ustar00rootroot00000000000000/* 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.go000066400000000000000000000156371367513235700342650ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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 } resp, err := c.Post(rootURL(c), b, &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get retrieves a particular virtual IP based on its unique ID. func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { resp, err := c.Get(resourceURL(c, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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 } resp, err := c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete will permanently delete a particular virtual IP based on its unique ID. func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { resp, err := c.Delete(resourceURL(c, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } results.go000066400000000000000000000124451367513235700341050ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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"` } 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 } golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/lbaas/vips/testing/000077500000000000000000000000001367513235700336035ustar00rootroot00000000000000doc.go000066400000000000000000000000431367513235700346150ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/lbaas/vips/testing// vips unit tests package testing requests_test.go000066400000000000000000000241411367513235700367670ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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) } golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/lbaas/vips/urls.go000066400000000000000000000005141367513235700334420ustar00rootroot00000000000000package 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) } golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/lbaas_v2/000077500000000000000000000000001367513235700315545ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/lbaas_v2/doc.go000066400000000000000000000002441367513235700326500ustar00rootroot00000000000000// 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.12.0/openstack/networking/v2/extensions/lbaas_v2/l7policies/000077500000000000000000000000001367513235700336265ustar00rootroot00000000000000doc.go000066400000000000000000000060721367513235700346500ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/lbaas_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.go000066400000000000000000000316771367513235700357670ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/lbaas_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"` // TenantID is the UUID of the tenant who owns the L7 policy in octavia. // Only administrative users can specify a project UUID other than their own. TenantID string `json:"tenant_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"` // The administrative state of the Loadbalancer. A valid value is true (UP) // or false (DOWN). AdminStateUp *bool `json:"admin_state_up,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 } resp, err := c.Post(rootURL(c), b, &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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"` Description string `q:"description"` ListenerID string `q:"listener_id"` Action string `q:"action"` TenantID string `q:"tenant_id"` RedirectPoolID string `q:"redirect_pool_id"` RedirectURL string `q:"redirect_url"` Position int32 `q:"position"` AdminStateUp bool `q:"admin_state_up"` 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) { resp, err := c.Get(resourceURL(c, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete will permanently delete a particular l7policy based on its unique ID. func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { resp, err := c.Delete(resourceURL(c, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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"` // The administrative state of the Loadbalancer. A valid value is true (UP) // or false (DOWN). AdminStateUp *bool `json:"admin_state_up,omitempty"` } // ToL7PolicyUpdateMap builds a request body from UpdateOpts. func (opts UpdateOpts) ToL7PolicyUpdateMap() (map[string]interface{}, error) { b, err := gophercloud.BuildRequestBody(opts, "l7policy") if err != nil { return nil, err } m := b["l7policy"].(map[string]interface{}) if m["redirect_pool_id"] == "" { m["redirect_pool_id"] = nil } if m["redirect_url"] == "" { m["redirect_url"] = nil } return b, nil } // 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 } resp, err := c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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"` // TenantID is the UUID of the tenant who owns the rule in octavia. // Only administrative users can specify a project UUID other than their own. TenantID string `json:"tenant_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"` // The administrative state of the Loadbalancer. A valid value is true (UP) // or false (DOWN). AdminStateUp *bool `json:"admin_state_up,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 } resp, err := c.Post(ruleRootURL(c, policyID), b, &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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"` TenantID string `q:"tenant_id"` CompareType CompareType `q:"compare_type"` Value string `q:"value"` Key string `q:"key"` Invert bool `q:"invert"` AdminStateUp bool `q:"admin_state_up"` 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) { resp, err := c.Get(ruleResourceURL(c, policyID, ruleID), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // DeleteRule will remove a Rule from a particular L7Policy. func DeleteRule(c *gophercloud.ServiceClient, policyID string, ruleID string) (r DeleteRuleResult) { resp, err := c.Delete(ruleResourceURL(c, policyID, ruleID), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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"` // The administrative state of the Loadbalancer. A valid value is true (UP) // or false (DOWN). AdminStateUp *bool `json:"admin_state_up,omitempty"` } // ToRuleUpdateMap builds a request body from UpdateRuleOpts. func (opts UpdateRuleOpts) ToRuleUpdateMap() (map[string]interface{}, error) { b, err := gophercloud.BuildRequestBody(opts, "rule") if err != nil { return nil, err } if m := b["rule"].(map[string]interface{}); m["key"] == "" { m["key"] = nil } return b, nil } // 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 } resp, err := c.Put(ruleResourceURL(c, policyID, ruleID), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201, 202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } results.go000066400000000000000000000173551367513235700356120ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/lbaas_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"` // TenantID is the UUID of the tenant who owns the L7 policy in octavia. // Only administrative users can specify a project UUID other than their own. TenantID string `json:"tenant_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"` // The provisioning status of the L7 policy. // This value is ACTIVE, PENDING_* or ERROR. // This field seems to only be returned during a call to a load balancer's /status // see: https://github.com/gophercloud/gophercloud/issues/1362 ProvisioningStatus string `json:"provisioning_status"` // The operating status of the L7 policy. // This field seems to only be returned during a call to a load balancer's /status // see: https://github.com/gophercloud/gophercloud/issues/1362 OperatingStatus string `json:"operating_status"` // 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"` // TenantID is the UUID of the tenant who owns the rule in octavia. // Only administrative users can specify a project UUID other than their own. TenantID string `json:"tenant_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"` // The provisioning status of the L7 rule. // This value is ACTIVE, PENDING_* or ERROR. // This field seems to only be returned during a call to a load balancer's /status // see: https://github.com/gophercloud/gophercloud/issues/1362 ProvisioningStatus string `json:"provisioning_status"` // The operating status of the L7 policy. // This field seems to only be returned during a call to a load balancer's /status // see: https://github.com/gophercloud/gophercloud/issues/1362 OperatingStatus string `json:"operating_status"` } 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/000077500000000000000000000000001367513235700352245ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/lbaas_v2/l7policiesdoc.go000066400000000000000000000000511367513235700363140ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/lbaas_v2/l7policies/testing// l7policies unit tests package testing fixtures.go000066400000000000000000000333571367513235700374370ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/lbaas_v2/l7policies/testingpackage testing import ( "fmt" "net/http" "testing" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_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, "tenant_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: "", TenantID: "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: "", TenantID: "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", TenantID: "e3cd678b11784734bc366148aa37580e", RedirectPoolID: "", RedirectURL: "http://www.new-example.com", AdminStateUp: true, Rules: []l7policies.Rule{}, } L7PolicyNullRedirectURLUpdated = 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", TenantID: "e3cd678b11784734bc366148aa37580e", RedirectPoolID: "", RedirectURL: "", AdminStateUp: true, Rules: []l7policies.Rule{}, } RulePath = l7policies.Rule{ ID: "16621dbb-a736-4888-a57a-3ecd53df784c", RuleType: "PATH", CompareType: "REGEX", Value: "/images*", TenantID: "e3cd678b11784734bc366148aa37580e", Key: "", Invert: true, AdminStateUp: true, } RuleHostName = l7policies.Rule{ ID: "d24521a0-df84-4468-861a-a531af116d1e", RuleType: "HOST_NAME", CompareType: "EQUAL_TO", Value: "www.example.com", TenantID: "e3cd678b11784734bc366148aa37580e", Key: "", Invert: false, AdminStateUp: true, } RuleUpdated = l7policies.Rule{ ID: "16621dbb-a736-4888-a57a-3ecd53df784c", RuleType: "PATH", CompareType: "REGEX", Value: "/images/special*", TenantID: "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": [], "tenant_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": [], "tenant_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, "tenant_id": "e3cd678b11784734bc366148aa37580e", "id": "8a1412f0-4c32-4257-8b07-af4770b604fd", "name": "NewL7PolicyName", "rules": [] } } ` // PostUpdateL7PolicyNullRedirectURLBody is the canned response body of a Update request // on an existing l7policy with a null redirect_url . const PostUpdateL7PolicyNullRedirectURLBody = ` { "l7policy": { "listener_id": "023f2e34-7806-443b-bfae-16c324569a3d", "description": "Redirect requests to example.com", "admin_state_up": true, "redirect_pool_id": null, "redirect_url": null, "action": "REDIRECT_TO_URL", "position": 1, "tenant_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) }) } // HandleL7PolicyUpdateNullRedirectURLSuccessfully sets up the test server to respond to a l7policy Update request. func HandleL7PolicyUpdateNullRedirectURLSuccessfully(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", "redirect_url": null } }`) fmt.Fprintf(w, PostUpdateL7PolicyNullRedirectURLBody) }) } // SingleRuleBody is the canned body of a Get request on an existing rule. const SingleRuleBody = ` { "rule": { "compare_type": "REGEX", "invert": true, "admin_state_up": true, "value": "/images*", "key": null, "tenant_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": true, "admin_state_up": true, "value": "/images*", "key": null, "tenant_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, "tenant_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, "tenant_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", "invert": false, "key": null, "type": "PATH", "value": "/images/special*" } }`) fmt.Fprintf(w, PostUpdateRuleBody) }) } requests_test.go000066400000000000000000000211321367513235700404640ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/lbaas_v2/l7policies/testingpackage testing import ( "testing" fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/l7policies" "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" redirectURL := "http://www.new-example.com" actual, err := l7policies.Update(client, "8a1412f0-4c32-4257-8b07-af4770b604fd", l7policies.UpdateOpts{ Name: &newName, Action: l7policies.ActionRedirectToURL, RedirectURL: &redirectURL, }).Extract() if err != nil { t.Fatalf("Unexpected Update error: %v", err) } th.CheckDeepEquals(t, L7PolicyUpdated, *actual) } func TestUpdateL7PolicyNullRedirectURL(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleL7PolicyUpdateNullRedirectURLSuccessfully(t) client := fake.ServiceClient() newName := "NewL7PolicyName" redirectURL := "" actual, err := l7policies.Update(client, "8a1412f0-4c32-4257-8b07-af4770b604fd", l7policies.UpdateOpts{ Name: &newName, RedirectURL: &redirectURL, }).Extract() if err != nil { t.Fatalf("Unexpected Update error: %v", err) } th.CheckDeepEquals(t, L7PolicyNullRedirectURLUpdated, *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() invert := false key := "" 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*", Invert: &invert, Key: &key, }).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.go000066400000000000000000000012501367513235700350610ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/lbaas_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.12.0/openstack/networking/v2/extensions/lbaas_v2/listeners/000077500000000000000000000000001367513235700335645ustar00rootroot00000000000000doc.go000066400000000000000000000026761367513235700346140ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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.go000066400000000000000000000167301367513235700357160ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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" ProtocolTerminatedHTTPS Protocol = "TERMINATED_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 } resp, err := c.Post(rootURL(c), b, &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get retrieves a particular Listeners based on its unique ID. func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { resp, err := c.Get(resourceURL(c, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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"` // 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"` } // ToListenerUpdateMap builds a request body from UpdateOpts. func (opts UpdateOpts) ToListenerUpdateMap() (map[string]interface{}, error) { b, err := gophercloud.BuildRequestBody(opts, "listener") if err != nil { return nil, err } if m := b["listener"].(map[string]interface{}); m["default_pool_id"] == "" { m["default_pool_id"] = nil } return b, nil } // Update is an operation which modifies the attributes of the specified // Listener. func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToListenerUpdateMap() if err != nil { r.Err = err return } resp, err := c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete will permanently delete a particular Listeners based on its unique ID. func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { resp, err := c.Delete(resourceURL(c, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } results.go000066400000000000000000000107341367513235700355420ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/lbaas_v2/listenerspackage listeners import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/l7policies" "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"` // L7policies are the L7 policies which are part of this listener. // This field seems to only be returned during a call to a load balancer's /status // see: https://github.com/gophercloud/gophercloud/issues/1352 L7Policies []l7policies.L7Policy `json:"l7policies"` // 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/000077500000000000000000000000001367513235700351625ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/lbaas_v2/listenersdoc.go000066400000000000000000000000501367513235700362510ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/lbaas_v2/listeners/testing// listeners unit tests package testing fixtures.go000066400000000000000000000202621367513235700373640ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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", "default_pool_id": null, "connection_limit": 1001 } }`) fmt.Fprintf(w, PostUpdateListenerBody) }) } requests_test.go000066400000000000000000000076661367513235700404420ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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 name := "NewListenerName" defaultPoolID := "" actual, err := listeners.Update(client, "4ec89087-d057-4e2c-911f-60a3b47ee304", listeners.UpdateOpts{ Name: &name, ConnLimit: &i1001, DefaultPoolID: &defaultPoolID, }).Extract() if err != nil { t.Fatalf("Unexpected Update error: %v", err) } th.CheckDeepEquals(t, ListenerUpdated, *actual) } urls.go000066400000000000000000000005311367513235700350200ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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/000077500000000000000000000000001367513235700342675ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/lbaas_v2doc.go000066400000000000000000000034361367513235700353710ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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", FlavorID: "60df399a-ee85-11e9-81b4-2a2ae2dbcce4", 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) } 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) } */ package loadbalancers requests.go000066400000000000000000000171431367513235700364770ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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"` FlavorID string `q:"flavor_id"` 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. FlavorID string `json:"flavor_id,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 } resp, err := c.Post(rootURL(c), b, &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get retrieves a particular Loadbalancer based on its unique ID. func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { resp, err := c.Get(resourceURL(c, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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 UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToLoadBalancerUpdateMap() if err != nil { r.Err = err return } resp, err := c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete will permanently delete a particular LoadBalancer based on its // unique ID. func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { resp, err := c.Delete(resourceURL(c, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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)) resp, err := c.Delete(u, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // GetStatuses will return the status of a particular LoadBalancer. func GetStatuses(c *gophercloud.ServiceClient, id string) (r GetStatusesResult) { resp, err := c.Get(statusRootURL(c, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // GetStats will return the shows the current statistics of a particular LoadBalancer. func GetStats(c *gophercloud.ServiceClient, id string) (r StatsResult) { resp, err := c.Get(statisticsRootURL(c, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } results.go000066400000000000000000000130211367513235700363140ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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/openstack/networking/v2/extensions/lbaas_v2/pools" "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. FlavorID string `json:"flavor_id"` // The name of the provider. Provider string `json:"provider"` // Listeners are the listeners related to this Loadbalancer. Listeners []listeners.Listener `json:"listeners"` // Pools are the pools related to this Loadbalancer. Pools []pools.Pool `json:"pools"` } // 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 } testing/000077500000000000000000000000001367513235700357445ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/lbaas_v2/loadbalancersdoc.go000066400000000000000000000000541367513235700370370ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/testing// loadbalancers unit tests package testing fixtures.go000066400000000000000000000262451367513235700401550ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/testingpackage testing import ( "fmt" "net/http" "testing" "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" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) // 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_id": "60df399a-ee85-11e9-81b4-2a2ae2dbcce4", "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_id": "bba40eb2-ee8c-11e9-81b4-2a2ae2dbcce4", "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_id": "bba40eb2-ee8c-11e9-81b4-2a2ae2dbcce4", "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_id": "bba40eb2-ee8c-11e9-81b4-2a2ae2dbcce4", "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", 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", FlavorID: "60df399a-ee85-11e9-81b4-2a2ae2dbcce4", 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", FlavorID: "bba40eb2-ee8c-11e9-81b4-2a2ae2dbcce4", 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", FlavorID: "bba40eb2-ee8c-11e9-81b4-2a2ae2dbcce4", 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_id": "bba40eb2-ee8c-11e9-81b4-2a2ae2dbcce4", "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, 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) }) } requests_test.go000066400000000000000000000120031367513235700412010ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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", FlavorID: "bba40eb2-ee8c-11e9-81b4-2a2ae2dbcce4", 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") th.AssertNoErr(t, res.Err) } func TestUpdateLoadbalancer(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleLoadbalancerUpdateSuccessfully(t) client := fake.ServiceClient() name := "NewLoadbalancerName" actual, err := loadbalancers.Update(client, "36e08a3e-a78f-4b40-a229-1e7e23eee1ab", loadbalancers.UpdateOpts{ Name: &name, }).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) } 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) } urls.go000066400000000000000000000012561367513235700356070ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/lbaas_v2/loadbalancerspackage loadbalancers import "github.com/gophercloud/gophercloud" const ( rootPath = "lbaas" resourcePath = "loadbalancers" statusPath = "statuses" 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 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) } golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/lbaas_v2/monitors/000077500000000000000000000000001367513235700334265ustar00rootroot00000000000000doc.go000066400000000000000000000027431367513235700344510ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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.go000066400000000000000000000213631367513235700355560ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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 } resp, err := c.Post(rootURL(c), b, &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get retrieves a particular Health Monitor based on its unique ID. func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { resp, err := c.Get(resourceURL(c, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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 } resp, err := c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete will permanently delete a particular Monitor based on its unique ID. func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { resp, err := c.Delete(resourceURL(c, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } results.go000066400000000000000000000114421367513235700354010ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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/000077500000000000000000000000001367513235700350245ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/lbaas_v2/monitorsdoc.go000066400000000000000000000000471367513235700361210ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/lbaas_v2/monitors/testing// monitors unit tests package testing fixtures.go000066400000000000000000000146621367513235700372350ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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.go000066400000000000000000000100501367513235700402610ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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() name := "NewHealthmonitorName" actual, err := monitors.Update(client, "5d4b5228-33b0-4e60-b225-9b727c1a20e7", monitors.UpdateOpts{ Name: &name, 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.go000066400000000000000000000005351367513235700346660ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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) } golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/lbaas_v2/pools/000077500000000000000000000000001367513235700327105ustar00rootroot00000000000000doc.go000066400000000000000000000051241367513235700337270ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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.ExtractPools(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" weight := 10 createOpts := pools.CreateMemberOpts{ Name: "db", SubnetID: "1981f108-3c48-48d2-b908-30f7d28532c9", Address: "10.0.2.11", ProtocolPort: 80, Weight: &weight, } 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" weight := 4 updateOpts := pools.UpdateMemberOpts{ Name: "new-name", Weight: &weight, } 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.go000066400000000000000000000320251367513235700350350ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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 } resp, err := c.Post(rootURL(c), b, &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get retrieves a particular pool based on its unique ID. func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { resp, err := c.Get(resourceURL(c, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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 } resp, err := c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete will permanently delete a particular pool based on its unique ID. func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { resp, err := c.Delete(resourceURL(c, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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 } resp, err := c.Post(memberRootURL(c, poolID), b, &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // GetMember retrieves a particular Pool Member based on its unique ID. func GetMember(c *gophercloud.ServiceClient, poolID string, memberID string) (r GetMemberResult) { resp, err := c.Get(memberResourceURL(c, poolID, memberID), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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 } resp, err := c.Put(memberResourceURL(c, poolID, memberID), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201, 202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // DisassociateMember will remove and disassociate a Member from a particular // Pool. func DeleteMember(c *gophercloud.ServiceClient, poolID string, memberID string) (r DeleteMemberResult) { resp, err := c.Delete(memberResourceURL(c, poolID, memberID), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } results.go000066400000000000000000000220551367513235700346650ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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"` // The operating status of the pool. // This field seems to only be returned during a call to a load balancer's /status // see: https://github.com/gophercloud/gophercloud/issues/1362 OperatingStatus string `json:"operating_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"` // The operating status of the member. // This field seems to only be returned during a call to a load balancer's /status // see: https://github.com/gophercloud/gophercloud/issues/1362 OperatingStatus string `json:"operating_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/000077500000000000000000000000001367513235700343065ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/lbaas_v2/poolsdoc.go000066400000000000000000000000441367513235700354000ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/lbaas_v2/pools/testing// pools unit tests package testing fixtures.go000066400000000000000000000313211367513235700365060ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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.go000066400000000000000000000162431367513235700375550ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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() name := "NewPoolName" actual, err := pools.Update(client, "c3741b06-df4d-4715-b142-276b6bce75ab", pools.UpdateOpts{ Name: &name, 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) weight := 10 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: &weight, }).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) weight := 4 client := fake.ServiceClient() name := "newMemberName" actual, err := pools.UpdateMember(client, "332abe93-f488-41ba-870b-2ac66be7f853", "2a280670-c202-4b0b-a562-34077415aabf", pools.UpdateMemberOpts{ Name: &name, Weight: &weight, }).Extract() if err != nil { t.Fatalf("Unexpected Update error: %v", err) } th.CheckDeepEquals(t, MemberUpdated, *actual) } urls.go000066400000000000000000000012441367513235700341460ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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, memberID string) string { return c.ServiceURL(rootPath, resourcePath, poolID, memberPath, memberID) } golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/mtu/000077500000000000000000000000001367513235700306705ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/mtu/requests.go000066400000000000000000000042211367513235700330710ustar00rootroot00000000000000package mtu import ( "fmt" "net/url" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack/networking/v2/networks" ) // ListOptsExt adds an MTU option to the base ListOpts. type ListOptsExt struct { networks.ListOptsBuilder // The maximum transmission unit (MTU) value to address fragmentation. // Minimum value is 68 for IPv4, and 1280 for IPv6. MTU int `q:"mtu"` } // 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.MTU > 0 { params.Add("mtu", fmt.Sprintf("%d", opts.MTU)) } q = &url.URL{RawQuery: params.Encode()} return q.String(), err } // CreateOptsExt adds an MTU option to the base Network CreateOpts. type CreateOptsExt struct { networks.CreateOptsBuilder // The maximum transmission unit (MTU) value to address fragmentation. // Minimum value is 68 for IPv4, and 1280 for IPv6. MTU int `json:"mtu,omitempty"` } // ToNetworkCreateMap adds an MTU 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.MTU == 0 { return base, nil } networkMap := base["network"].(map[string]interface{}) networkMap["mtu"] = opts.MTU return base, nil } // CreateOptsExt adds an MTU option to the base Network UpdateOpts. type UpdateOptsExt struct { networks.UpdateOptsBuilder // The maximum transmission unit (MTU) value to address fragmentation. // Minimum value is 68 for IPv4, and 1280 for IPv6. MTU int `json:"mtu,omitempty"` } // ToNetworkUpdateMap adds an MTU to the base network uptade options. func (opts UpdateOptsExt) ToNetworkUpdateMap() (map[string]interface{}, error) { base, err := opts.UpdateOptsBuilder.ToNetworkUpdateMap() if err != nil { return nil, err } if opts.MTU == 0 { return base, nil } networkMap := base["network"].(map[string]interface{}) networkMap["mtu"] = opts.MTU return base, nil } golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/mtu/results.go000066400000000000000000000004231367513235700327170ustar00rootroot00000000000000package mtu // NetworkMTUExt represents an extended form of a Network with additional MTU field. type NetworkMTUExt struct { // The maximum transmission unit (MTU) value to address fragmentation. // Minimum value is 68 for IPv4, and 1280 for IPv6. MTU int `json:"mtu"` } golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/mtu/testing/000077500000000000000000000000001367513235700323455ustar00rootroot00000000000000fixtures.go000066400000000000000000000024201367513235700344640ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/mtu/testingpackage testing // These fixtures are here instead of in the underlying networks package // because all network tests (including extensions) would have to // implement the NetworkMTUExt extention for create/update tests // to pass. const CreateRequest = ` { "network": { "name": "private", "admin_state_up": true, "mtu": 1500 } }` 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", "mtu": 1500 } }` const UpdateRequest = ` { "network": { "name": "new_network_name", "admin_state_up": false, "shared": true, "mtu": 1350 } }` 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", "mtu": 1350 } }` const ExpectedListOpts = "?id=d32019d3-bc6e-4319-9c1d-6722fc136a22&mtu=1500" requests_test.go000066400000000000000000000011051367513235700355240ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/mtu/testingpackage testing import ( "testing" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/mtu" "github.com/gophercloud/gophercloud/openstack/networking/v2/networks" th "github.com/gophercloud/gophercloud/testhelper" ) func TestListExternal(t *testing.T) { networkListOpts := networks.ListOpts{ ID: "d32019d3-bc6e-4319-9c1d-6722fc136a22", } listOpts := mtu.ListOptsExt{ ListOptsBuilder: networkListOpts, MTU: 1500, } actual, err := listOpts.ToNetworkListQuery() th.AssertNoErr(t, err) th.AssertEquals(t, ExpectedListOpts, actual) } results_test.go000066400000000000000000000100751367513235700353600ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/mtu/testingpackage testing import ( "fmt" "net/http" "testing" fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/mtu" "github.com/gophercloud/gophercloud/openstack/networking/v2/networks" nettest "github.com/gophercloud/gophercloud/openstack/networking/v2/networks/testing" th "github.com/gophercloud/gophercloud/testhelper" ) type NetworkMTU struct { networks.Network mtu.NetworkMTUExt } 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 NetworkWithMTUExt struct { networks.Network mtu.NetworkMTUExt } var actual []NetworkWithMTUExt 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, 1500, actual[0].MTU) } 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 NetworkMTU 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, 1500, s.MTU) } 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 networkCreateOpts := networks.CreateOpts{ Name: "private", AdminStateUp: &iTrue, } mtuCreateOpts := mtu.CreateOptsExt{ CreateOptsBuilder: &networkCreateOpts, MTU: 1500, } var s NetworkMTU err := networks.Create(fake.ServiceClient(), mtuCreateOpts).ExtractInto(&s) th.AssertNoErr(t, err) th.AssertEquals(t, "db193ab3-96e3-4cb3-8fc5-05f4296d0324", s.ID) th.AssertEquals(t, iTrue, s.AdminStateUp) th.AssertEquals(t, 1500, s.MTU) } 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 name := "new_network_name" networkUpdateOpts := networks.UpdateOpts{ Name: &name, AdminStateUp: &iFalse, Shared: &iTrue, } mtuUpdateOpts := mtu.UpdateOptsExt{ UpdateOptsBuilder: &networkUpdateOpts, MTU: 1350, } var s NetworkMTU err := networks.Update(fake.ServiceClient(), "4e8e5957-649f-477b-9e5b-f1f75b21c03c", mtuUpdateOpts).ExtractInto(&s) th.AssertNoErr(t, err) th.AssertEquals(t, "4e8e5957-649f-477b-9e5b-f1f75b21c03c", s.ID) th.AssertEquals(t, "new_network_name", s.Name) th.AssertEquals(t, iFalse, s.AdminStateUp) th.AssertEquals(t, iTrue, s.Shared) th.AssertEquals(t, 1350, s.MTU) } networkipavailabilities/000077500000000000000000000000001367513235700347315ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensionsdoc.go000066400000000000000000000014361367513235700360310ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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.go000066400000000000000000000040621367513235700371350ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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) { resp, err := c.Get(getURL(c, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } results.go000066400000000000000000000074561367513235700367750ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/networkipavailabilitiespackage networkipavailabilities import ( "encoding/json" "math/big" "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 string `json:"-"` // UsedIPs represents a number of used IP addresses in the network. UsedIPs string `json:"-"` } func (r *NetworkIPAvailability) UnmarshalJSON(b []byte) error { type tmp NetworkIPAvailability var s struct { tmp TotalIPs big.Int `json:"total_ips"` UsedIPs big.Int `json:"used_ips"` } err := json.Unmarshal(b, &s) if err != nil { return err } *r = NetworkIPAvailability(s.tmp) r.TotalIPs = s.TotalIPs.String() r.UsedIPs = s.UsedIPs.String() return err } // 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 string `json:"-"` // UsedIPs represents a number of used IP addresses in the subnet. UsedIPs string `json:"-"` } func (r *SubnetIPAvailability) UnmarshalJSON(b []byte) error { type tmp SubnetIPAvailability var s struct { tmp TotalIPs big.Int `json:"total_ips"` UsedIPs big.Int `json:"used_ips"` } err := json.Unmarshal(b, &s) if err != nil { return err } *r = SubnetIPAvailability(s.tmp) r.TotalIPs = s.TotalIPs.String() r.UsedIPs = s.UsedIPs.String() return err } // 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/000077500000000000000000000000001367513235700364065ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/networkipavailabilitiesdoc.go000066400000000000000000000000661367513235700375040ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/networkipavailabilities/testing// networkipavailabilities unit tests package testing fixtures.go000066400000000000000000000105051367513235700406070ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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": "fdbc:bf53:567e::/64", "ip_version": 6, "subnet_id": "497ac4d3-0b92-42cf-82de-71302ab2b656", "subnet_name": "ipv6-private-subnet", "total_ips": 18446744073709552000, "used_ips": 2 }, { "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: "ipv6-private-subnet", CIDR: "fdbc:bf53:567e::/64", IPVersion: int(gophercloud.IPv6), TotalIPs: "18446744073709552000", UsedIPs: "2", }, { 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.go000066400000000000000000000050701367513235700416510ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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 err := 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 }) th.AssertNoErr(t, err) 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.go000066400000000000000000000011001367513235700362350ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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) } golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/portsbinding/000077500000000000000000000000001367513235700325655ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/portsbinding/doc.go000066400000000000000000000002331367513235700336570ustar00rootroot00000000000000// Package portsbinding provides information and interaction with the port // binding extension for the OpenStack Networking service. package portsbinding requests.go000066400000000000000000000053401367513235700347120ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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]interface{} `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]interface{} `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 != nil { port["binding:host_id"] = *opts.HostID } if opts.VNICType != "" { port["binding:vnic_type"] = opts.VNICType } if opts.Profile != nil { if len(opts.Profile) == 0 { // send null instead of the empty json object ("{}") port["binding:profile"] = nil } else { port["binding:profile"] = opts.Profile } } return base, nil } results.go000066400000000000000000000015621367513235700345420ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/portsbindingpackage portsbinding // 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]interface{} `json:"binding:profile"` } testing/000077500000000000000000000000001367513235700341635ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/portsbindingdoc.go000066400000000000000000000000541367513235700352560ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/portsbinding/testing// portsbindings unit tests package testing fixtures.go000066400000000000000000000100641367513235700363640ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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.go000066400000000000000000000123571367513235700374340ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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 } name := "new_port_name" portUpdateOpts := ports.UpdateOpts{ Name: &name, FixedIPs: []ports.IP{ {SubnetID: "a0304c3a-4f08-4c43-88af-d796509c97d2", IPAddress: "10.0.0.3"}, }, SecurityGroups: &[]string{"f0ac4394-7e4a-4409-9701-ba8be283dbc3"}, } hostID := "HOST1" updateOpts := portsbinding.UpdateOptsExt{ UpdateOptsBuilder: portUpdateOpts, HostID: &hostID, 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") } golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/portsecurity/000077500000000000000000000000001367513235700326375ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/portsecurity/doc.go000066400000000000000000000065531367513235700337440ustar00rootroot00000000000000/* 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.go000066400000000000000000000055341367513235700347710ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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.go000066400000000000000000000003031367513235700346040ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/portsecuritypackage portsecurity type PortSecurityExt struct { // PortSecurityEnabled specifies whether port security is enabled or // disabled. PortSecurityEnabled bool `json:"port_security_enabled"` } golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/provider/000077500000000000000000000000001367513235700317155ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/provider/doc.go000066400000000000000000000043371367513235700330200ustar00rootroot00000000000000/* 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 golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/provider/requests.go000066400000000000000000000012751367513235700341240ustar00rootroot00000000000000package 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 } golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/provider/results.go000066400000000000000000000040231367513235700337440ustar00rootroot00000000000000package 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 } golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/provider/testing/000077500000000000000000000000001367513235700333725ustar00rootroot00000000000000doc.go000066400000000000000000000000471367513235700344100ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/provider/testing// provider unit tests package testing results_test.go000066400000000000000000000143501367513235700364050ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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 name := "new_network_name" options := networks.UpdateOpts{Name: &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) } golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/qos/000077500000000000000000000000001367513235700306655ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/qos/policies/000077500000000000000000000000001367513235700324745ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/qos/policies/doc.go000066400000000000000000000135701367513235700335760ustar00rootroot00000000000000/* Package policies provides information and interaction with the QoS policy extension for the OpenStack Networking service. Example to Get a Port with a QoS policy var portWithQoS struct { ports.Port policies.QoSPolicyExt } portID := "46d4bfb9-b26e-41f3-bd2e-e6dcc1ccedb2" err = ports.Get(client, portID).ExtractInto(&portWithQoS) if err != nil { log.Fatal(err) } fmt.Printf("Port: %+v\n", portWithQoS) Example to Create a Port with a QoS policy var portWithQoS struct { ports.Port policies.QoSPolicyExt } policyID := "d6ae28ce-fcb5-4180-aa62-d260a27e09ae" networkID := "7069db8d-e817-4b39-a654-d2dd76e73d36" portCreateOpts := ports.CreateOpts{ NetworkID: networkID, } createOpts := policies.PortCreateOptsExt{ CreateOptsBuilder: portCreateOpts, QoSPolicyID: policyID, } err = ports.Create(client, createOpts).ExtractInto(&portWithQoS) if err != nil { panic(err) } fmt.Printf("Port: %+v\n", portWithQoS) Example to Add a QoS policy to an existing Port var portWithQoS struct { ports.Port policies.QoSPolicyExt } portUpdateOpts := ports.UpdateOpts{} policyID := "d6ae28ce-fcb5-4180-aa62-d260a27e09ae" updateOpts := policies.PortUpdateOptsExt{ UpdateOptsBuilder: portUpdateOpts, QoSPolicyID: &policyID, } err := ports.Update(client, "65c0ee9f-d634-4522-8954-51021b570b0d", updateOpts).ExtractInto(&portWithQoS) if err != nil { panic(err) } fmt.Printf("Port: %+v\n", portWithQoS) Example to Delete a QoS policy from the existing Port var portWithQoS struct { ports.Port policies.QoSPolicyExt } portUpdateOpts := ports.UpdateOpts{} policyID := "" updateOpts := policies.PortUpdateOptsExt{ UpdateOptsBuilder: portUpdateOpts, QoSPolicyID: &policyID, } err := ports.Update(client, "65c0ee9f-d634-4522-8954-51021b570b0d", updateOpts).ExtractInto(&portWithQoS) if err != nil { panic(err) } fmt.Printf("Port: %+v\n", portWithQoS) Example to Get a Network with a QoS policy var networkWithQoS struct { networks.Network policies.QoSPolicyExt } networkID := "46d4bfb9-b26e-41f3-bd2e-e6dcc1ccedb2" err = networks.Get(client, networkID).ExtractInto(&networkWithQoS) if err != nil { log.Fatal(err) } fmt.Printf("Network: %+v\n", networkWithQoS) Example to Create a Network with a QoS policy var networkWithQoS struct { networks.Network policies.QoSPolicyExt } policyID := "d6ae28ce-fcb5-4180-aa62-d260a27e09ae" networkID := "7069db8d-e817-4b39-a654-d2dd76e73d36" networkCreateOpts := networks.CreateOpts{ NetworkID: networkID, } createOpts := policies.NetworkCreateOptsExt{ CreateOptsBuilder: networkCreateOpts, QoSPolicyID: policyID, } err = networks.Create(client, createOpts).ExtractInto(&networkWithQoS) if err != nil { panic(err) } fmt.Printf("Network: %+v\n", networkWithQoS) Example to add a QoS policy to an existing Network var networkWithQoS struct { networks.Network policies.QoSPolicyExt } networkUpdateOpts := networks.UpdateOpts{} policyID := "d6ae28ce-fcb5-4180-aa62-d260a27e09ae" updateOpts := policies.NetworkUpdateOptsExt{ UpdateOptsBuilder: networkUpdateOpts, QoSPolicyID: &policyID, } err := networks.Update(client, "65c0ee9f-d634-4522-8954-51021b570b0d", updateOpts).ExtractInto(&networkWithQoS) if err != nil { panic(err) } fmt.Printf("Network: %+v\n", networkWithQoS) Example to delete a QoS policy from the existing Network var networkWithQoS struct { networks.Network policies.QoSPolicyExt } networkUpdateOpts := networks.UpdateOpts{} policyID := "" updateOpts := policies.NetworkUpdateOptsExt{ UpdateOptsBuilder: networkUpdateOpts, QoSPolicyID: &policyID, } err := networks.Update(client, "65c0ee9f-d634-4522-8954-51021b570b0d", updateOpts).ExtractInto(&networkWithQoS) if err != nil { panic(err) } fmt.Printf("Network: %+v\n", networkWithQoS) Example to List QoS policies shared := true listOpts := policies.ListOpts{ Name: "shared-policy", Shared: &shared, } 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 Get a specific QoS policy policyID := "30a57f4a-336b-4382-8275-d708babd2241" policy, err := policies.Get(networkClient, policyID).Extract() if err != nil { panic(err) } fmt.Printf("%+v\n", policy) Example to Create a QoS policy createOpts := policies.CreateOpts{ Name: "shared-default-policy", Shared: true, IsDefault: true, } policy, err := policies.Create(networkClient, createOpts).Extract() if err != nil { panic(err) } fmt.Printf("%+v\n", policy) Example to Update a QoS policy shared := true isDefault := false opts := policies.UpdateOpts{ Name: "new-name", Shared: &shared, IsDefault: &isDefault, } policyID := "30a57f4a-336b-4382-8275-d708babd2241" policy, err := policies.Update(networkClient, policyID, opts).Extract() if err != nil { panic(err) } fmt.Printf("%+v\n", policy) Example to Delete a QoS policy policyID := "30a57f4a-336b-4382-8275-d708babd2241" err := policies.Delete(networkClient, policyID).ExtractErr() if err != nil { panic(err) } */ package policies requests.go000066400000000000000000000205231367513235700346210ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/qos/policiespackage policies import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack/networking/v2/networks" "github.com/gophercloud/gophercloud/openstack/networking/v2/ports" "github.com/gophercloud/gophercloud/pagination" ) // PortCreateOptsExt adds QoS options to the base ports.CreateOpts. type PortCreateOptsExt struct { ports.CreateOptsBuilder // QoSPolicyID represents an associated QoS policy. QoSPolicyID string `json:"qos_policy_id,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.QoSPolicyID != "" { port["qos_policy_id"] = opts.QoSPolicyID } return base, nil } // PortUpdateOptsExt adds QoS options to the base ports.UpdateOpts. type PortUpdateOptsExt struct { ports.UpdateOptsBuilder // QoSPolicyID represents an associated QoS policy. // Setting it to a pointer of an empty string will remove associated QoS policy from port. QoSPolicyID *string `json:"qos_policy_id,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.QoSPolicyID != nil { qosPolicyID := *opts.QoSPolicyID if qosPolicyID != "" { port["qos_policy_id"] = qosPolicyID } else { port["qos_policy_id"] = nil } } return base, nil } // NetworkCreateOptsExt adds QoS options to the base networks.CreateOpts. type NetworkCreateOptsExt struct { networks.CreateOptsBuilder // QoSPolicyID represents an associated QoS policy. QoSPolicyID string `json:"qos_policy_id,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.QoSPolicyID != "" { network["qos_policy_id"] = opts.QoSPolicyID } return base, nil } // NetworkUpdateOptsExt adds QoS options to the base networks.UpdateOpts. type NetworkUpdateOptsExt struct { networks.UpdateOptsBuilder // QoSPolicyID represents an associated QoS policy. // Setting it to a pointer of an empty string will remove associated QoS policy from network. QoSPolicyID *string `json:"qos_policy_id,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.QoSPolicyID != nil { qosPolicyID := *opts.QoSPolicyID if qosPolicyID != "" { network["qos_policy_id"] = qosPolicyID } else { network["qos_policy_id"] = nil } } return base, nil } // PolicyListOptsBuilder allows extensions to add additional parameters to the List request. type PolicyListOptsBuilder interface { ToPolicyListQuery() (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 Policy attributes you want to see returned. // SortKey allows you to sort by a particular Policy 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"` TenantID string `q:"tenant_id"` ProjectID string `q:"project_id"` Name string `q:"name"` Description string `q:"description"` RevisionNumber *int `q:"revision_number"` IsDefault *bool `q:"is_default"` Shared *bool `q:"shared"` Limit int `q:"limit"` Marker string `q:"marker"` SortKey string `q:"sort_key"` SortDir string `q:"sort_dir"` Tags string `q:"tags"` TagsAny string `q:"tags-any"` NotTags string `q:"not-tags"` NotTagsAny string `q:"not-tags-any"` } // 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 // Policy. It accepts a ListOpts struct, which allows you to filter and sort // the returned collection for greater efficiency. func List(c *gophercloud.ServiceClient, opts PolicyListOptsBuilder) pagination.Pager { url := listURL(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}} }) } // Get retrieves a specific QoS policy based on its ID. func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { resp, err := c.Get(getURL(c, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // CreateOptsBuilder allows to add additional parameters to the // Create request. type CreateOptsBuilder interface { ToPolicyCreateMap() (map[string]interface{}, error) } // CreateOpts specifies parameters of a new QoS policy. type CreateOpts struct { // Name is the human-readable name of the QoS policy. Name string `json:"name"` // 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"` // Shared indicates whether this QoS policy is shared across all projects. Shared bool `json:"shared,omitempty"` // Description is the human-readable description for the QoS policy. Description string `json:"description,omitempty"` // IsDefault indicates if this QoS policy is default policy or not. IsDefault bool `json:"is_default,omitempty"` } // ToPolicyCreateMap constructs a request body from CreateOpts. func (opts CreateOpts) ToPolicyCreateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "policy") } // Create requests the creation of a new QoS policy on the server. func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToPolicyCreateMap() if err != nil { r.Err = err return } resp, err := client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{201}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // UpdateOptsBuilder allows extensions to add additional parameters to the // Update request. type UpdateOptsBuilder interface { ToPolicyUpdateMap() (map[string]interface{}, error) } // UpdateOpts represents options used to update a QoS policy. type UpdateOpts struct { // Name is the human-readable name of the QoS policy. Name string `json:"name,omitempty"` // Shared indicates whether this QoS policy is shared across all projects. Shared *bool `json:"shared,omitempty"` // Description is the human-readable description for the QoS policy. Description *string `json:"description,omitempty"` // IsDefault indicates if this QoS policy is default policy or not. IsDefault *bool `json:"is_default,omitempty"` } // ToPolicyUpdateMap builds a request body from UpdateOpts. func (opts UpdateOpts) ToPolicyUpdateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "policy") } // Update accepts a UpdateOpts struct and updates an existing policy using the // values provided. func Update(c *gophercloud.ServiceClient, policyID string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToPolicyUpdateMap() if err != nil { r.Err = err return } resp, err := c.Put(updateURL(c, policyID), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete accepts a unique ID and deletes the QoS policy associated with it. func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { resp, err := c.Delete(deleteURL(c, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } results.go000066400000000000000000000071001367513235700344430ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/qos/policiespackage policies import ( "time" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // QoSPolicyExt represents additional resource attributes available with the QoS extension. type QoSPolicyExt struct { // QoSPolicyID represents an associated QoS policy. QoSPolicyID string `json:"qos_policy_id"` } type commonResult struct { gophercloud.Result } // GetResult represents the result of a get operation. Call its Extract // method to interpret it as a QoS policy. type GetResult struct { commonResult } // CreateResult represents the result of a Create operation. Call its Extract // method to interpret it as a QoS policy. type CreateResult struct { commonResult } // UpdateResult represents the result of a Create operation. Call its Extract // method to interpret it as a QoS policy. 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 } // Extract is a function that accepts a result and extracts a QoS policy resource. func (r commonResult) Extract() (*Policy, error) { var s struct { Policy *Policy `json:"policy"` } err := r.ExtractInto(&s) return s.Policy, err } // Policy represents a QoS policy. type Policy struct { // ID is the id of the policy. ID string `json:"id"` // Name is the human-readable name of the policy. Name string `json:"name"` // 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 the policy has been created. CreatedAt time.Time `json:"created_at"` // UpdatedAt is the time at which the policy has been created. UpdatedAt time.Time `json:"updated_at"` // IsDefault indicates if the policy is default policy or not. IsDefault bool `json:"is_default"` // Description is thehuman-readable description for the resource. Description string `json:"description"` // Shared indicates whether this policy is shared across all projects. Shared bool `json:"shared"` // RevisionNumber represents revision number of the policy. RevisionNumber int `json:"revision_number"` // Rules represents QoS rules of the policy. Rules []map[string]interface{} `json:"rules"` // Tags optionally set via extensions/attributestags Tags []string `json:"tags"` } // PolicyPage stores a single page of Policies from a List() API call. type PolicyPage struct { pagination.LinkedPageBase } // NextPageURL is invoked when a paginated collection of 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:"policies_links"` } err := r.ExtractInto(&s) if err != nil { return "", err } return gophercloud.ExtractNextURL(s.Links) } // IsEmpty checks whether a PolicyPage is empty. func (r PolicyPage) IsEmpty() (bool, error) { is, err := ExtractPolicies(r) return len(is) == 0, err } // ExtractPolicies accepts a PolicyPage, and extracts the elements into a slice of Policies. func ExtractPolicies(r pagination.Page) ([]Policy, error) { var s []Policy err := ExtractPolicysInto(r, &s) return s, err } // ExtractPoliciesInto extracts the elements into a slice of RBAC Policy structs. func ExtractPolicysInto(r pagination.Page, v interface{}) error { return r.(PolicyPage).Result.ExtractIntoSlicePtr(v, "policies") } testing/000077500000000000000000000000001367513235700340725ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/qos/policiesfixtures.go000066400000000000000000000167451367513235700363070ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/qos/policies/testingpackage testing import ( "time" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/qos/policies" ) const GetPortResponse = ` { "port": { "id": "65c0ee9f-d634-4522-8954-51021b570b0d", "qos_policy_id": "591e0597-39a6-4665-8149-2111d8de9a08" } } ` const CreatePortRequest = ` { "port": { "network_id": "a87cc70a-3e15-4acf-8205-9b711a3531b7", "qos_policy_id": "591e0597-39a6-4665-8149-2111d8de9a08" } } ` const CreatePortResponse = ` { "port": { "network_id": "a87cc70a-3e15-4acf-8205-9b711a3531b7", "tenant_id": "d6700c0c9ffa4f1cb322cd4a1f3906fa", "id": "65c0ee9f-d634-4522-8954-51021b570b0d", "qos_policy_id": "591e0597-39a6-4665-8149-2111d8de9a08" } } ` const UpdatePortWithPolicyRequest = ` { "port": { "qos_policy_id": "591e0597-39a6-4665-8149-2111d8de9a08" } } ` const UpdatePortWithPolicyResponse = ` { "port": { "network_id": "a87cc70a-3e15-4acf-8205-9b711a3531b7", "tenant_id": "d6700c0c9ffa4f1cb322cd4a1f3906fa", "id": "65c0ee9f-d634-4522-8954-51021b570b0d", "qos_policy_id": "591e0597-39a6-4665-8149-2111d8de9a08" } } ` const UpdatePortWithoutPolicyRequest = ` { "port": { "qos_policy_id": null } } ` const UpdatePortWithoutPolicyResponse = ` { "port": { "network_id": "a87cc70a-3e15-4acf-8205-9b711a3531b7", "tenant_id": "d6700c0c9ffa4f1cb322cd4a1f3906fa", "id": "65c0ee9f-d634-4522-8954-51021b570b0d", "qos_policy_id": "" } } ` const GetNetworkResponse = ` { "network": { "id": "65c0ee9f-d634-4522-8954-51021b570b0d", "qos_policy_id": "591e0597-39a6-4665-8149-2111d8de9a08" } } ` const CreateNetworkRequest = ` { "network": { "name": "private", "qos_policy_id": "591e0597-39a6-4665-8149-2111d8de9a08" } } ` const CreateNetworkResponse = ` { "network": { "tenant_id": "4fd44f30292945e481c7b8a0c8908869", "id": "65c0ee9f-d634-4522-8954-51021b570b0d", "qos_policy_id": "591e0597-39a6-4665-8149-2111d8de9a08" } } ` const UpdateNetworkWithPolicyRequest = ` { "network": { "name": "updated", "qos_policy_id": "591e0597-39a6-4665-8149-2111d8de9a08" } } ` const UpdateNetworkWithPolicyResponse = ` { "network": { "tenant_id": "4fd44f30292945e481c7b8a0c8908869", "id": "65c0ee9f-d634-4522-8954-51021b570b0d", "name": "updated", "qos_policy_id": "591e0597-39a6-4665-8149-2111d8de9a08" } } ` const UpdateNetworkWithoutPolicyRequest = ` { "network": { "qos_policy_id": null } } ` const UpdateNetworkWithoutPolicyResponse = ` { "network": { "tenant_id": "4fd44f30292945e481c7b8a0c8908869", "id": "65c0ee9f-d634-4522-8954-51021b570b0d", "qos_policy_id": "" } } ` const ListPoliciesResponse = ` { "policies": [ { "name": "bw-limiter", "tags": [], "rules": [ { "max_kbps": 3000, "direction": "egress", "qos_policy_id": "d6ae28ce-fcb5-4180-aa62-d260a27e09ae", "type": "bandwidth_limit", "id": "30a57f4a-336b-4382-8275-d708babd2241", "max_burst_kbps": 300 } ], "tenant_id": "a77cbe0998374aed9a6798ad6c61677e", "created_at": "2019-05-19T11:17:50Z", "updated_at": "2019-05-19T11:17:57Z", "is_default": false, "revision_number": 1, "shared": false, "project_id": "a77cbe0998374aed9a6798ad6c61677e", "id": "d6ae28ce-fcb5-4180-aa62-d260a27e09ae", "description": "" }, { "name": "no-rules", "tags": [], "rules": [], "tenant_id": "a77cbe0998374aed9a6798ad6c61677e", "created_at": "2019-06-01T10:38:58Z", "updated_at": "2019-06-01T10:38:58Z", "is_default": false, "revision_number": 0, "shared": false, "project_id": "a77cbe0998374aed9a6798ad6c61677e", "id": "d6e7c2fe-24dc-43be-a088-24b47d4f8f88", "description": "" } ] } ` var Policy1 = policies.Policy{ Name: "bw-limiter", Rules: []map[string]interface{}{ { "type": "bandwidth_limit", "max_kbps": float64(3000), "direction": "egress", "qos_policy_id": "d6ae28ce-fcb5-4180-aa62-d260a27e09ae", "max_burst_kbps": float64(300), "id": "30a57f4a-336b-4382-8275-d708babd2241", }, }, Tags: []string{}, TenantID: "a77cbe0998374aed9a6798ad6c61677e", CreatedAt: time.Date(2019, 5, 19, 11, 17, 50, 0, time.UTC), UpdatedAt: time.Date(2019, 5, 19, 11, 17, 57, 0, time.UTC), RevisionNumber: 1, ProjectID: "a77cbe0998374aed9a6798ad6c61677e", ID: "d6ae28ce-fcb5-4180-aa62-d260a27e09ae", } var Policy2 = policies.Policy{ Name: "no-rules", Tags: []string{}, Rules: []map[string]interface{}{}, TenantID: "a77cbe0998374aed9a6798ad6c61677e", CreatedAt: time.Date(2019, 6, 1, 10, 38, 58, 0, time.UTC), UpdatedAt: time.Date(2019, 6, 1, 10, 38, 58, 0, time.UTC), RevisionNumber: 0, ProjectID: "a77cbe0998374aed9a6798ad6c61677e", ID: "d6e7c2fe-24dc-43be-a088-24b47d4f8f88", } const GetPolicyResponse = ` { "policy": { "name": "bw-limiter", "tags": [], "rules": [ { "max_kbps": 3000, "direction": "egress", "qos_policy_id": "d6ae28ce-fcb5-4180-aa62-d260a27e09ae", "type": "bandwidth_limit", "id": "30a57f4a-336b-4382-8275-d708babd2241", "max_burst_kbps": 300 } ], "tenant_id": "a77cbe0998374aed9a6798ad6c61677e", "created_at": "2019-05-19T11:17:50Z", "updated_at": "2019-05-19T11:17:57Z", "is_default": false, "revision_number": 1, "shared": false, "project_id": "a77cbe0998374aed9a6798ad6c61677e", "id": "d6ae28ce-fcb5-4180-aa62-d260a27e09ae", "description": "" } } ` const CreatePolicyRequest = ` { "policy": { "name": "shared-default-policy", "is_default": true, "shared": true, "description": "use-me" } } ` const CreatePolicyResponse = ` { "policy": { "name": "shared-default-policy", "tags": [], "rules": [], "tenant_id": "a77cbe0998374aed9a6798ad6c61677e", "created_at": "2019-05-19T11:17:50Z", "updated_at": "2019-05-19T11:17:57Z", "is_default": true, "revision_number": 0, "shared": true, "project_id": "a77cbe0998374aed9a6798ad6c61677e", "id": "d6ae28ce-fcb5-4180-aa62-d260a27e09ae", "description": "use-me" } } ` const UpdatePolicyRequest = ` { "policy": { "name": "new-name", "shared": true, "description": "" } } ` const UpdatePolicyResponse = ` { "policy": { "name": "new-name", "tags": [], "rules": [], "tenant_id": "a77cbe0998374aed9a6798ad6c61677e", "created_at": "2019-05-19T11:17:50Z", "updated_at": "2019-06-01T13:17:57Z", "is_default": false, "revision_number": 1, "shared": true, "project_id": "a77cbe0998374aed9a6798ad6c61677e", "id": "d6ae28ce-fcb5-4180-aa62-d260a27e09ae", "description": "" } } ` requests_test.go000066400000000000000000000351761367513235700373470ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/qos/policies/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/qos/policies" "github.com/gophercloud/gophercloud/openstack/networking/v2/networks" "github.com/gophercloud/gophercloud/openstack/networking/v2/ports" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" ) func TestGetPort(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, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) _, err := fmt.Fprintf(w, GetPortResponse) th.AssertNoErr(t, err) }) var p struct { ports.Port policies.QoSPolicyExt } err := ports.Get(fake.ServiceClient(), "65c0ee9f-d634-4522-8954-51021b570b0d").ExtractInto(&p) th.AssertNoErr(t, err) th.AssertEquals(t, p.ID, "65c0ee9f-d634-4522-8954-51021b570b0d") th.AssertEquals(t, p.QoSPolicyID, "591e0597-39a6-4665-8149-2111d8de9a08") } func TestCreatePort(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, CreatePortRequest) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) _, err := fmt.Fprintf(w, CreatePortResponse) th.AssertNoErr(t, err) }) var p struct { ports.Port policies.QoSPolicyExt } portCreateOpts := ports.CreateOpts{ NetworkID: "a87cc70a-3e15-4acf-8205-9b711a3531b7", } createOpts := policies.PortCreateOptsExt{ CreateOptsBuilder: portCreateOpts, QoSPolicyID: "591e0597-39a6-4665-8149-2111d8de9a08", } err := ports.Create(fake.ServiceClient(), createOpts).ExtractInto(&p) th.AssertNoErr(t, err) th.AssertEquals(t, p.NetworkID, "a87cc70a-3e15-4acf-8205-9b711a3531b7") th.AssertEquals(t, p.TenantID, "d6700c0c9ffa4f1cb322cd4a1f3906fa") th.AssertEquals(t, p.ID, "65c0ee9f-d634-4522-8954-51021b570b0d") th.AssertEquals(t, p.QoSPolicyID, "591e0597-39a6-4665-8149-2111d8de9a08") } func TestUpdatePortWithPolicy(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, UpdatePortWithPolicyRequest) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) _, err := fmt.Fprintf(w, UpdatePortWithPolicyResponse) th.AssertNoErr(t, err) }) policyID := "591e0597-39a6-4665-8149-2111d8de9a08" var p struct { ports.Port policies.QoSPolicyExt } portUpdateOpts := ports.UpdateOpts{} updateOpts := policies.PortUpdateOptsExt{ UpdateOptsBuilder: portUpdateOpts, QoSPolicyID: &policyID, } err := ports.Update(fake.ServiceClient(), "65c0ee9f-d634-4522-8954-51021b570b0d", updateOpts).ExtractInto(&p) th.AssertNoErr(t, err) th.AssertEquals(t, p.NetworkID, "a87cc70a-3e15-4acf-8205-9b711a3531b7") th.AssertEquals(t, p.TenantID, "d6700c0c9ffa4f1cb322cd4a1f3906fa") th.AssertEquals(t, p.ID, "65c0ee9f-d634-4522-8954-51021b570b0d") th.AssertEquals(t, p.QoSPolicyID, "591e0597-39a6-4665-8149-2111d8de9a08") } func TestUpdatePortWithoutPolicy(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, UpdatePortWithoutPolicyRequest) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) _, err := fmt.Fprintf(w, UpdatePortWithoutPolicyResponse) th.AssertNoErr(t, err) }) policyID := "" var p struct { ports.Port policies.QoSPolicyExt } portUpdateOpts := ports.UpdateOpts{} updateOpts := policies.PortUpdateOptsExt{ UpdateOptsBuilder: portUpdateOpts, QoSPolicyID: &policyID, } err := ports.Update(fake.ServiceClient(), "65c0ee9f-d634-4522-8954-51021b570b0d", updateOpts).ExtractInto(&p) th.AssertNoErr(t, err) th.AssertEquals(t, p.NetworkID, "a87cc70a-3e15-4acf-8205-9b711a3531b7") th.AssertEquals(t, p.TenantID, "d6700c0c9ffa4f1cb322cd4a1f3906fa") th.AssertEquals(t, p.ID, "65c0ee9f-d634-4522-8954-51021b570b0d") th.AssertEquals(t, p.QoSPolicyID, "") } func TestGetNetwork(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/networks/65c0ee9f-d634-4522-8954-51021b570b0d", 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) _, err := fmt.Fprintf(w, GetNetworkResponse) th.AssertNoErr(t, err) }) var n struct { networks.Network policies.QoSPolicyExt } err := networks.Get(fake.ServiceClient(), "65c0ee9f-d634-4522-8954-51021b570b0d").ExtractInto(&n) th.AssertNoErr(t, err) th.AssertEquals(t, n.ID, "65c0ee9f-d634-4522-8954-51021b570b0d") th.AssertEquals(t, n.QoSPolicyID, "591e0597-39a6-4665-8149-2111d8de9a08") } func TestCreateNetwork(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, CreateNetworkRequest) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) _, err := fmt.Fprintf(w, CreateNetworkResponse) th.AssertNoErr(t, err) }) var n struct { networks.Network policies.QoSPolicyExt } networkCreateOpts := networks.CreateOpts{ Name: "private", } createOpts := policies.NetworkCreateOptsExt{ CreateOptsBuilder: networkCreateOpts, QoSPolicyID: "591e0597-39a6-4665-8149-2111d8de9a08", } err := networks.Create(fake.ServiceClient(), createOpts).ExtractInto(&n) th.AssertNoErr(t, err) th.AssertEquals(t, n.TenantID, "4fd44f30292945e481c7b8a0c8908869") th.AssertEquals(t, n.ID, "65c0ee9f-d634-4522-8954-51021b570b0d") th.AssertEquals(t, n.QoSPolicyID, "591e0597-39a6-4665-8149-2111d8de9a08") } func TestUpdateNetworkWithPolicy(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/networks/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, UpdateNetworkWithPolicyRequest) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) _, err := fmt.Fprintf(w, UpdateNetworkWithPolicyResponse) th.AssertNoErr(t, err) }) policyID := "591e0597-39a6-4665-8149-2111d8de9a08" name := "updated" var n struct { networks.Network policies.QoSPolicyExt } networkUpdateOpts := networks.UpdateOpts{ Name: &name, } updateOpts := policies.NetworkUpdateOptsExt{ UpdateOptsBuilder: networkUpdateOpts, QoSPolicyID: &policyID, } err := networks.Update(fake.ServiceClient(), "65c0ee9f-d634-4522-8954-51021b570b0d", updateOpts).ExtractInto(&n) th.AssertNoErr(t, err) th.AssertEquals(t, n.TenantID, "4fd44f30292945e481c7b8a0c8908869") th.AssertEquals(t, n.ID, "65c0ee9f-d634-4522-8954-51021b570b0d") th.AssertEquals(t, n.Name, "updated") th.AssertEquals(t, n.QoSPolicyID, "591e0597-39a6-4665-8149-2111d8de9a08") } func TestUpdateNetworkWithoutPolicy(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/networks/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, UpdateNetworkWithoutPolicyRequest) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) _, err := fmt.Fprintf(w, UpdateNetworkWithoutPolicyResponse) th.AssertNoErr(t, err) }) policyID := "" var n struct { networks.Network policies.QoSPolicyExt } networkUpdateOpts := networks.UpdateOpts{} updateOpts := policies.NetworkUpdateOptsExt{ UpdateOptsBuilder: networkUpdateOpts, QoSPolicyID: &policyID, } err := networks.Update(fake.ServiceClient(), "65c0ee9f-d634-4522-8954-51021b570b0d", updateOpts).ExtractInto(&n) th.AssertNoErr(t, err) th.AssertEquals(t, n.TenantID, "4fd44f30292945e481c7b8a0c8908869") th.AssertEquals(t, n.ID, "65c0ee9f-d634-4522-8954-51021b570b0d") th.AssertEquals(t, n.QoSPolicyID, "") } func TestListPolicies(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/qos/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, ListPoliciesResponse) }) count := 0 err := 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 policies: %v", err) return false, nil } expected := []policies.Policy{ Policy1, Policy2, } th.CheckDeepEquals(t, expected, actual) return true, nil }) th.AssertNoErr(t, err) if count != 1 { t.Errorf("Expected 1 page, got %d", count) } } func TestGetPolicy(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/qos/policies/30a57f4a-336b-4382-8275-d708babd2241", 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, GetPolicyResponse) }) p, err := policies.Get(fake.ServiceClient(), "30a57f4a-336b-4382-8275-d708babd2241").Extract() th.AssertNoErr(t, err) th.AssertEquals(t, "bw-limiter", p.Name) th.AssertDeepEquals(t, []string{}, p.Tags) th.AssertDeepEquals(t, []map[string]interface{}{ { "max_kbps": float64(3000), "direction": "egress", "qos_policy_id": "d6ae28ce-fcb5-4180-aa62-d260a27e09ae", "type": "bandwidth_limit", "id": "30a57f4a-336b-4382-8275-d708babd2241", "max_burst_kbps": float64(300), }, }, p.Rules) th.AssertEquals(t, "a77cbe0998374aed9a6798ad6c61677e", p.TenantID) th.AssertEquals(t, "a77cbe0998374aed9a6798ad6c61677e", p.ProjectID) th.AssertEquals(t, time.Date(2019, 5, 19, 11, 17, 50, 0, time.UTC), p.CreatedAt) th.AssertEquals(t, time.Date(2019, 5, 19, 11, 17, 57, 0, time.UTC), p.UpdatedAt) th.AssertEquals(t, 1, p.RevisionNumber) th.AssertEquals(t, "d6ae28ce-fcb5-4180-aa62-d260a27e09ae", p.ID) } func TestCreatePolicy(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/qos/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, CreatePolicyRequest) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) fmt.Fprintf(w, CreatePolicyResponse) }) opts := policies.CreateOpts{ Name: "shared-default-policy", Shared: true, IsDefault: true, Description: "use-me", } p, err := policies.Create(fake.ServiceClient(), opts).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, "shared-default-policy", p.Name) th.AssertEquals(t, true, p.Shared) th.AssertEquals(t, true, p.IsDefault) th.AssertEquals(t, "use-me", p.Description) th.AssertEquals(t, "a77cbe0998374aed9a6798ad6c61677e", p.TenantID) th.AssertEquals(t, "a77cbe0998374aed9a6798ad6c61677e", p.ProjectID) th.AssertEquals(t, time.Date(2019, 5, 19, 11, 17, 50, 0, time.UTC), p.CreatedAt) th.AssertEquals(t, time.Date(2019, 5, 19, 11, 17, 57, 0, time.UTC), p.UpdatedAt) th.AssertEquals(t, 0, p.RevisionNumber) th.AssertEquals(t, "d6ae28ce-fcb5-4180-aa62-d260a27e09ae", p.ID) } func TestUpdatePolicy(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/qos/policies/d6ae28ce-fcb5-4180-aa62-d260a27e09ae", 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, UpdatePolicyRequest) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, UpdatePolicyResponse) }) shared := true description := "" opts := policies.UpdateOpts{ Name: "new-name", Shared: &shared, Description: &description, } p, err := policies.Update(fake.ServiceClient(), "d6ae28ce-fcb5-4180-aa62-d260a27e09ae", opts).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, "new-name", p.Name) th.AssertEquals(t, true, p.Shared) th.AssertEquals(t, false, p.IsDefault) th.AssertEquals(t, "", p.Description) th.AssertEquals(t, "a77cbe0998374aed9a6798ad6c61677e", p.TenantID) th.AssertEquals(t, "a77cbe0998374aed9a6798ad6c61677e", p.ProjectID) th.AssertEquals(t, time.Date(2019, 5, 19, 11, 17, 50, 0, time.UTC), p.CreatedAt) th.AssertEquals(t, time.Date(2019, 6, 1, 13, 17, 57, 0, time.UTC), p.UpdatedAt) th.AssertEquals(t, 1, p.RevisionNumber) th.AssertEquals(t, "d6ae28ce-fcb5-4180-aa62-d260a27e09ae", p.ID) } func TestDeletePolicy(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/qos/policies/d6ae28ce-fcb5-4180-aa62-d260a27e09ae", 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(), "d6ae28ce-fcb5-4180-aa62-d260a27e09ae") th.AssertNoErr(t, res.Err) } golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/qos/policies/urls.go000066400000000000000000000013321367513235700340070ustar00rootroot00000000000000package policies import "github.com/gophercloud/gophercloud" const resourcePath = "qos/policies" func rootURL(c *gophercloud.ServiceClient) string { return c.ServiceURL(resourcePath) } func resourceURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL(resourcePath, id) } 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.12.0/openstack/networking/v2/extensions/qos/rules/000077500000000000000000000000001367513235700320175ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/qos/rules/doc.go000066400000000000000000000145271367513235700331240ustar00rootroot00000000000000/* Package rules provides the ability to retrieve and manage QoS policy rules through the Neutron API. Example of Listing BandwidthLimitRules listOpts := rules.BandwidthLimitRulesListOpts{ MaxKBps: 3000, } policyID := "501005fa-3b56-4061-aaca-3f24995112e1" allPages, err := rules.ListBandwidthLimitRules(networkClient, policyID, listOpts).AllPages() if err != nil { panic(err) } allBandwidthLimitRules, err := rules.ExtractBandwidthLimitRules(allPages) if err != nil { panic(err) } for _, bandwidthLimitRule := range allBandwidthLimitRules { fmt.Printf("%+v\n", bandwidthLimitRule) } Example of Getting a single BandwidthLimitRule policyID := "501005fa-3b56-4061-aaca-3f24995112e1" ruleID := "30a57f4a-336b-4382-8275-d708babd2241" rule, err := rules.GetBandwidthLimitRule(networkClient, policyID, ruleID).ExtractBandwidthLimitRule() if err != nil { panic(err) } fmt.Printf("Rule: %+v\n", rule) Example of Creating a single BandwidthLimitRule opts := rules.CreateBandwidthLimitRuleOpts{ MaxKBps: 2000, MaxBurstKBps: 200, } policyID := "501005fa-3b56-4061-aaca-3f24995112e1" rule, err := rules.CreateBandwidthLimitRule(networkClient, policyID, opts).ExtractBandwidthLimitRule() if err != nil { panic(err) } fmt.Printf("Rule: %+v\n", rule) Example of Updating a single BandwidthLimitRule maxKBps := 500 maxBurstKBps := 0 opts := rules.UpdateBandwidthLimitRuleOpts{ MaxKBps: &maxKBps, MaxBurstKBps: &maxBurstKBps, } policyID := "501005fa-3b56-4061-aaca-3f24995112e1" ruleID := "30a57f4a-336b-4382-8275-d708babd2241" rule, err := rules.UpdateBandwidthLimitRule(networkClient, policyID, ruleID, opts).ExtractBandwidthLimitRule() if err != nil { panic(err) } fmt.Printf("Rule: %+v\n", rule) Example of Deleting a single BandwidthLimitRule policyID := "501005fa-3b56-4061-aaca-3f24995112e1" ruleID := "30a57f4a-336b-4382-8275-d708babd2241" err := rules.DeleteBandwidthLimitRule(fake.ServiceClient(), "501005fa-3b56-4061-aaca-3f24995112e1", "30a57f4a-336b-4382-8275-d708babd2241").ExtractErr() if err != nil { panic(err) } Example of Listing DSCP marking rules listOpts := rules.DSCPMarkingRulesListOpts{} policyID := "501005fa-3b56-4061-aaca-3f24995112e1" allPages, err := rules.ListDSCPMarkingRules(networkClient, policyID, listOpts).AllPages() if err != nil { panic(err) } allDSCPMarkingRules, err := rules.ExtractDSCPMarkingRules(allPages) if err != nil { panic(err) } for _, dscpMarkingRule := range allDSCPMarkingRules { fmt.Printf("%+v\n", dscpMarkingRule) } Example of Getting a single DSCPMarkingRule policyID := "501005fa-3b56-4061-aaca-3f24995112e1" ruleID := "30a57f4a-336b-4382-8275-d708babd2241" rule, err := rules.GetDSCPMarkingRule(networkClient, policyID, ruleID).ExtractDSCPMarkingRule() if err != nil { panic(err) } fmt.Printf("Rule: %+v\n", rule) Example of Creating a single DSCPMarkingRule opts := rules.CreateDSCPMarkingRuleOpts{ DSCPMark: 20, } policyID := "501005fa-3b56-4061-aaca-3f24995112e1" rule, err := rules.CreateDSCPMarkingRule(networkClient, policyID, opts).ExtractDSCPMarkingRule() if err != nil { panic(err) } fmt.Printf("Rule: %+v\n", rule) Example of Updating a single DSCPMarkingRule dscpMark := 26 opts := rules.UpdateDSCPMarkingRuleOpts{ DSCPMark: &dscpMark, } policyID := "501005fa-3b56-4061-aaca-3f24995112e1" ruleID := "30a57f4a-336b-4382-8275-d708babd2241" rule, err := rules.UpdateDSCPMarkingRule(networkClient, policyID, ruleID, opts).ExtractDSCPMarkingRule() if err != nil { panic(err) } fmt.Printf("Rule: %+v\n", rule) Example of Deleting a single DSCPMarkingRule policyID := "501005fa-3b56-4061-aaca-3f24995112e1" ruleID := "30a57f4a-336b-4382-8275-d708babd2241" err := rules.DeleteDSCPMarkingRule(fake.ServiceClient(), "501005fa-3b56-4061-aaca-3f24995112e1", "30a57f4a-336b-4382-8275-d708babd2241").ExtractErr() if err != nil { panic(err) } Example of Listing MinimumBandwidthRules listOpts := rules.MinimumBandwidthRulesListOpts{ MinKBps: 3000, } policyID := "501005fa-3b56-4061-aaca-3f24995112e1" allPages, err := rules.ListMinimumBandwidthRules(networkClient, policyID, listOpts).AllPages() if err != nil { panic(err) } allMinimumBandwidthRules, err := rules.ExtractMinimumBandwidthRules(allPages) if err != nil { panic(err) } for _, bandwidthLimitRule := range allMinimumBandwidthRules { fmt.Printf("%+v\n", bandwidthLimitRule) } Example of Getting a single MinimumBandwidthRule policyID := "501005fa-3b56-4061-aaca-3f24995112e1" ruleID := "30a57f4a-336b-4382-8275-d708babd2241" rule, err := rules.GetMinimumBandwidthRule(networkClient, policyID, ruleID).ExtractMinimumBandwidthRule() if err != nil { panic(err) } fmt.Printf("Rule: %+v\n", rule) Example of Creating a single MinimumBandwidthRule opts := rules.CreateMinimumBandwidthRuleOpts{ MinKBps: 2000, } policyID := "501005fa-3b56-4061-aaca-3f24995112e1" rule, err := rules.CreateMinimumBandwidthRule(networkClient, policyID, opts).ExtractMinimumBandwidthRule() if err != nil { panic(err) } fmt.Printf("Rule: %+v\n", rule) Example of Updating a single MinimumBandwidthRule minKBps := 500 opts := rules.UpdateMinimumBandwidthRuleOpts{ MinKBps: &minKBps, } policyID := "501005fa-3b56-4061-aaca-3f24995112e1" ruleID := "30a57f4a-336b-4382-8275-d708babd2241" rule, err := rules.UpdateMinimumBandwidthRule(networkClient, policyID, ruleID, opts).ExtractMinimumBandwidthRule() if err != nil { panic(err) } fmt.Printf("Rule: %+v\n", rule) Example of Deleting a single MinimumBandwidthRule policyID := "501005fa-3b56-4061-aaca-3f24995112e1" ruleID := "30a57f4a-336b-4382-8275-d708babd2241" err := rules.DeleteMinimumBandwidthRule(fake.ServiceClient(), "501005fa-3b56-4061-aaca-3f24995112e1", "30a57f4a-336b-4382-8275-d708babd2241").ExtractErr() if err != nil { panic(err) } */ package rules requests.go000066400000000000000000000405111367513235700341430ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/qos/rulespackage rules import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // ListOptsBuilder allows extensions to add additional parameters to the // List request. type BandwidthLimitRulesListOptsBuilder interface { ToBandwidthLimitRulesListQuery() (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 BandwidthLimitRules attributes you want to see returned. // SortKey allows you to sort by a particular BandwidthLimitRule attribute. // SortDir sets the direction, and is either `asc' or `desc'. // Marker and Limit are used for the pagination. type BandwidthLimitRulesListOpts struct { ID string `q:"id"` TenantID string `q:"tenant_id"` MaxKBps int `q:"max_kbps"` MaxBurstKBps int `q:"max_burst_kbps"` Direction string `q:"direction"` Limit int `q:"limit"` Marker string `q:"marker"` SortKey string `q:"sort_key"` SortDir string `q:"sort_dir"` Tags string `q:"tags"` TagsAny string `q:"tags-any"` NotTags string `q:"not-tags"` NotTagsAny string `q:"not-tags-any"` } // ToBandwidthLimitRulesListQuery formats a ListOpts into a query string. func (opts BandwidthLimitRulesListOpts) ToBandwidthLimitRulesListQuery() (string, error) { q, err := gophercloud.BuildQueryString(opts) return q.String(), err } // ListBandwidthLimitRules returns a Pager which allows you to iterate over a collection of // BandwidthLimitRules. It accepts a ListOpts struct, which allows you to filter and sort // the returned collection for greater efficiency. func ListBandwidthLimitRules(c *gophercloud.ServiceClient, policyID string, opts BandwidthLimitRulesListOptsBuilder) pagination.Pager { url := listBandwidthLimitRulesURL(c, policyID) if opts != nil { query, err := opts.ToBandwidthLimitRulesListQuery() if err != nil { return pagination.Pager{Err: err} } url += query } return pagination.NewPager(c, url, func(r pagination.PageResult) pagination.Page { return BandwidthLimitRulePage{pagination.LinkedPageBase{PageResult: r}} }) } // GetBandwidthLimitRule retrieves a specific BandwidthLimitRule based on its ID. func GetBandwidthLimitRule(c *gophercloud.ServiceClient, policyID, ruleID string) (r GetBandwidthLimitRuleResult) { resp, err := c.Get(getBandwidthLimitRuleURL(c, policyID, ruleID), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // CreateBandwidthLimitRuleOptsBuilder allows to add additional parameters to the // CreateBandwidthLimitRule request. type CreateBandwidthLimitRuleOptsBuilder interface { ToBandwidthLimitRuleCreateMap() (map[string]interface{}, error) } // CreateBandwidthLimitRuleOpts specifies parameters of a new BandwidthLimitRule. type CreateBandwidthLimitRuleOpts struct { // MaxKBps is a maximum kilobits per second. It's a required parameter. MaxKBps int `json:"max_kbps"` // MaxBurstKBps is a maximum burst size in kilobits. MaxBurstKBps int `json:"max_burst_kbps,omitempty"` // Direction represents the direction of traffic. Direction string `json:"direction,omitempty"` } // ToBandwidthLimitRuleCreateMap constructs a request body from CreateBandwidthLimitRuleOpts. func (opts CreateBandwidthLimitRuleOpts) ToBandwidthLimitRuleCreateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "bandwidth_limit_rule") } // CreateBandwidthLimitRule requests the creation of a new BandwidthLimitRule on the server. func CreateBandwidthLimitRule(client *gophercloud.ServiceClient, policyID string, opts CreateBandwidthLimitRuleOptsBuilder) (r CreateBandwidthLimitRuleResult) { b, err := opts.ToBandwidthLimitRuleCreateMap() if err != nil { r.Err = err return } resp, err := client.Post(createBandwidthLimitRuleURL(client, policyID), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{201}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // UpdateBandwidthLimitRuleOptsBuilder allows to add additional parameters to the // UpdateBandwidthLimitRule request. type UpdateBandwidthLimitRuleOptsBuilder interface { ToBandwidthLimitRuleUpdateMap() (map[string]interface{}, error) } // UpdateBandwidthLimitRuleOpts specifies parameters for the Update call. type UpdateBandwidthLimitRuleOpts struct { // MaxKBps is a maximum kilobits per second. MaxKBps *int `json:"max_kbps,omitempty"` // MaxBurstKBps is a maximum burst size in kilobits. MaxBurstKBps *int `json:"max_burst_kbps,omitempty"` // Direction represents the direction of traffic. Direction string `json:"direction,omitempty"` } // ToBandwidthLimitRuleUpdateMap constructs a request body from UpdateBandwidthLimitRuleOpts. func (opts UpdateBandwidthLimitRuleOpts) ToBandwidthLimitRuleUpdateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "bandwidth_limit_rule") } // UpdateBandwidthLimitRule requests the creation of a new BandwidthLimitRule on the server. func UpdateBandwidthLimitRule(client *gophercloud.ServiceClient, policyID, ruleID string, opts UpdateBandwidthLimitRuleOptsBuilder) (r UpdateBandwidthLimitRuleResult) { b, err := opts.ToBandwidthLimitRuleUpdateMap() if err != nil { r.Err = err return } resp, err := client.Put(updateBandwidthLimitRuleURL(client, policyID, ruleID), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete accepts policy and rule ID and deletes the BandwidthLimitRule associated with them. func DeleteBandwidthLimitRule(c *gophercloud.ServiceClient, policyID, ruleID string) (r DeleteBandwidthLimitRuleResult) { resp, err := c.Delete(deleteBandwidthLimitRuleURL(c, policyID, ruleID), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // DSCPMarkingRulesListOptsBuilder allows extensions to add additional parameters to the // List request. type DSCPMarkingRulesListOptsBuilder interface { ToDSCPMarkingRulesListQuery() (string, error) } // DSCPMarkingRulesListOpts 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 DSCPMarking attributes you want to see returned. // SortKey allows you to sort by a particular DSCPMarkingRule attribute. // SortDir sets the direction, and is either `asc' or `desc'. // Marker and Limit are used for the pagination. type DSCPMarkingRulesListOpts struct { ID string `q:"id"` TenantID string `q:"tenant_id"` DSCPMark int `q:"dscp_mark"` Limit int `q:"limit"` Marker string `q:"marker"` SortKey string `q:"sort_key"` SortDir string `q:"sort_dir"` Tags string `q:"tags"` TagsAny string `q:"tags-any"` NotTags string `q:"not-tags"` NotTagsAny string `q:"not-tags-any"` } // ToDSCPMarkingRulesListQuery formats a ListOpts into a query string. func (opts DSCPMarkingRulesListOpts) ToDSCPMarkingRulesListQuery() (string, error) { q, err := gophercloud.BuildQueryString(opts) return q.String(), err } // ListDSCPMarkingRules returns a Pager which allows you to iterate over a collection of // DSCPMarkingRules. It accepts a ListOpts struct, which allows you to filter and sort // the returned collection for greater efficiency. func ListDSCPMarkingRules(c *gophercloud.ServiceClient, policyID string, opts DSCPMarkingRulesListOptsBuilder) pagination.Pager { url := listDSCPMarkingRulesURL(c, policyID) if opts != nil { query, err := opts.ToDSCPMarkingRulesListQuery() if err != nil { return pagination.Pager{Err: err} } url += query } return pagination.NewPager(c, url, func(r pagination.PageResult) pagination.Page { return DSCPMarkingRulePage{pagination.LinkedPageBase{PageResult: r}} }) } // GetDSCPMarkingRule retrieves a specific DSCPMarkingRule based on its ID. func GetDSCPMarkingRule(c *gophercloud.ServiceClient, policyID, ruleID string) (r GetDSCPMarkingRuleResult) { resp, err := c.Get(getDSCPMarkingRuleURL(c, policyID, ruleID), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // CreateDSCPMarkingRuleOptsBuilder allows to add additional parameters to the // CreateDSCPMarkingRule request. type CreateDSCPMarkingRuleOptsBuilder interface { ToDSCPMarkingRuleCreateMap() (map[string]interface{}, error) } // CreateDSCPMarkingRuleOpts specifies parameters of a new DSCPMarkingRule. type CreateDSCPMarkingRuleOpts struct { // DSCPMark contains DSCP mark value. DSCPMark int `json:"dscp_mark"` } // ToDSCPMarkingRuleCreateMap constructs a request body from CreateDSCPMarkingRuleOpts. func (opts CreateDSCPMarkingRuleOpts) ToDSCPMarkingRuleCreateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "dscp_marking_rule") } // CreateDSCPMarkingRule requests the creation of a new DSCPMarkingRule on the server. func CreateDSCPMarkingRule(client *gophercloud.ServiceClient, policyID string, opts CreateDSCPMarkingRuleOptsBuilder) (r CreateDSCPMarkingRuleResult) { b, err := opts.ToDSCPMarkingRuleCreateMap() if err != nil { r.Err = err return } resp, err := client.Post(createDSCPMarkingRuleURL(client, policyID), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{201}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // UpdateDSCPMarkingRuleOptsBuilder allows to add additional parameters to the // UpdateDSCPMarkingRule request. type UpdateDSCPMarkingRuleOptsBuilder interface { ToDSCPMarkingRuleUpdateMap() (map[string]interface{}, error) } // UpdateDSCPMarkingRuleOpts specifies parameters for the Update call. type UpdateDSCPMarkingRuleOpts struct { // DSCPMark contains DSCP mark value. DSCPMark *int `json:"dscp_mark,omitempty"` } // ToDSCPMarkingRuleUpdateMap constructs a request body from UpdateDSCPMarkingRuleOpts. func (opts UpdateDSCPMarkingRuleOpts) ToDSCPMarkingRuleUpdateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "dscp_marking_rule") } // UpdateDSCPMarkingRule requests the creation of a new DSCPMarkingRule on the server. func UpdateDSCPMarkingRule(client *gophercloud.ServiceClient, policyID, ruleID string, opts UpdateDSCPMarkingRuleOptsBuilder) (r UpdateDSCPMarkingRuleResult) { b, err := opts.ToDSCPMarkingRuleUpdateMap() if err != nil { r.Err = err return } resp, err := client.Put(updateDSCPMarkingRuleURL(client, policyID, ruleID), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // DeleteDSCPMarkingRule accepts policy and rule ID and deletes the DSCPMarkingRule associated with them. func DeleteDSCPMarkingRule(c *gophercloud.ServiceClient, policyID, ruleID string) (r DeleteDSCPMarkingRuleResult) { resp, err := c.Delete(deleteDSCPMarkingRuleURL(c, policyID, ruleID), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // ListOptsBuilder allows extensions to add additional parameters to the // List request. type MinimumBandwidthRulesListOptsBuilder interface { ToMinimumBandwidthRulesListQuery() (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 MinimumBandwidthRules attributes you want to see returned. // SortKey allows you to sort by a particular MinimumBandwidthRule attribute. // SortDir sets the direction, and is either `asc' or `desc'. // Marker and Limit are used for the pagination. type MinimumBandwidthRulesListOpts struct { ID string `q:"id"` TenantID string `q:"tenant_id"` MinKBps int `q:"min_kbps"` Direction string `q:"direction"` Limit int `q:"limit"` Marker string `q:"marker"` SortKey string `q:"sort_key"` SortDir string `q:"sort_dir"` Tags string `q:"tags"` TagsAny string `q:"tags-any"` NotTags string `q:"not-tags"` NotTagsAny string `q:"not-tags-any"` } // ToMinimumBandwidthRulesListQuery formats a ListOpts into a query string. func (opts MinimumBandwidthRulesListOpts) ToMinimumBandwidthRulesListQuery() (string, error) { q, err := gophercloud.BuildQueryString(opts) return q.String(), err } // ListMinimumBandwidthRules returns a Pager which allows you to iterate over a collection of // MinimumBandwidthRules. It accepts a ListOpts struct, which allows you to filter and sort // the returned collection for greater efficiency. func ListMinimumBandwidthRules(c *gophercloud.ServiceClient, policyID string, opts MinimumBandwidthRulesListOptsBuilder) pagination.Pager { url := listMinimumBandwidthRulesURL(c, policyID) if opts != nil { query, err := opts.ToMinimumBandwidthRulesListQuery() if err != nil { return pagination.Pager{Err: err} } url += query } return pagination.NewPager(c, url, func(r pagination.PageResult) pagination.Page { return MinimumBandwidthRulePage{pagination.LinkedPageBase{PageResult: r}} }) } // GetMinimumBandwidthRule retrieves a specific MinimumBandwidthRule based on its ID. func GetMinimumBandwidthRule(c *gophercloud.ServiceClient, policyID, ruleID string) (r GetMinimumBandwidthRuleResult) { resp, err := c.Get(getMinimumBandwidthRuleURL(c, policyID, ruleID), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // CreateMinimumBandwidthRuleOptsBuilder allows to add additional parameters to the // CreateMinimumBandwidthRule request. type CreateMinimumBandwidthRuleOptsBuilder interface { ToMinimumBandwidthRuleCreateMap() (map[string]interface{}, error) } // CreateMinimumBandwidthRuleOpts specifies parameters of a new MinimumBandwidthRule. type CreateMinimumBandwidthRuleOpts struct { // MaxKBps is a minimum kilobits per second. It's a required parameter. MinKBps int `json:"min_kbps"` // Direction represents the direction of traffic. Direction string `json:"direction,omitempty"` } // ToMinimumBandwidthRuleCreateMap constructs a request body from CreateMinimumBandwidthRuleOpts. func (opts CreateMinimumBandwidthRuleOpts) ToMinimumBandwidthRuleCreateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "minimum_bandwidth_rule") } // CreateMinimumBandwidthRule requests the creation of a new MinimumBandwidthRule on the server. func CreateMinimumBandwidthRule(client *gophercloud.ServiceClient, policyID string, opts CreateMinimumBandwidthRuleOptsBuilder) (r CreateMinimumBandwidthRuleResult) { b, err := opts.ToMinimumBandwidthRuleCreateMap() if err != nil { r.Err = err return } resp, err := client.Post(createMinimumBandwidthRuleURL(client, policyID), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{201}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // UpdateMinimumBandwidthRuleOptsBuilder allows to add additional parameters to the // UpdateMinimumBandwidthRule request. type UpdateMinimumBandwidthRuleOptsBuilder interface { ToMinimumBandwidthRuleUpdateMap() (map[string]interface{}, error) } // UpdateMinimumBandwidthRuleOpts specifies parameters for the Update call. type UpdateMinimumBandwidthRuleOpts struct { // MaxKBps is a minimum kilobits per second. It's a required parameter. MinKBps *int `json:"min_kbps,omitempty"` // Direction represents the direction of traffic. Direction string `json:"direction,omitempty"` } // ToMinimumBandwidthRuleUpdateMap constructs a request body from UpdateMinimumBandwidthRuleOpts. func (opts UpdateMinimumBandwidthRuleOpts) ToMinimumBandwidthRuleUpdateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "minimum_bandwidth_rule") } // UpdateMinimumBandwidthRule requests the creation of a new MinimumBandwidthRule on the server. func UpdateMinimumBandwidthRule(client *gophercloud.ServiceClient, policyID, ruleID string, opts UpdateMinimumBandwidthRuleOptsBuilder) (r UpdateMinimumBandwidthRuleResult) { b, err := opts.ToMinimumBandwidthRuleUpdateMap() if err != nil { r.Err = err return } resp, err := client.Put(updateMinimumBandwidthRuleURL(client, policyID, ruleID), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // DeleteMinimumBandwidthRule accepts policy and rule ID and deletes the MinimumBandwidthRule associated with them. func DeleteMinimumBandwidthRule(c *gophercloud.ServiceClient, policyID, ruleID string) (r DeleteMinimumBandwidthRuleResult) { resp, err := c.Delete(deleteMinimumBandwidthRuleURL(c, policyID, ruleID), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/qos/rules/results.go000066400000000000000000000172711367513235700340570ustar00rootroot00000000000000package rules 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 BandwidthLimitRule. func (r commonResult) ExtractBandwidthLimitRule() (*BandwidthLimitRule, error) { var s struct { BandwidthLimitRule *BandwidthLimitRule `json:"bandwidth_limit_rule"` } err := r.ExtractInto(&s) return s.BandwidthLimitRule, err } // GetBandwidthLimitRuleResult represents the result of a Get operation. Call its Extract // method to interpret it as a BandwidthLimitRule. type GetBandwidthLimitRuleResult struct { commonResult } // CreateBandwidthLimitRuleResult represents the result of a Create operation. Call its Extract // method to interpret it as a BandwidthLimitRule. type CreateBandwidthLimitRuleResult struct { commonResult } // UpdateBandwidthLimitRuleResult represents the result of a Update operation. Call its Extract // method to interpret it as a BandwidthLimitRule. type UpdateBandwidthLimitRuleResult struct { commonResult } // DeleteBandwidthLimitRuleResult represents the result of a Delete operation. Call its Extract // method to interpret it as a BandwidthLimitRule. type DeleteBandwidthLimitRuleResult struct { gophercloud.ErrResult } // BandwidthLimitRule represents a QoS policy rule to set bandwidth limits. type BandwidthLimitRule struct { // ID is a unique ID of the policy. ID string `json:"id"` // TenantID is the ID of the Identity project. TenantID string `json:"tenant_id"` // MaxKBps is a maximum kilobits per second. MaxKBps int `json:"max_kbps"` // MaxBurstKBps is a maximum burst size in kilobits. MaxBurstKBps int `json:"max_burst_kbps"` // Direction represents the direction of traffic. Direction string `json:"direction"` // Tags optionally set via extensions/attributestags. Tags []string `json:"tags"` } // BandwidthLimitRulePage stores a single page of BandwidthLimitRules from a List() API call. type BandwidthLimitRulePage struct { pagination.LinkedPageBase } // IsEmpty checks whether a BandwidthLimitRulePage is empty. func (r BandwidthLimitRulePage) IsEmpty() (bool, error) { is, err := ExtractBandwidthLimitRules(r) return len(is) == 0, err } // ExtractBandwidthLimitRules accepts a BandwidthLimitRulePage, and extracts the elements into a slice of // BandwidthLimitRules. func ExtractBandwidthLimitRules(r pagination.Page) ([]BandwidthLimitRule, error) { var s []BandwidthLimitRule err := ExtractBandwidthLimitRulesInto(r, &s) return s, err } // ExtractBandwidthLimitRulesInto extracts the elements into a slice of RBAC Policy structs. func ExtractBandwidthLimitRulesInto(r pagination.Page, v interface{}) error { return r.(BandwidthLimitRulePage).Result.ExtractIntoSlicePtr(v, "bandwidth_limit_rules") } // Extract is a function that accepts a result and extracts a DSCPMarkingRule. func (r commonResult) ExtractDSCPMarkingRule() (*DSCPMarkingRule, error) { var s struct { DSCPMarkingRule *DSCPMarkingRule `json:"dscp_marking_rule"` } err := r.ExtractInto(&s) return s.DSCPMarkingRule, err } // GetDSCPMarkingRuleResult represents the result of a Get operation. Call its Extract // method to interpret it as a DSCPMarkingRule. type GetDSCPMarkingRuleResult struct { commonResult } // CreateDSCPMarkingRuleResult represents the result of a Create operation. Call its Extract // method to interpret it as a DSCPMarkingRule. type CreateDSCPMarkingRuleResult struct { commonResult } // UpdateDSCPMarkingRuleResult represents the result of a Update operation. Call its Extract // method to interpret it as a DSCPMarkingRule. type UpdateDSCPMarkingRuleResult struct { commonResult } // DeleteDSCPMarkingRuleResult represents the result of a Delete operation. Call its Extract // method to interpret it as a DSCPMarkingRule. type DeleteDSCPMarkingRuleResult struct { gophercloud.ErrResult } // DSCPMarkingRule represents a QoS policy rule to set DSCP marking. type DSCPMarkingRule struct { // ID is a unique ID of the policy. ID string `json:"id"` // TenantID is the ID of the Identity project. TenantID string `json:"tenant_id"` // DSCPMark contains DSCP mark value. DSCPMark int `json:"dscp_mark"` // Tags optionally set via extensions/attributestags. Tags []string `json:"tags"` } // DSCPMarkingRulePage stores a single page of DSCPMarkingRules from a List() API call. type DSCPMarkingRulePage struct { pagination.LinkedPageBase } // IsEmpty checks whether a DSCPMarkingRulePage is empty. func (r DSCPMarkingRulePage) IsEmpty() (bool, error) { is, err := ExtractDSCPMarkingRules(r) return len(is) == 0, err } // ExtractDSCPMarkingRules accepts a DSCPMarkingRulePage, and extracts the elements into a slice of // DSCPMarkingRules. func ExtractDSCPMarkingRules(r pagination.Page) ([]DSCPMarkingRule, error) { var s []DSCPMarkingRule err := ExtractDSCPMarkingRulesInto(r, &s) return s, err } // ExtractDSCPMarkingRulesInto extracts the elements into a slice of RBAC Policy structs. func ExtractDSCPMarkingRulesInto(r pagination.Page, v interface{}) error { return r.(DSCPMarkingRulePage).Result.ExtractIntoSlicePtr(v, "dscp_marking_rules") } // Extract is a function that accepts a result and extracts a BandwidthLimitRule. func (r commonResult) ExtractMinimumBandwidthRule() (*MinimumBandwidthRule, error) { var s struct { MinimumBandwidthRule *MinimumBandwidthRule `json:"minimum_bandwidth_rule"` } err := r.ExtractInto(&s) return s.MinimumBandwidthRule, err } // GetMinimumBandwidthRuleResult represents the result of a Get operation. Call its Extract // method to interpret it as a MinimumBandwidthRule. type GetMinimumBandwidthRuleResult struct { commonResult } // CreateMinimumBandwidthRuleResult represents the result of a Create operation. Call its Extract // method to interpret it as a MinimumBandwidthtRule. type CreateMinimumBandwidthRuleResult struct { commonResult } // UpdateMinimumBandwidthRuleResult represents the result of a Update operation. Call its Extract // method to interpret it as a MinimumBandwidthRule. type UpdateMinimumBandwidthRuleResult struct { commonResult } // DeleteMinimumBandwidthRuleResult represents the result of a Delete operation. Call its Extract // method to interpret it as a MinimumBandwidthRule. type DeleteMinimumBandwidthRuleResult struct { gophercloud.ErrResult } // MinimumBandwidthRule represents a QoS policy rule to set minimum bandwidth. type MinimumBandwidthRule struct { // ID is a unique ID of the rule. ID string `json:"id"` // TenantID is the ID of the Identity project. TenantID string `json:"tenant_id"` // MaxKBps is a maximum kilobits per second. MinKBps int `json:"min_kbps"` // Direction represents the direction of traffic. Direction string `json:"direction"` // Tags optionally set via extensions/attributestags. Tags []string `json:"tags"` } // MinimumBandwidthRulePage stores a single page of MinimumBandwidthRules from a List() API call. type MinimumBandwidthRulePage struct { pagination.LinkedPageBase } // IsEmpty checks whether a MinimumBandwidthRulePage is empty. func (r MinimumBandwidthRulePage) IsEmpty() (bool, error) { is, err := ExtractMinimumBandwidthRules(r) return len(is) == 0, err } // ExtractMinimumBandwidthRules accepts a MinimumBandwidthRulePage, and extracts the elements into a slice of // MinimumBandwidthRules. func ExtractMinimumBandwidthRules(r pagination.Page) ([]MinimumBandwidthRule, error) { var s []MinimumBandwidthRule err := ExtractMinimumBandwidthRulesInto(r, &s) return s, err } // ExtractMinimumBandwidthRulesInto extracts the elements into a slice of RBAC Policy structs. func ExtractMinimumBandwidthRulesInto(r pagination.Page, v interface{}) error { return r.(MinimumBandwidthRulePage).Result.ExtractIntoSlicePtr(v, "minimum_bandwidth_rules") } golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/qos/rules/testing/000077500000000000000000000000001367513235700334745ustar00rootroot00000000000000doc.go000066400000000000000000000000571367513235700345130ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/qos/rules/testing// QoS policy rules unit tests package testing fixtures.go000066400000000000000000000111701367513235700356150ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/qos/rules/testingpackage testing // BandwidthLimitRulesListResult represents a raw result of a List call to BandwidthLimitRules. const BandwidthLimitRulesListResult = ` { "bandwidth_limit_rules": [ { "max_kbps": 3000, "direction": "egress", "id": "30a57f4a-336b-4382-8275-d708babd2241", "max_burst_kbps": 300 } ] } ` // BandwidthLimitRulesGetResult represents a raw result of a Get call to a specific BandwidthLimitRule. const BandwidthLimitRulesGetResult = ` { "bandwidth_limit_rule": { "max_kbps": 3000, "direction": "egress", "id": "30a57f4a-336b-4382-8275-d708babd2241", "max_burst_kbps": 300 } } ` // BandwidthLimitRulesCreateRequest represents a raw body of a Create BandwidthLimitRule call. const BandwidthLimitRulesCreateRequest = ` { "bandwidth_limit_rule": { "max_kbps": 2000, "max_burst_kbps": 200 } } ` // BandwidthLimitRulesCreateResult represents a raw result of a Create BandwidthLimitRule call. const BandwidthLimitRulesCreateResult = ` { "bandwidth_limit_rule": { "max_kbps": 2000, "id": "30a57f4a-336b-4382-8275-d708babd2241", "max_burst_kbps": 200 } } ` // BandwidthLimitRulesUpdateRequest represents a raw body of a Update BandwidthLimitRule call. const BandwidthLimitRulesUpdateRequest = ` { "bandwidth_limit_rule": { "max_kbps": 500, "max_burst_kbps": 0 } } ` // BandwidthLimitRulesUpdateResult represents a raw result of a Update BandwidthLimitRule call. const BandwidthLimitRulesUpdateResult = ` { "bandwidth_limit_rule": { "max_kbps": 500, "id": "30a57f4a-336b-4382-8275-d708babd2241", "max_burst_kbps": 0 } } ` // DSCPMarkingRulesListResult represents a raw result of a List call to DSCPMarkingRules. const DSCPMarkingRulesListResult = ` { "dscp_marking_rules": [ { "id": "30a57f4a-336b-4382-8275-d708babd2241", "dscp_mark": 20 } ] } ` // DSCPMarkingRuleGetResult represents a raw result of a Get DSCPMarkingRule call. const DSCPMarkingRuleGetResult = ` { "dscp_marking_rule": { "id": "30a57f4a-336b-4382-8275-d708babd2241", "dscp_mark": 26 } } ` // DSCPMarkingRuleCreateRequest represents a raw body of a Create DSCPMarkingRule call. const DSCPMarkingRuleCreateRequest = ` { "dscp_marking_rule": { "dscp_mark": 20 } } ` // DSCPMarkingRuleCreateResult represents a raw result of a Update DSCPMarkingRule call. const DSCPMarkingRuleCreateResult = ` { "dscp_marking_rule": { "id": "30a57f4a-336b-4382-8275-d708babd2241", "dscp_mark": 20 } } ` // DSCPMarkingRuleUpdateRequest represents a raw body of a Update DSCPMarkingRule call. const DSCPMarkingRuleUpdateRequest = ` { "dscp_marking_rule": { "dscp_mark": 26 } } ` // DSCPMarkingRuleUpdateResult represents a raw result of a Update DSCPMarkingRule call. const DSCPMarkingRuleUpdateResult = ` { "dscp_marking_rule": { "id": "30a57f4a-336b-4382-8275-d708babd2241", "dscp_mark": 26 } } ` // MinimumBandwidthRulesListResult represents a raw result of a List call to MinimumBandwidthRules. const MinimumBandwidthRulesListResult = ` { "minimum_bandwidth_rules": [ { "min_kbps": 3000, "direction": "egress", "id": "30a57f4a-336b-4382-8275-d708babd2241" } ] } ` // MinimumBandwidthRulesGetResult represents a raw result of a Get call to a specific MinimumBandwidthRule. const MinimumBandwidthRulesGetResult = ` { "minimum_bandwidth_rule": { "min_kbps": 3000, "direction": "egress", "id": "30a57f4a-336b-4382-8275-d708babd2241" } } ` // MinimumBandwidthRulesCreateRequest represents a raw body of a Create MinimumBandwidthRule call. const MinimumBandwidthRulesCreateRequest = ` { "minimum_bandwidth_rule": { "min_kbps": 2000 } } ` // MinimumBandwidthRulesCreateResult represents a raw result of a Create MinimumBandwidthRule call. const MinimumBandwidthRulesCreateResult = ` { "minimum_bandwidth_rule": { "min_kbps": 2000, "id": "30a57f4a-336b-4382-8275-d708babd2241" } } ` // MinimumBandwidthRulesUpdateRequest represents a raw body of a Update MinimumBandwidthRule call. const MinimumBandwidthRulesUpdateRequest = ` { "minimum_bandwidth_rule": { "min_kbps": 500 } } ` // MinimumBandwidthRulesUpdateResult represents a raw result of a Update MinimumBandwidthRule call. const MinimumBandwidthRulesUpdateResult = ` { "minimum_bandwidth_rule": { "min_kbps": 500, "id": "30a57f4a-336b-4382-8275-d708babd2241" } } ` requests_test.go000066400000000000000000000332501367513235700366610ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/qos/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/qos/rules" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" ) func TestListBandwidthLimitRule(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/qos/policies/501005fa-3b56-4061-aaca-3f24995112e1/bandwidth_limit_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, BandwidthLimitRulesListResult) }) count := 0 err := rules.ListBandwidthLimitRules( fake.ServiceClient(), "501005fa-3b56-4061-aaca-3f24995112e1", rules.BandwidthLimitRulesListOpts{}, ).EachPage(func(page pagination.Page) (bool, error) { count++ actual, err := rules.ExtractBandwidthLimitRules(page) if err != nil { t.Errorf("Failed to extract bandwith limit rules: %v", err) return false, nil } expected := []rules.BandwidthLimitRule{ { ID: "30a57f4a-336b-4382-8275-d708babd2241", MaxKBps: 3000, MaxBurstKBps: 300, Direction: "egress", }, } th.CheckDeepEquals(t, expected, actual) return true, nil }) th.AssertNoErr(t, err) if count != 1 { t.Errorf("Expected 1 page, got %d", count) } } func TestGetBandwidthLimitRule(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/qos/policies/501005fa-3b56-4061-aaca-3f24995112e1/bandwidth_limit_rules/30a57f4a-336b-4382-8275-d708babd2241", 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, BandwidthLimitRulesGetResult) }) r, err := rules.GetBandwidthLimitRule(fake.ServiceClient(), "501005fa-3b56-4061-aaca-3f24995112e1", "30a57f4a-336b-4382-8275-d708babd2241").ExtractBandwidthLimitRule() th.AssertNoErr(t, err) th.AssertEquals(t, r.ID, "30a57f4a-336b-4382-8275-d708babd2241") th.AssertEquals(t, r.Direction, "egress") th.AssertEquals(t, r.MaxBurstKBps, 300) th.AssertEquals(t, r.MaxKBps, 3000) } func TestCreateBandwidthLimitRule(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/qos/policies/501005fa-3b56-4061-aaca-3f24995112e1/bandwidth_limit_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, BandwidthLimitRulesCreateRequest) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) fmt.Fprintf(w, BandwidthLimitRulesCreateResult) }) opts := rules.CreateBandwidthLimitRuleOpts{ MaxKBps: 2000, MaxBurstKBps: 200, } r, err := rules.CreateBandwidthLimitRule(fake.ServiceClient(), "501005fa-3b56-4061-aaca-3f24995112e1", opts).ExtractBandwidthLimitRule() th.AssertNoErr(t, err) th.AssertEquals(t, 200, r.MaxBurstKBps) th.AssertEquals(t, 2000, r.MaxKBps) } func TestUpdateBandwidthLimitRule(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/qos/policies/501005fa-3b56-4061-aaca-3f24995112e1/bandwidth_limit_rules/30a57f4a-336b-4382-8275-d708babd2241", 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, BandwidthLimitRulesUpdateRequest) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, BandwidthLimitRulesUpdateResult) }) maxKBps := 500 maxBurstKBps := 0 opts := rules.UpdateBandwidthLimitRuleOpts{ MaxKBps: &maxKBps, MaxBurstKBps: &maxBurstKBps, } r, err := rules.UpdateBandwidthLimitRule(fake.ServiceClient(), "501005fa-3b56-4061-aaca-3f24995112e1", "30a57f4a-336b-4382-8275-d708babd2241", opts).ExtractBandwidthLimitRule() th.AssertNoErr(t, err) th.AssertEquals(t, 0, r.MaxBurstKBps) th.AssertEquals(t, 500, r.MaxKBps) } func TestDeleteBandwidthLimitRule(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/qos/policies/501005fa-3b56-4061-aaca-3f24995112e1/bandwidth_limit_rules/30a57f4a-336b-4382-8275-d708babd2241", 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.DeleteBandwidthLimitRule(fake.ServiceClient(), "501005fa-3b56-4061-aaca-3f24995112e1", "30a57f4a-336b-4382-8275-d708babd2241") th.AssertNoErr(t, res.Err) } func TestListDSCPMarkingRule(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/qos/policies/501005fa-3b56-4061-aaca-3f24995112e1/dscp_marking_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, DSCPMarkingRulesListResult) }) count := 0 err := rules.ListDSCPMarkingRules( fake.ServiceClient(), "501005fa-3b56-4061-aaca-3f24995112e1", rules.DSCPMarkingRulesListOpts{}, ).EachPage(func(page pagination.Page) (bool, error) { count++ actual, err := rules.ExtractDSCPMarkingRules(page) if err != nil { t.Errorf("Failed to extract DSCP marking rules: %v", err) return false, nil } expected := []rules.DSCPMarkingRule{ { ID: "30a57f4a-336b-4382-8275-d708babd2241", DSCPMark: 20, }, } th.CheckDeepEquals(t, expected, actual) return true, nil }) th.AssertNoErr(t, err) if count != 1 { t.Errorf("Expected 1 page, got %d", count) } } func TestGetDSCPMarkingRule(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/qos/policies/501005fa-3b56-4061-aaca-3f24995112e1/dscp_marking_rules/30a57f4a-336b-4382-8275-d708babd2241", 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, DSCPMarkingRuleGetResult) }) r, err := rules.GetDSCPMarkingRule(fake.ServiceClient(), "501005fa-3b56-4061-aaca-3f24995112e1", "30a57f4a-336b-4382-8275-d708babd2241").ExtractDSCPMarkingRule() th.AssertNoErr(t, err) th.AssertEquals(t, r.ID, "30a57f4a-336b-4382-8275-d708babd2241") th.AssertEquals(t, 26, r.DSCPMark) } func TestCreateDSCPMarkingRule(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/qos/policies/501005fa-3b56-4061-aaca-3f24995112e1/dscp_marking_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, DSCPMarkingRuleCreateRequest) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) fmt.Fprintf(w, DSCPMarkingRuleCreateResult) }) opts := rules.CreateDSCPMarkingRuleOpts{ DSCPMark: 20, } r, err := rules.CreateDSCPMarkingRule(fake.ServiceClient(), "501005fa-3b56-4061-aaca-3f24995112e1", opts).ExtractDSCPMarkingRule() th.AssertNoErr(t, err) th.AssertEquals(t, "30a57f4a-336b-4382-8275-d708babd2241", r.ID) th.AssertEquals(t, 20, r.DSCPMark) } func TestUpdateDSCPMarkingRule(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/qos/policies/501005fa-3b56-4061-aaca-3f24995112e1/dscp_marking_rules/30a57f4a-336b-4382-8275-d708babd2241", 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, DSCPMarkingRuleUpdateRequest) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, DSCPMarkingRuleUpdateResult) }) dscpMark := 26 opts := rules.UpdateDSCPMarkingRuleOpts{ DSCPMark: &dscpMark, } r, err := rules.UpdateDSCPMarkingRule(fake.ServiceClient(), "501005fa-3b56-4061-aaca-3f24995112e1", "30a57f4a-336b-4382-8275-d708babd2241", opts).ExtractDSCPMarkingRule() th.AssertNoErr(t, err) th.AssertEquals(t, "30a57f4a-336b-4382-8275-d708babd2241", r.ID) th.AssertEquals(t, 26, r.DSCPMark) } func TestDeleteDSCPMarkingRule(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/qos/policies/501005fa-3b56-4061-aaca-3f24995112e1/dscp_marking_rules/30a57f4a-336b-4382-8275-d708babd2241", 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.DeleteDSCPMarkingRule(fake.ServiceClient(), "501005fa-3b56-4061-aaca-3f24995112e1", "30a57f4a-336b-4382-8275-d708babd2241") th.AssertNoErr(t, res.Err) } func TestListMinimumBandwidthRule(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/qos/policies/501005fa-3b56-4061-aaca-3f24995112e1/minimum_bandwidth_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, MinimumBandwidthRulesListResult) }) count := 0 err := rules.ListMinimumBandwidthRules( fake.ServiceClient(), "501005fa-3b56-4061-aaca-3f24995112e1", rules.MinimumBandwidthRulesListOpts{}, ).EachPage(func(page pagination.Page) (bool, error) { count++ actual, err := rules.ExtractMinimumBandwidthRules(page) if err != nil { t.Errorf("Failed to extract minimum bandwith rules: %v", err) return false, nil } expected := []rules.MinimumBandwidthRule{ { ID: "30a57f4a-336b-4382-8275-d708babd2241", Direction: "egress", MinKBps: 3000, }, } th.CheckDeepEquals(t, expected, actual) return true, nil }) th.AssertNoErr(t, err) if count != 1 { t.Errorf("Expected 1 page, got %d", count) } } func TestGetMinimumBandwidthRule(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/qos/policies/501005fa-3b56-4061-aaca-3f24995112e1/minimum_bandwidth_rules/30a57f4a-336b-4382-8275-d708babd2241", 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, MinimumBandwidthRulesGetResult) }) r, err := rules.GetMinimumBandwidthRule(fake.ServiceClient(), "501005fa-3b56-4061-aaca-3f24995112e1", "30a57f4a-336b-4382-8275-d708babd2241").ExtractMinimumBandwidthRule() th.AssertNoErr(t, err) th.AssertEquals(t, r.ID, "30a57f4a-336b-4382-8275-d708babd2241") th.AssertEquals(t, r.Direction, "egress") th.AssertEquals(t, r.MinKBps, 3000) } func TestCreateMinimumBandwidthRule(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/qos/policies/501005fa-3b56-4061-aaca-3f24995112e1/minimum_bandwidth_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, MinimumBandwidthRulesCreateRequest) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) fmt.Fprintf(w, MinimumBandwidthRulesCreateResult) }) opts := rules.CreateMinimumBandwidthRuleOpts{ MinKBps: 2000, } r, err := rules.CreateMinimumBandwidthRule(fake.ServiceClient(), "501005fa-3b56-4061-aaca-3f24995112e1", opts).ExtractMinimumBandwidthRule() th.AssertNoErr(t, err) th.AssertEquals(t, 2000, r.MinKBps) } func TestUpdateMinimumBandwidthRule(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/qos/policies/501005fa-3b56-4061-aaca-3f24995112e1/minimum_bandwidth_rules/30a57f4a-336b-4382-8275-d708babd2241", 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, MinimumBandwidthRulesUpdateRequest) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, MinimumBandwidthRulesUpdateResult) }) minKBps := 500 opts := rules.UpdateMinimumBandwidthRuleOpts{ MinKBps: &minKBps, } r, err := rules.UpdateMinimumBandwidthRule(fake.ServiceClient(), "501005fa-3b56-4061-aaca-3f24995112e1", "30a57f4a-336b-4382-8275-d708babd2241", opts).ExtractMinimumBandwidthRule() th.AssertNoErr(t, err) th.AssertEquals(t, 500, r.MinKBps) } func TestDeleteMinimumBandwidthRule(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/qos/policies/501005fa-3b56-4061-aaca-3f24995112e1/minimum_bandwidth_rules/30a57f4a-336b-4382-8275-d708babd2241", 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.DeleteMinimumBandwidthRule(fake.ServiceClient(), "501005fa-3b56-4061-aaca-3f24995112e1", "30a57f4a-336b-4382-8275-d708babd2241") th.AssertNoErr(t, res.Err) } golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/qos/rules/urls.go000066400000000000000000000070001367513235700333300ustar00rootroot00000000000000package rules import "github.com/gophercloud/gophercloud" const ( rootPath = "qos/policies" bandwidthLimitRulesResourcePath = "bandwidth_limit_rules" dscpMarkingRulesResourcePath = "dscp_marking_rules" minimumBandwidthRulesResourcePath = "minimum_bandwidth_rules" ) func bandwidthLimitRulesRootURL(c *gophercloud.ServiceClient, policyID string) string { return c.ServiceURL(rootPath, policyID, bandwidthLimitRulesResourcePath) } func bandwidthLimitRulesResourceURL(c *gophercloud.ServiceClient, policyID, ruleID string) string { return c.ServiceURL(rootPath, policyID, bandwidthLimitRulesResourcePath, ruleID) } func listBandwidthLimitRulesURL(c *gophercloud.ServiceClient, policyID string) string { return bandwidthLimitRulesRootURL(c, policyID) } func getBandwidthLimitRuleURL(c *gophercloud.ServiceClient, policyID, ruleID string) string { return bandwidthLimitRulesResourceURL(c, policyID, ruleID) } func createBandwidthLimitRuleURL(c *gophercloud.ServiceClient, policyID string) string { return bandwidthLimitRulesRootURL(c, policyID) } func updateBandwidthLimitRuleURL(c *gophercloud.ServiceClient, policyID, ruleID string) string { return bandwidthLimitRulesResourceURL(c, policyID, ruleID) } func deleteBandwidthLimitRuleURL(c *gophercloud.ServiceClient, policyID, ruleID string) string { return bandwidthLimitRulesResourceURL(c, policyID, ruleID) } func dscpMarkingRulesRootURL(c *gophercloud.ServiceClient, policyID string) string { return c.ServiceURL(rootPath, policyID, dscpMarkingRulesResourcePath) } func dscpMarkingRulesResourceURL(c *gophercloud.ServiceClient, policyID, ruleID string) string { return c.ServiceURL(rootPath, policyID, dscpMarkingRulesResourcePath, ruleID) } func listDSCPMarkingRulesURL(c *gophercloud.ServiceClient, policyID string) string { return dscpMarkingRulesRootURL(c, policyID) } func getDSCPMarkingRuleURL(c *gophercloud.ServiceClient, policyID, ruleID string) string { return dscpMarkingRulesResourceURL(c, policyID, ruleID) } func createDSCPMarkingRuleURL(c *gophercloud.ServiceClient, policyID string) string { return dscpMarkingRulesRootURL(c, policyID) } func updateDSCPMarkingRuleURL(c *gophercloud.ServiceClient, policyID, ruleID string) string { return dscpMarkingRulesResourceURL(c, policyID, ruleID) } func deleteDSCPMarkingRuleURL(c *gophercloud.ServiceClient, policyID, ruleID string) string { return dscpMarkingRulesResourceURL(c, policyID, ruleID) } func minimumBandwidthRulesRootURL(c *gophercloud.ServiceClient, policyID string) string { return c.ServiceURL(rootPath, policyID, minimumBandwidthRulesResourcePath) } func minimumBandwidthRulesResourceURL(c *gophercloud.ServiceClient, policyID, ruleID string) string { return c.ServiceURL(rootPath, policyID, minimumBandwidthRulesResourcePath, ruleID) } func listMinimumBandwidthRulesURL(c *gophercloud.ServiceClient, policyID string) string { return minimumBandwidthRulesRootURL(c, policyID) } func getMinimumBandwidthRuleURL(c *gophercloud.ServiceClient, policyID, ruleID string) string { return minimumBandwidthRulesResourceURL(c, policyID, ruleID) } func createMinimumBandwidthRuleURL(c *gophercloud.ServiceClient, policyID string) string { return minimumBandwidthRulesRootURL(c, policyID) } func updateMinimumBandwidthRuleURL(c *gophercloud.ServiceClient, policyID, ruleID string) string { return minimumBandwidthRulesResourceURL(c, policyID, ruleID) } func deleteMinimumBandwidthRuleURL(c *gophercloud.ServiceClient, policyID, ruleID string) string { return minimumBandwidthRulesResourceURL(c, policyID, ruleID) } golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/qos/ruletypes/000077500000000000000000000000001367513235700327215ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/qos/ruletypes/doc.go000066400000000000000000000011641367513235700340170ustar00rootroot00000000000000/* Package ruletypes contains functionality for working with Neutron 'quality of service' rule-type resources. Example of Listing QoS rule types 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) Example of Getting a single QoS rule type by it's name ruleTypeName := "bandwidth_limit" ruleType, err := ruletypes.Get(networkClient, ruleTypeName).Extract() if err != nil { panic(err) } fmt.Printf("%+v\n", ruleTypeName) */ package ruletypes requests.go000066400000000000000000000012671367513235700350520ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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)} }) } // GetRuleType retrieves a specific QoS RuleType based on its name. func GetRuleType(c *gophercloud.ServiceClient, name string) (r GetResult) { resp, err := c.Get(getRuleTypeURL(c, name), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } results.go000066400000000000000000000027211367513235700346740ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/qos/ruletypespackage ruletypes import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) type commonResult struct { gophercloud.Result } func (r commonResult) Extract() (*RuleType, error) { var s struct { RuleType *RuleType `json:"rule_type"` } err := r.ExtractInto(&s) return s.RuleType, err } // GetResult represents the result of a get operation. Call its Extract // method to interpret it as a RuleType. type GetResult struct { commonResult } // RuleType represents a single QoS rule type. type RuleType struct { Type string `json:"type"` Drivers []Driver `json:"drivers"` } // Driver represents a single QoS driver. type Driver struct { Name string `json:"name"` SupportedParameters []SupportedParameter `json:"supported_parameters"` } // SupportedParameter represents a single set of supported parameters for a some QoS driver's . type SupportedParameter struct { ParameterName string `json:"parameter_name"` ParameterType string `json:"parameter_type"` ParameterValues interface{} `json:"parameter_values"` } 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/000077500000000000000000000000001367513235700343175ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/qos/ruletypesdoc.go000066400000000000000000000000421367513235700354070ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/qos/ruletypes/testing// qos unit tests package testing fixtures.go000066400000000000000000000046631367513235700365300ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/qos/ruletypes/testingpackage testing const ( ListRuleTypesResponse = ` { "rule_types": [ { "type": "bandwidth_limit" }, { "type": "dscp_marking" }, { "type": "minimum_bandwidth" } ] } ` GetRuleTypeResponse = ` { "rule_type": { "drivers": [ { "name": "linuxbridge", "supported_parameters": [ { "parameter_values": { "start": 0, "end": 2147483647 }, "parameter_type": "range", "parameter_name": "max_kbps" }, { "parameter_values": [ "ingress", "egress" ], "parameter_type": "choices", "parameter_name": "direction" }, { "parameter_values": { "start": 0, "end": 2147483647 }, "parameter_type": "range", "parameter_name": "max_burst_kbps" } ] }, { "name": "openvswitch", "supported_parameters": [ { "parameter_values": { "start": 0, "end": 2147483647 }, "parameter_type": "range", "parameter_name": "max_kbps" }, { "parameter_values": [ "ingress", "egress" ], "parameter_type": "choices", "parameter_name": "direction" }, { "parameter_values": { "start": 0, "end": 2147483647 }, "parameter_type": "range", "parameter_name": "max_burst_kbps" } ] } ], "type": "bandwidth_limit" } } ` ) requests_test.go000066400000000000000000000036011367513235700375600ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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) } func TestGetRuleType(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/qos/rule-types/bandwidth_limit", 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) _, err := fmt.Fprintf(w, GetRuleTypeResponse) th.AssertNoErr(t, err) }) r, err := ruletypes.GetRuleType(fake.ServiceClient(), "bandwidth_limit").Extract() th.AssertNoErr(t, err) th.AssertEquals(t, "bandwidth_limit", r.Type) th.AssertEquals(t, 2, len(r.Drivers)) th.AssertEquals(t, "linuxbridge", r.Drivers[0].Name) th.AssertEquals(t, 3, len(r.Drivers[0].SupportedParameters)) th.AssertEquals(t, "openvswitch", r.Drivers[1].Name) th.AssertEquals(t, 3, len(r.Drivers[1].SupportedParameters)) } urls.go000066400000000000000000000004441367513235700341600ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/qos/ruletypespackage ruletypes import "github.com/gophercloud/gophercloud" func listRuleTypesURL(c *gophercloud.ServiceClient) string { return c.ServiceURL("qos", "rule-types") } func getRuleTypeURL(c *gophercloud.ServiceClient, name string) string { return c.ServiceURL("qos", "rule-types", name) } golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/quotas/000077500000000000000000000000001367513235700313775ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/quotas/doc.go000066400000000000000000000022171367513235700324750ustar00rootroot00000000000000/* Package quotas provides the ability to retrieve and manage Networking quotas through the Neutron API. Example to Get project quotas projectID = "23d5d3f79dfa4f73b72b8b0b0063ec55" quotasInfo, err := quotas.Get(networkClient, projectID).Extract() if err != nil { log.Fatal(err) } fmt.Printf("quotas: %#v\n", quotasInfo) Example to Update project quotas projectID = "23d5d3f79dfa4f73b72b8b0b0063ec55" updateOpts := quotas.UpdateOpts{ FloatingIP: gophercloud.IntToPointer(0), Network: gophercloud.IntToPointer(-1), Port: gophercloud.IntToPointer(5), RBACPolicy: gophercloud.IntToPointer(10), Router: gophercloud.IntToPointer(15), SecurityGroup: gophercloud.IntToPointer(20), SecurityGroupRule: gophercloud.IntToPointer(-1), Subnet: gophercloud.IntToPointer(25), SubnetPool: gophercloud.IntToPointer(0), } quotasInfo, err := quotas.Update(networkClient, projectID) if err != nil { log.Fatal(err) } fmt.Printf("quotas: %#v\n", quotasInfo) */ package quotas golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/quotas/requests.go000066400000000000000000000045401367513235700336040ustar00rootroot00000000000000package quotas import "github.com/gophercloud/gophercloud" // Get returns Networking Quotas for a project. func Get(client *gophercloud.ServiceClient, projectID string) (r GetResult) { resp, err := client.Get(getURL(client, projectID), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // UpdateOptsBuilder allows extensions to add additional parameters to the // Update request. type UpdateOptsBuilder interface { ToQuotaUpdateMap() (map[string]interface{}, error) } // UpdateOpts represents options used to update the Networking Quotas. type UpdateOpts struct { // FloatingIP represents a number of floating IPs. A "-1" value means no limit. FloatingIP *int `json:"floatingip,omitempty"` // Network represents a number of networks. A "-1" value means no limit. Network *int `json:"network,omitempty"` // Port represents a number of ports. A "-1" value means no limit. Port *int `json:"port,omitempty"` // RBACPolicy represents a number of RBAC policies. A "-1" value means no limit. RBACPolicy *int `json:"rbac_policy,omitempty"` // Router represents a number of routers. A "-1" value means no limit. Router *int `json:"router,omitempty"` // SecurityGroup represents a number of security groups. A "-1" value means no limit. SecurityGroup *int `json:"security_group,omitempty"` // SecurityGroupRule represents a number of security group rules. A "-1" value means no limit. SecurityGroupRule *int `json:"security_group_rule,omitempty"` // Subnet represents a number of subnets. A "-1" value means no limit. Subnet *int `json:"subnet,omitempty"` // SubnetPool represents a number of subnet pools. A "-1" value means no limit. SubnetPool *int `json:"subnetpool,omitempty"` } // ToQuotaUpdateMap builds a request body from UpdateOpts. func (opts UpdateOpts) ToQuotaUpdateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "quota") } // Update accepts a UpdateOpts struct and updates an existing Networking Quotas using the // values provided. func Update(c *gophercloud.ServiceClient, projectID string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToQuotaUpdateMap() if err != nil { r.Err = err return } resp, err := c.Put(updateURL(c, projectID), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/quotas/results.go000066400000000000000000000033361367513235700334340ustar00rootroot00000000000000package quotas import "github.com/gophercloud/gophercloud" type commonResult struct { gophercloud.Result } // Extract is a function that accepts a result and extracts a Quota resource. func (r commonResult) Extract() (*Quota, error) { var s struct { Quota *Quota `json:"quota"` } err := r.ExtractInto(&s) return s.Quota, err } // GetResult represents the result of a get operation. Call its Extract // method to interpret it as a Quota. type GetResult struct { commonResult } // UpdateResult represents the result of an update operation. Call its Extract // method to interpret it as a Quota. type UpdateResult struct { commonResult } // Quota contains Networking quotas for a project. type Quota struct { // FloatingIP represents a number of floating IPs. A "-1" value means no limit. FloatingIP int `json:"floatingip"` // Network represents a number of networks. A "-1" value means no limit. Network int `json:"network"` // Port represents a number of ports. A "-1" value means no limit. Port int `json:"port"` // RBACPolicy represents a number of RBAC policies. A "-1" value means no limit. RBACPolicy int `json:"rbac_policy"` // Router represents a number of routers. A "-1" value means no limit. Router int `json:"router"` // SecurityGroup represents a number of security groups. A "-1" value means no limit. SecurityGroup int `json:"security_group"` // SecurityGroupRule represents a number of security group rules. A "-1" value means no limit. SecurityGroupRule int `json:"security_group_rule"` // Subnet represents a number of subnets. A "-1" value means no limit. Subnet int `json:"subnet"` // SubnetPool represents a number of subnet pools. A "-1" value means no limit. SubnetPool int `json:"subnetpool"` } golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/quotas/testing/000077500000000000000000000000001367513235700330545ustar00rootroot00000000000000doc.go000066400000000000000000000000451367513235700340700ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/quotas/testing// quotas unit tests package testing fixtures.go000066400000000000000000000022321367513235700351740ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/quotas/testingpackage testing import "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/quotas" const GetResponseRaw = ` { "quota": { "floatingip": 15, "network": 20, "port": 25, "rbac_policy": -1, "router": 30, "security_group": 35, "security_group_rule": 40, "subnet": 45, "subnetpool": -1 } } ` var GetResponse = quotas.Quota{ FloatingIP: 15, Network: 20, Port: 25, RBACPolicy: -1, Router: 30, SecurityGroup: 35, SecurityGroupRule: 40, Subnet: 45, SubnetPool: -1, } const UpdateRequestResponseRaw = ` { "quota": { "floatingip": 0, "network": -1, "port": 5, "rbac_policy": 10, "router": 15, "security_group": 20, "security_group_rule": -1, "subnet": 25, "subnetpool": 0 } } ` var UpdateResponse = quotas.Quota{ FloatingIP: 0, Network: -1, Port: 5, RBACPolicy: 10, Router: 15, SecurityGroup: 20, SecurityGroupRule: -1, Subnet: 25, SubnetPool: 0, } requests_test.go000066400000000000000000000035541367513235700362450ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/quotas/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/quotas" th "github.com/gophercloud/gophercloud/testhelper" ) func TestGet(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/quotas/0a73845280574ad389c292f6a74afa76", 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, GetResponseRaw) }) q, err := quotas.Get(fake.ServiceClient(), "0a73845280574ad389c292f6a74afa76").Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, q, &GetResponse) } func TestUpdate(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/quotas/0a73845280574ad389c292f6a74afa76", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "PUT") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, UpdateRequestResponseRaw) }) q, err := quotas.Update(fake.ServiceClient(), "0a73845280574ad389c292f6a74afa76", quotas.UpdateOpts{ FloatingIP: gophercloud.IntToPointer(0), Network: gophercloud.IntToPointer(-1), Port: gophercloud.IntToPointer(5), RBACPolicy: gophercloud.IntToPointer(10), Router: gophercloud.IntToPointer(15), SecurityGroup: gophercloud.IntToPointer(20), SecurityGroupRule: gophercloud.IntToPointer(-1), Subnet: gophercloud.IntToPointer(25), SubnetPool: gophercloud.IntToPointer(0), }).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, q, &UpdateResponse) } golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/quotas/urls.go000066400000000000000000000006551367513235700327210ustar00rootroot00000000000000package quotas import "github.com/gophercloud/gophercloud" const resourcePath = "quotas" func resourceURL(c *gophercloud.ServiceClient, projectID string) string { return c.ServiceURL(resourcePath, projectID) } func getURL(c *gophercloud.ServiceClient, projectID string) string { return resourceURL(c, projectID) } func updateURL(c *gophercloud.ServiceClient, projectID string) string { return resourceURL(c, projectID) } golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/rbacpolicies/000077500000000000000000000000001367513235700325225ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/rbacpolicies/doc.go000066400000000000000000000043231367513235700336200ustar00rootroot00000000000000/* 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.go000066400000000000000000000122511367513235700346460ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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"` Tags string `q:"tags"` TagsAny string `q:"tags-any"` NotTags string `q:"not-tags"` NotTagsAny string `q:"not-tags-any"` } // 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) { resp, err := c.Get(getURL(c, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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 } resp, err := c.Post(createURL(c), b, &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete accepts a unique ID and deletes the rbac-policy associated with it. func Delete(c *gophercloud.ServiceClient, rbacPolicyID string) (r DeleteResult) { resp, err := c.Delete(deleteURL(c, rbacPolicyID), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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 } resp, err := c.Put(updateURL(c, rbacPolicyID), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } results.go000066400000000000000000000060051367513235700344740ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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"` // Tags optionally set via extensions/attributestags Tags []string `json:"tags"` } // 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/000077500000000000000000000000001367513235700341205ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/rbacpoliciesdoc.go000066400000000000000000000000741367513235700352150ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/rbacpolicies/testing// Package testing includes rbac unit tests package testing fixtures.go000066400000000000000000000073361367513235700363310ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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.go000066400000000000000000000117031367513235700373630ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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") } golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/rbacpolicies/urls.go000066400000000000000000000012771367513235700340450ustar00rootroot00000000000000package 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) } golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/security/000077500000000000000000000000001367513235700317325ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/security/doc.go000066400000000000000000000032101367513235700330220ustar00rootroot00000000000000// 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 golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/security/groups/000077500000000000000000000000001367513235700332515ustar00rootroot00000000000000doc.go000066400000000000000000000022241367513235700342660ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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.go000066400000000000000000000111271367513235700353760ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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"` Description string `q:"description"` 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"` Tags string `q:"tags"` TagsAny string `q:"tags-any"` NotTags string `q:"not-tags"` NotTagsAny string `q:"not-tags-any"` } // 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 } resp, err := c.Post(rootURL(c), b, &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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 } resp, err := c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get retrieves a particular security group based on its unique ID. func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { resp, err := c.Get(resourceURL(c, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete will permanently delete a particular security group based on its // unique ID. func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { resp, err := c.Delete(resourceURL(c, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } results.go000066400000000000000000000101141367513235700352170ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/security/groupspackage groups import ( "encoding/json" "time" "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"` // UpdatedAt and CreatedAt contain ISO-8601 timestamps of when the state of the // security group last changed, and when it was created. UpdatedAt time.Time `json:"-"` CreatedAt time.Time `json:"-"` // ProjectID is the project owner of the security group. ProjectID string `json:"project_id"` // Tags optionally set via extensions/attributestags Tags []string `json:"tags"` } func (r *SecGroup) UnmarshalJSON(b []byte) error { type tmp SecGroup // Support for older neutron time format var s1 struct { tmp CreatedAt gophercloud.JSONRFC3339NoZ `json:"created_at"` UpdatedAt gophercloud.JSONRFC3339NoZ `json:"updated_at"` } err := json.Unmarshal(b, &s1) if err == nil { *r = SecGroup(s1.tmp) r.CreatedAt = time.Time(s1.CreatedAt) r.UpdatedAt = time.Time(s1.UpdatedAt) return nil } // Support for newer neutron time format var s2 struct { tmp CreatedAt time.Time `json:"created_at"` UpdatedAt time.Time `json:"updated_at"` } err = json.Unmarshal(b, &s2) if err != nil { return err } *r = SecGroup(s2.tmp) r.CreatedAt = time.Time(s2.CreatedAt) r.UpdatedAt = time.Time(s2.UpdatedAt) return nil } // 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/000077500000000000000000000000001367513235700346475ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/security/groupsdoc.go000066400000000000000000000000451367513235700357420ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/security/groups/testing// groups unit tests package testing fixtures.go000066400000000000000000000127051367513235700370540ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/security/groups/testingpackage testing import ( "time" "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", "created_at": "2019-06-30T04:15:37", "updated_at": "2019-06-30T05:18:49" } ] } ` var ( createdTime, _ = time.Parse(time.RFC3339, "2019-06-30T04:15:37Z") updatedTime, _ = time.Parse(time.RFC3339, "2019-06-30T05:18:49Z") SecurityGroup1 = groups.SecGroup{ Description: "default", ID: "85cc3048-abc3-43cc-89b3-377341426ac5", Name: "default", Rules: []rules.SecGroupRule{}, TenantID: "e4f50856753b4dc6afee5fa6b9b6c550", CreatedAt: createdTime, UpdatedAt: updatedTime, } ) 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", "created_at": "2019-06-30T04:15:37Z", "updated_at": "2019-06-30T05:18:49Z" } } ` 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", "created_at": "2019-06-30T04:15:37Z", "updated_at": "2019-06-30T05:18:49Z" } } ` 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", "created_at": "2019-06-30T04:15:37Z", "updated_at": "2019-06-30T05:18:49Z" } } ` requests_test.go000066400000000000000000000105531367513235700401140ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/security/groups/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/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 err := 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 }) 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() 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) th.AssertEquals(t, "2019-06-30T04:15:37Z", sg.CreatedAt.Format(time.RFC3339)) th.AssertEquals(t, "2019-06-30T05:18:49Z", sg.UpdatedAt.Format(time.RFC3339)) } 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) th.AssertEquals(t, "2019-06-30T04:15:37Z", sg.CreatedAt.Format(time.RFC3339)) th.AssertEquals(t, "2019-06-30T05:18:49Z", sg.UpdatedAt.Format(time.RFC3339)) } 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.go000066400000000000000000000004371367513235700345120ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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) } golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/security/rules/000077500000000000000000000000001367513235700330645ustar00rootroot00000000000000doc.go000066400000000000000000000020601367513235700340770ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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.go000066400000000000000000000145031367513235700352120ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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"` Description string `q:"description"` 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,omitempty"` // 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 } resp, err := c.Post(rootURL(c), b, &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get retrieves a particular security group rule based on its unique ID. func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { resp, err := c.Get(resourceURL(c, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete will permanently delete a particular security group rule based on its // unique ID. func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { resp, err := c.Delete(resourceURL(c, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } results.go000066400000000000000000000105331367513235700350370ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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/000077500000000000000000000000001367513235700344625ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/security/rulesdoc.go000066400000000000000000000000441367513235700355540ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/security/rules/testing// rules unit tests package testing requests_test.go000066400000000000000000000162621367513235700377320ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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.go000066400000000000000000000004431367513235700343220ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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) } golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/subnetpools/000077500000000000000000000000001367513235700324405ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/subnetpools/doc.go000066400000000000000000000030701367513235700335340ustar00rootroot00000000000000/* 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.go000066400000000000000000000210101367513235700345550ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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"` Tags string `q:"tags"` TagsAny string `q:"tags-any"` NotTags string `q:"not-tags"` NotTagsAny string `q:"not-tags-any"` } // 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) { resp, err := c.Get(getURL(c, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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 } resp, err := client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{201}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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 } resp, err := c.Put(updateURL(c, subnetPoolID), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete accepts a unique ID and deletes the subnetpool associated with it. func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { resp, err := c.Delete(deleteURL(c, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } results.go000066400000000000000000000163731367513235700344230ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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:"-"` // UpdatedAt is the time at which subnetpool has been created. UpdatedAt time.Time `json:"-"` // 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"` // Tags optionally set via extensions/attributestags Tags []string `json:"tags"` } func (r *SubnetPool) UnmarshalJSON(b []byte) error { type tmp SubnetPool // Support for older neutron time format var s1 struct { tmp DefaultPrefixLen interface{} `json:"default_prefixlen"` MinPrefixLen interface{} `json:"min_prefixlen"` MaxPrefixLen interface{} `json:"max_prefixlen"` CreatedAt gophercloud.JSONRFC3339NoZ `json:"created_at"` UpdatedAt gophercloud.JSONRFC3339NoZ `json:"updated_at"` } err := json.Unmarshal(b, &s1) if err == nil { *r = SubnetPool(s1.tmp) r.CreatedAt = time.Time(s1.CreatedAt) r.UpdatedAt = time.Time(s1.UpdatedAt) switch t := s1.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 := s1.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 := s1.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 } // Support for newer neutron time format var s2 struct { tmp DefaultPrefixLen interface{} `json:"default_prefixlen"` MinPrefixLen interface{} `json:"min_prefixlen"` MaxPrefixLen interface{} `json:"max_prefixlen"` CreatedAt time.Time `json:"created_at"` UpdatedAt time.Time `json:"updated_at"` } err = json.Unmarshal(b, &s2) if err != nil { return err } *r = SubnetPool(s2.tmp) r.CreatedAt = time.Time(s2.CreatedAt) r.UpdatedAt = time.Time(s2.UpdatedAt) switch t := s2.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 := s2.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 := s2.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 } golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/subnetpools/testing/000077500000000000000000000000001367513235700341155ustar00rootroot00000000000000doc.go000066400000000000000000000000521367513235700351270ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/subnetpools/testing// subnetpools unit tests package testing fixtures.go000066400000000000000000000165371367513235700362520ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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:27", "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:27" } ] } ` 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:01", "prefixes": [ "2001:db8::a3/64" ], "updated_at": "2018-01-01T00:10:10", "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.go000066400000000000000000000131471367513235700373050ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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 err := 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 }) th.AssertNoErr(t, err) 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) } golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/subnetpools/urls.go000066400000000000000000000013341367513235700337550ustar00rootroot00000000000000package 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) } golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/testing/000077500000000000000000000000001367513235700315405ustar00rootroot00000000000000delegate_test.go000066400000000000000000000056051367513235700346270ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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.") } golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/testing/doc.go000066400000000000000000000000511367513235700326300ustar00rootroot00000000000000// extensions unit tests package testing golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/trunks/000077500000000000000000000000001367513235700314115ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/trunks/doc.go000066400000000000000000000072511367513235700325120ustar00rootroot00000000000000/* Package trunks provides the ability to retrieve and manage trunks through the Neutron API. Trunks allow you to multiplex multiple ports traffic on a single port. For example, you could have a compute instance port be the parent port of a trunk and inside the VM run workloads using other ports, without the need of plugging those ports. Example of a new empty Trunk creation iTrue := true createOpts := trunks.CreateOpts{ Name: "gophertrunk", Description: "Trunk created by gophercloud", AdminStateUp: &iTrue, PortID: "a6f0560c-b7a8-401f-bf6e-d0a5c851ae10", } trunk, err := trunks.Create(networkClient, createOpts).Extract() if err != nil { panic(err) } fmt.Printf("%+v\n", trunk) Example of a new Trunk creation with 2 subports iTrue := true createOpts := trunks.CreateOpts{ Name: "gophertrunk", Description: "Trunk created by gophercloud", AdminStateUp: &iTrue, PortID: "a6f0560c-b7a8-401f-bf6e-d0a5c851ae10", Subports: []trunks.Subport{ { SegmentationID: 1, SegmentationType: "vlan", PortID: "bf4efcc0-b1c7-4674-81f0-31f58a33420a", }, { SegmentationID: 10, SegmentationType: "vlan", PortID: "2cf671b9-02b3-4121-9e85-e0af3548d112", }, }, } trunk, err := trunks.Create(client, createOpts).Extract() if err != nil { panic(err) } fmt.Printf("%+v\n", trunk) Example of deleting a Trunk trunkID := "c36e7f2e-0c53-4742-8696-aee77c9df159" err := trunks.Delete(networkClient, trunkID).ExtractErr() if err != nil { panic(err) } Example of listing Trunks listOpts := trunks.ListOpts{} allPages, err := trunks.List(networkClient, listOpts).AllPages() if err != nil { panic(err) } allTrunks, err := trunks.ExtractTrunks(allPages) if err != nil { panic(err) } for _, trunk := range allTrunks { fmt.Printf("%+v\n", trunk) } Example of getting a Trunk trunkID = "52d8d124-3dc9-4563-9fef-bad3187ecf2d" trunk, err := trunks.Get(networkClient, trunkID).Extract() if err != nil { panic(err) } fmt.Printf("%+v\n", trunk) Example of updating a Trunk trunkID := "c36e7f2e-0c53-4742-8696-aee77c9df159" subports, err := trunks.GetSubports(client, trunkID).Extract() iFalse := false updateOpts := trunks.UpdateOpts{ AdminStateUp: &iFalse, Name: "updated_gophertrunk", Description: "trunk updated by gophercloud", } trunk, err = trunks.Update(networkClient, trunkID, updateOpts).Extract() if err != nil { log.Fatal(err) } fmt.Printf("%+v\n", trunk) Example of showing subports of a Trunk trunkID := "c36e7f2e-0c53-4742-8696-aee77c9df159" subports, err := trunks.GetSubports(client, trunkID).Extract() fmt.Printf("%+v\n", subports) Example of adding two subports to a Trunk trunkID := "c36e7f2e-0c53-4742-8696-aee77c9df159" addSubportsOpts := trunks.AddSubportsOpts{ Subports: []trunks.Subport{ { SegmentationID: 1, SegmentationType: "vlan", PortID: "bf4efcc0-b1c7-4674-81f0-31f58a33420a", }, { SegmentationID: 10, SegmentationType: "vlan", PortID: "2cf671b9-02b3-4121-9e85-e0af3548d112", }, }, } trunk, err := trunks.AddSubports(client, trunkID, addSubportsOpts).Extract() if err != nil { panic(err) } fmt.Printf("%+v\n", trunk) Example of deleting two subports from a Trunk trunkID := "c36e7f2e-0c53-4742-8696-aee77c9df159" removeSubportsOpts := trunks.RemoveSubportsOpts{ Subports: []trunks.RemoveSubport{ {PortID: "bf4efcc0-b1c7-4674-81f0-31f58a33420a"}, {PortID: "2cf671b9-02b3-4121-9e85-e0af3548d112"}, }, } trunk, err := trunks.RemoveSubports(networkClient, trunkID, removeSubportsOpts).Extract() if err != nil { panic(err) } fmt.Printf("%+v\n", trunk) */ package trunks golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/trunks/requests.go000066400000000000000000000144421367513235700336200ustar00rootroot00000000000000package trunks import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // CreateOptsBuilder allows extensions to add additional parameters to the // Create request. type CreateOptsBuilder interface { ToTrunkCreateMap() (map[string]interface{}, error) } // CreateOpts represents the attributes used when creating a new trunk. type CreateOpts struct { TenantID string `json:"tenant_id,omitempty"` ProjectID string `json:"project_id,omitempty"` PortID string `json:"port_id" required:"true"` Name string `json:"name,omitempty"` Description string `json:"description,omitempty"` AdminStateUp *bool `json:"admin_state_up,omitempty"` Subports []Subport `json:"sub_ports"` } // ToTrunkCreateMap builds a request body from CreateOpts. func (opts CreateOpts) ToTrunkCreateMap() (map[string]interface{}, error) { if opts.Subports == nil { opts.Subports = []Subport{} } return gophercloud.BuildRequestBody(opts, "trunk") } func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { body, err := opts.ToTrunkCreateMap() if err != nil { r.Err = err return } resp, err := c.Post(createURL(c), body, &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete accepts a unique ID and deletes the trunk associated with it. func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { resp, err := c.Delete(deleteURL(c, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // ListOptsBuilder allows extensions to add additional parameters to the // List request. type ListOptsBuilder interface { ToTrunkListQuery() (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 trunk attributes you want to see returned. SortKey allows you to sort // by a particular trunk attribute. SortDir sets the direction, and is either // `asc' or `desc'. Marker and Limit are used for pagination. type ListOpts struct { AdminStateUp *bool `q:"admin_state_up"` Description string `q:"description"` ID string `q:"id"` Name string `q:"name"` PortID string `q:"port_id"` RevisionNumber string `q:"revision_number"` Status string `q:"status"` TenantID string `q:"tenant_id"` ProjectID string `q:"project_id"` SortDir string `q:"sort_dir"` SortKey string `q:"sort_key"` Tags string `q:"tags"` TagsAny string `q:"tags-any"` NotTags string `q:"not-tags"` NotTagsAny string `q:"not-tags-any"` } // ToTrunkListQuery formats a ListOpts into a query string. func (opts ListOpts) ToTrunkListQuery() (string, error) { q, err := gophercloud.BuildQueryString(opts) return q.String(), err } // List returns a Pager which allows you to iterate over a collection of // trunks. It accepts a ListOpts struct, which allows you to filter and sort // the returned collection for greater efficiency. // // Default policy settings return only those trunks 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.ToTrunkListQuery() if err != nil { return pagination.Pager{Err: err} } url += query } return pagination.NewPager(c, url, func(r pagination.PageResult) pagination.Page { return TrunkPage{pagination.LinkedPageBase{PageResult: r}} }) } // Get retrieves a specific trunk based on its unique ID. func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { resp, err := c.Get(getURL(c, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } type UpdateOptsBuilder interface { ToTrunkUpdateMap() (map[string]interface{}, error) } type UpdateOpts struct { AdminStateUp *bool `json:"admin_state_up,omitempty"` Name *string `json:"name,omitempty"` Description *string `json:"description,omitempty"` } func (opts UpdateOpts) ToTrunkUpdateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "trunk") } func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { body, err := opts.ToTrunkUpdateMap() if err != nil { r.Err = err return } resp, err := c.Put(updateURL(c, id), body, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } func GetSubports(c *gophercloud.ServiceClient, id string) (r GetSubportsResult) { resp, err := c.Get(getSubportsURL(c, id), &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } type AddSubportsOpts struct { Subports []Subport `json:"sub_ports" required:"true"` } type AddSubportsOptsBuilder interface { ToTrunkAddSubportsMap() (map[string]interface{}, error) } func (opts AddSubportsOpts) ToTrunkAddSubportsMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "") } func AddSubports(c *gophercloud.ServiceClient, id string, opts AddSubportsOptsBuilder) (r UpdateSubportsResult) { body, err := opts.ToTrunkAddSubportsMap() if err != nil { r.Err = err return } resp, err := c.Put(addSubportsURL(c, id), body, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } type RemoveSubport struct { PortID string `json:"port_id" required:"true"` } type RemoveSubportsOpts struct { Subports []RemoveSubport `json:"sub_ports"` } type RemoveSubportsOptsBuilder interface { ToTrunkRemoveSubportsMap() (map[string]interface{}, error) } func (opts RemoveSubportsOpts) ToTrunkRemoveSubportsMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "") } func RemoveSubports(c *gophercloud.ServiceClient, id string, opts RemoveSubportsOptsBuilder) (r UpdateSubportsResult) { body, err := opts.ToTrunkRemoveSubportsMap() if err != nil { r.Err = err return } resp, err := c.Put(removeSubportsURL(c, id), body, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/trunks/results.go000066400000000000000000000067721367513235700334550ustar00rootroot00000000000000package trunks import ( "time" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) type Subport struct { SegmentationID int `json:"segmentation_id" required:"true"` SegmentationType string `json:"segmentation_type" required:"true"` PortID string `json:"port_id" required:"true"` } type commonResult struct { gophercloud.Result } // CreateResult is the response from a Create operation. Call its Extract method // to interpret it as a Trunk. 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 } // GetResult is the response from a Get operation. Call its Extract method // to interpret it as a Trunk. type GetResult struct { commonResult } // UpdateResult is the result of an Update request. Call its Extract method to // interpret it as a Trunk. type UpdateResult struct { commonResult } // GetSubportsResult is the result of a Get request on the trunks subports // resource. Call its Extract method to interpret it as a slice of Subport. type GetSubportsResult struct { commonResult } // UpdateSubportsResult is the result of either an AddSubports or a RemoveSubports // request. Call its Extract method to interpret it as a Trunk. type UpdateSubportsResult struct { commonResult } type Trunk struct { // Indicates whether the trunk is currently operational. Possible values include // `ACTIVE', `DOWN', `BUILD', 'DEGRADED' or `ERROR'. Status string `json:"status"` // A list of ports associated with the trunk Subports []Subport `json:"sub_ports"` // Human-readable name for the trunk. Might not be unique. Name string `json:"name,omitempty"` // The administrative state of the trunk. If false (down), the trunk does not // forward packets. AdminStateUp bool `json:"admin_state_up,omitempty"` // ProjectID is the project owner of the trunk. ProjectID string `json:"project_id"` // TenantID is the project owner of the trunk. TenantID string `json:"tenant_id"` // The date and time when the resource was created. CreatedAt time.Time `json:"created_at"` // 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:"updated_at"` RevisionNumber int `json:"revision_number"` // UUID of the trunk's parent port PortID string `json:"port_id"` // UUID for the trunk resource ID string `json:"id"` // Display description. Description string `json:"description"` // A list of tags associated with the trunk Tags []string `json:"tags,omitempty"` } func (r commonResult) Extract() (*Trunk, error) { var s struct { Trunk *Trunk `json:"trunk"` } err := r.ExtractInto(&s) return s.Trunk, err } // TrunkPage is the page returned by a pager when traversing a collection of // trunk resources. type TrunkPage struct { pagination.LinkedPageBase } func (page TrunkPage) IsEmpty() (bool, error) { trunks, err := ExtractTrunks(page) return len(trunks) == 0, err } func ExtractTrunks(page pagination.Page) ([]Trunk, error) { var a struct { Trunks []Trunk `json:"trunks"` } err := (page.(TrunkPage)).ExtractInto(&a) return a.Trunks, err } func (r GetSubportsResult) Extract() ([]Subport, error) { var s struct { Subports []Subport `json:"sub_ports"` } err := r.ExtractInto(&s) return s.Subports, err } func (r UpdateSubportsResult) Extract() (t *Trunk, err error) { err = r.ExtractInto(&t) return } golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/trunks/testing/000077500000000000000000000000001367513235700330665ustar00rootroot00000000000000doc.go000066400000000000000000000000451367513235700341020ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/trunks/testing// trunks unit tests package testing fixtures.go000066400000000000000000000236371367513235700352220ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/trunks/testingpackage testing import ( "time" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/trunks" ) const CreateRequest = ` { "trunk": { "admin_state_up": true, "description": "Trunk created by gophercloud", "name": "gophertrunk", "port_id": "c373d2fa-3d3b-4492-924c-aff54dea19b6", "sub_ports": [ { "port_id": "28e452d7-4f8a-4be4-b1e6-7f3db4c0430b", "segmentation_id": 1, "segmentation_type": "vlan" }, { "port_id": "4c8b2bff-9824-4d4c-9b60-b3f6621b2bab", "segmentation_id": 2, "segmentation_type": "vlan" } ] } }` const CreateResponse = ` { "trunk": { "admin_state_up": true, "created_at": "2018-10-03T13:57:24Z", "description": "Trunk created by gophercloud", "id": "f6a9718c-5a64-43e3-944f-4deccad8e78c", "name": "gophertrunk", "port_id": "c373d2fa-3d3b-4492-924c-aff54dea19b6", "project_id": "e153f3f9082240a5974f667cfe1036e3", "revision_number": 1, "status": "ACTIVE", "sub_ports": [ { "port_id": "28e452d7-4f8a-4be4-b1e6-7f3db4c0430b", "segmentation_id": 1, "segmentation_type": "vlan" }, { "port_id": "4c8b2bff-9824-4d4c-9b60-b3f6621b2bab", "segmentation_id": 2, "segmentation_type": "vlan" } ], "tags": [], "tenant_id": "e153f3f9082240a5974f667cfe1036e3", "updated_at": "2018-10-03T13:57:26Z" } }` const CreateNoSubportsRequest = ` { "trunk": { "admin_state_up": true, "description": "Trunk created by gophercloud", "name": "gophertrunk", "port_id": "c373d2fa-3d3b-4492-924c-aff54dea19b6", "sub_ports": [] } }` const CreateNoSubportsResponse = ` { "trunk": { "admin_state_up": true, "created_at": "2018-10-03T13:57:24Z", "description": "Trunk created by gophercloud", "id": "f6a9718c-5a64-43e3-944f-4deccad8e78c", "name": "gophertrunk", "port_id": "c373d2fa-3d3b-4492-924c-aff54dea19b6", "project_id": "e153f3f9082240a5974f667cfe1036e3", "revision_number": 1, "status": "ACTIVE", "sub_ports": [], "tags": [], "tenant_id": "e153f3f9082240a5974f667cfe1036e3", "updated_at": "2018-10-03T13:57:26Z" } }` const ListResponse = ` { "trunks": [ { "admin_state_up": true, "created_at": "2018-10-01T15:29:39Z", "description": "", "id": "3e72aa1b-d0da-48f2-831a-fd1c5f3f99c2", "name": "mytrunk", "port_id": "16c425d3-d7fc-40b8-b94c-cc95da45b270", "project_id": "e153f3f9082240a5974f667cfe1036e3", "revision_number": 3, "status": "ACTIVE", "sub_ports": [ { "port_id": "424da4b7-7868-4db2-bb71-05155601c6e4", "segmentation_id": 11, "segmentation_type": "vlan" } ], "tags": [], "tenant_id": "e153f3f9082240a5974f667cfe1036e3", "updated_at": "2018-10-01T15:43:04Z" }, { "admin_state_up": true, "created_at": "2018-10-03T13:57:24Z", "description": "Trunk created by gophercloud", "id": "f6a9718c-5a64-43e3-944f-4deccad8e78c", "name": "gophertrunk", "port_id": "c373d2fa-3d3b-4492-924c-aff54dea19b6", "project_id": "e153f3f9082240a5974f667cfe1036e3", "revision_number": 1, "status": "ACTIVE", "sub_ports": [ { "port_id": "28e452d7-4f8a-4be4-b1e6-7f3db4c0430b", "segmentation_id": 1, "segmentation_type": "vlan" }, { "port_id": "4c8b2bff-9824-4d4c-9b60-b3f6621b2bab", "segmentation_id": 2, "segmentation_type": "vlan" } ], "tags": [], "tenant_id": "e153f3f9082240a5974f667cfe1036e3", "updated_at": "2018-10-03T13:57:26Z" } ] }` const GetResponse = ` { "trunk": { "admin_state_up": true, "created_at": "2018-10-03T13:57:24Z", "description": "Trunk created by gophercloud", "id": "f6a9718c-5a64-43e3-944f-4deccad8e78c", "name": "gophertrunk", "port_id": "c373d2fa-3d3b-4492-924c-aff54dea19b6", "project_id": "e153f3f9082240a5974f667cfe1036e3", "revision_number": 1, "status": "ACTIVE", "sub_ports": [ { "port_id": "28e452d7-4f8a-4be4-b1e6-7f3db4c0430b", "segmentation_id": 1, "segmentation_type": "vlan" }, { "port_id": "4c8b2bff-9824-4d4c-9b60-b3f6621b2bab", "segmentation_id": 2, "segmentation_type": "vlan" } ], "tags": [], "tenant_id": "e153f3f9082240a5974f667cfe1036e3", "updated_at": "2018-10-03T13:57:26Z" } }` const UpdateRequest = ` { "trunk": { "admin_state_up": false, "description": "gophertrunk updated by gophercloud", "name": "updated_gophertrunk" } }` const UpdateResponse = ` { "trunk": { "admin_state_up": false, "created_at": "2018-10-03T13:57:24Z", "description": "gophertrunk updated by gophercloud", "id": "f6a9718c-5a64-43e3-944f-4deccad8e78c", "name": "updated_gophertrunk", "port_id": "c373d2fa-3d3b-4492-924c-aff54dea19b6", "project_id": "e153f3f9082240a5974f667cfe1036e3", "revision_number": 6, "status": "ACTIVE", "sub_ports": [ { "port_id": "28e452d7-4f8a-4be4-b1e6-7f3db4c0430b", "segmentation_id": 1, "segmentation_type": "vlan" }, { "port_id": "4c8b2bff-9824-4d4c-9b60-b3f6621b2bab", "segmentation_id": 2, "segmentation_type": "vlan" } ], "tags": [], "tenant_id": "e153f3f9082240a5974f667cfe1036e3", "updated_at": "2018-10-03T13:57:33Z" } }` const ListSubportsResponse = ` { "sub_ports": [ { "port_id": "28e452d7-4f8a-4be4-b1e6-7f3db4c0430b", "segmentation_id": 1, "segmentation_type": "vlan" }, { "port_id": "4c8b2bff-9824-4d4c-9b60-b3f6621b2bab", "segmentation_id": 2, "segmentation_type": "vlan" } ] }` const AddSubportsRequest = ListSubportsResponse const AddSubportsResponse = ` { "admin_state_up": true, "created_at": "2018-10-03T13:57:24Z", "description": "Trunk created by gophercloud", "id": "f6a9718c-5a64-43e3-944f-4deccad8e78c", "name": "gophertrunk", "port_id": "c373d2fa-3d3b-4492-924c-aff54dea19b6", "project_id": "e153f3f9082240a5974f667cfe1036e3", "revision_number": 2, "status": "ACTIVE", "sub_ports": [ { "port_id": "28e452d7-4f8a-4be4-b1e6-7f3db4c0430b", "segmentation_id": 1, "segmentation_type": "vlan" }, { "port_id": "4c8b2bff-9824-4d4c-9b60-b3f6621b2bab", "segmentation_id": 2, "segmentation_type": "vlan" } ], "tags": [], "tenant_id": "e153f3f9082240a5974f667cfe1036e3", "updated_at": "2018-10-03T13:57:30Z" }` const RemoveSubportsRequest = ` { "sub_ports": [ { "port_id": "28e452d7-4f8a-4be4-b1e6-7f3db4c0430b" }, { "port_id": "4c8b2bff-9824-4d4c-9b60-b3f6621b2bab" } ] }` const RemoveSubportsResponse = ` { "admin_state_up": true, "created_at": "2018-10-03T13:57:24Z", "description": "Trunk created by gophercloud", "id": "f6a9718c-5a64-43e3-944f-4deccad8e78c", "name": "gophertrunk", "port_id": "c373d2fa-3d3b-4492-924c-aff54dea19b6", "project_id": "e153f3f9082240a5974f667cfe1036e3", "revision_number": 2, "status": "ACTIVE", "sub_ports": [], "tags": [], "tenant_id": "e153f3f9082240a5974f667cfe1036e3", "updated_at": "2018-10-03T13:57:27Z" }` var ExpectedSubports = []trunks.Subport{ { PortID: "28e452d7-4f8a-4be4-b1e6-7f3db4c0430b", SegmentationID: 1, SegmentationType: "vlan", }, { PortID: "4c8b2bff-9824-4d4c-9b60-b3f6621b2bab", SegmentationID: 2, SegmentationType: "vlan", }, } func ExpectedTrunkSlice() (exp []trunks.Trunk, err error) { trunk1CreatedAt, err := time.Parse(time.RFC3339, "2018-10-01T15:29:39Z") if err != nil { return nil, err } trunk1UpdatedAt, err := time.Parse(time.RFC3339, "2018-10-01T15:43:04Z") if err != nil { return nil, err } exp = make([]trunks.Trunk, 2) exp[0] = trunks.Trunk{ AdminStateUp: true, Description: "", ID: "3e72aa1b-d0da-48f2-831a-fd1c5f3f99c2", Name: "mytrunk", PortID: "16c425d3-d7fc-40b8-b94c-cc95da45b270", ProjectID: "e153f3f9082240a5974f667cfe1036e3", TenantID: "e153f3f9082240a5974f667cfe1036e3", RevisionNumber: 3, Status: "ACTIVE", Subports: []trunks.Subport{ { PortID: "424da4b7-7868-4db2-bb71-05155601c6e4", SegmentationID: 11, SegmentationType: "vlan", }, }, Tags: []string{}, CreatedAt: trunk1CreatedAt, UpdatedAt: trunk1UpdatedAt, } trunk2CreatedAt, err := time.Parse(time.RFC3339, "2018-10-03T13:57:24Z") if err != nil { return nil, err } trunk2UpdatedAt, err := time.Parse(time.RFC3339, "2018-10-03T13:57:26Z") if err != nil { return nil, err } exp[1] = trunks.Trunk{ AdminStateUp: true, Description: "Trunk created by gophercloud", ID: "f6a9718c-5a64-43e3-944f-4deccad8e78c", Name: "gophertrunk", PortID: "c373d2fa-3d3b-4492-924c-aff54dea19b6", ProjectID: "e153f3f9082240a5974f667cfe1036e3", TenantID: "e153f3f9082240a5974f667cfe1036e3", RevisionNumber: 1, Status: "ACTIVE", Subports: ExpectedSubports, Tags: []string{}, CreatedAt: trunk2CreatedAt, UpdatedAt: trunk2UpdatedAt, } return } func ExpectedSubportsAddedTrunk() (exp trunks.Trunk, err error) { trunkUpdatedAt, err := time.Parse(time.RFC3339, "2018-10-03T13:57:30Z") if err != nil { return } expectedTrunks, err := ExpectedTrunkSlice() if err != nil { return } exp = expectedTrunks[1] exp.RevisionNumber += 1 exp.UpdatedAt = trunkUpdatedAt return } func ExpectedSubportsRemovedTrunk() (exp trunks.Trunk, err error) { trunkUpdatedAt, err := time.Parse(time.RFC3339, "2018-10-03T13:57:27Z") if err != nil { return } expectedTrunks, err := ExpectedTrunkSlice() if err != nil { return } exp = expectedTrunks[1] exp.RevisionNumber += 1 exp.UpdatedAt = trunkUpdatedAt exp.Subports = []trunks.Subport{} return } requests_test.go000066400000000000000000000211421367513235700362500ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/trunks/testingpackage testing import ( "fmt" "net/http" "testing" fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/trunks" "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/trunks", 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 := trunks.CreateOpts{ Name: "gophertrunk", Description: "Trunk created by gophercloud", AdminStateUp: &iTrue, Subports: []trunks.Subport{ { SegmentationID: 1, SegmentationType: "vlan", PortID: "28e452d7-4f8a-4be4-b1e6-7f3db4c0430b", }, { SegmentationID: 2, SegmentationType: "vlan", PortID: "4c8b2bff-9824-4d4c-9b60-b3f6621b2bab", }, }, } _, err := trunks.Create(fake.ServiceClient(), options).Extract() if err == nil { t.Fatalf("Failed to detect missing parent PortID field") } options.PortID = "c373d2fa-3d3b-4492-924c-aff54dea19b6" n, err := trunks.Create(fake.ServiceClient(), options).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, n.Status, "ACTIVE") expectedTrunks, err := ExpectedTrunkSlice() th.AssertNoErr(t, err) th.AssertDeepEquals(t, &expectedTrunks[1], n) } func TestCreateNoSubports(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/trunks", 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, CreateNoSubportsRequest) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) fmt.Fprintf(w, CreateNoSubportsResponse) }) iTrue := true options := trunks.CreateOpts{ Name: "gophertrunk", Description: "Trunk created by gophercloud", AdminStateUp: &iTrue, PortID: "c373d2fa-3d3b-4492-924c-aff54dea19b6", } n, err := trunks.Create(fake.ServiceClient(), options).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, n.Status, "ACTIVE") th.AssertEquals(t, 0, len(n.Subports)) } func TestDelete(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/trunks/f6a9718c-5a64-43e3-944f-4deccad8e78c", 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 := trunks.Delete(fake.ServiceClient(), "f6a9718c-5a64-43e3-944f-4deccad8e78c") th.AssertNoErr(t, res.Err) } func TestList(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/trunks", 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 trunks.List(client, trunks.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { count++ actual, err := trunks.ExtractTrunks(page) if err != nil { t.Errorf("Failed to extract trunks: %v", err) return false, err } expected, err := ExpectedTrunkSlice() th.AssertNoErr(t, err) 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/trunks/f6a9718c-5a64-43e3-944f-4deccad8e78c", 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 := trunks.Get(fake.ServiceClient(), "f6a9718c-5a64-43e3-944f-4deccad8e78c").Extract() th.AssertNoErr(t, err) expectedTrunks, err := ExpectedTrunkSlice() th.AssertNoErr(t, err) th.CheckDeepEquals(t, &expectedTrunks[1], n) } func TestUpdate(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/trunks/f6a9718c-5a64-43e3-944f-4deccad8e78c", 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) }) iFalse := false name := "updated_gophertrunk" description := "gophertrunk updated by gophercloud" options := trunks.UpdateOpts{ Name: &name, AdminStateUp: &iFalse, Description: &description, } n, err := trunks.Update(fake.ServiceClient(), "f6a9718c-5a64-43e3-944f-4deccad8e78c", options).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, n.Name, name) th.AssertEquals(t, n.AdminStateUp, iFalse) th.AssertEquals(t, n.Description, description) } func TestGetSubports(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/trunks/f6a9718c-5a64-43e3-944f-4deccad8e78c/get_subports", 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, ListSubportsResponse) }) client := fake.ServiceClient() subports, err := trunks.GetSubports(client, "f6a9718c-5a64-43e3-944f-4deccad8e78c").Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, ExpectedSubports, subports) } func TestMissingFields(t *testing.T) { iTrue := true opts := trunks.CreateOpts{ Name: "gophertrunk", PortID: "c373d2fa-3d3b-4492-924c-aff54dea19b6", Description: "Trunk created by gophercloud", AdminStateUp: &iTrue, Subports: []trunks.Subport{ { SegmentationID: 1, SegmentationType: "vlan", PortID: "28e452d7-4f8a-4be4-b1e6-7f3db4c0430b", }, { SegmentationID: 2, SegmentationType: "vlan", PortID: "4c8b2bff-9824-4d4c-9b60-b3f6621b2bab", }, { PortID: "4c8b2bff-9824-4d4c-9b60-b3f6621b2bab", }, }, } _, err := opts.ToTrunkCreateMap() if err == nil { t.Fatalf("Failed to detect missing subport fields") } } func TestAddSubports(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/trunks/f6a9718c-5a64-43e3-944f-4deccad8e78c/add_subports", 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, AddSubportsRequest) w.WriteHeader(http.StatusOK) fmt.Fprintf(w, AddSubportsResponse) }) client := fake.ServiceClient() opts := trunks.AddSubportsOpts{ Subports: ExpectedSubports, } trunk, err := trunks.AddSubports(client, "f6a9718c-5a64-43e3-944f-4deccad8e78c", opts).Extract() th.AssertNoErr(t, err) expectedTrunk, err := ExpectedSubportsAddedTrunk() th.AssertNoErr(t, err) th.CheckDeepEquals(t, &expectedTrunk, trunk) } func TestRemoveSubports(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/trunks/f6a9718c-5a64-43e3-944f-4deccad8e78c/remove_subports", 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, RemoveSubportsRequest) w.WriteHeader(http.StatusOK) fmt.Fprintf(w, RemoveSubportsResponse) }) client := fake.ServiceClient() opts := trunks.RemoveSubportsOpts{ Subports: []trunks.RemoveSubport{ {PortID: "28e452d7-4f8a-4be4-b1e6-7f3db4c0430b"}, {PortID: "4c8b2bff-9824-4d4c-9b60-b3f6621b2bab"}, }, } trunk, err := trunks.RemoveSubports(client, "f6a9718c-5a64-43e3-944f-4deccad8e78c", opts).Extract() th.AssertNoErr(t, err) expectedTrunk, err := ExpectedSubportsRemovedTrunk() th.AssertNoErr(t, err) th.CheckDeepEquals(t, &expectedTrunk, trunk) } golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/trunks/urls.go000066400000000000000000000021301367513235700327210ustar00rootroot00000000000000package trunks import "github.com/gophercloud/gophercloud" const resourcePath = "trunks" func rootURL(c *gophercloud.ServiceClient) string { return c.ServiceURL(resourcePath) } func resourceURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL(resourcePath, id) } func createURL(c *gophercloud.ServiceClient) string { return rootURL(c) } func deleteURL(c *gophercloud.ServiceClient, id string) string { return resourceURL(c, id) } func listURL(c *gophercloud.ServiceClient) string { return rootURL(c) } func getURL(c *gophercloud.ServiceClient, id string) string { return resourceURL(c, id) } func updateURL(c *gophercloud.ServiceClient, id string) string { return resourceURL(c, id) } func getSubportsURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL(resourcePath, id, "get_subports") } func addSubportsURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL(resourcePath, id, "add_subports") } func removeSubportsURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL(resourcePath, id, "remove_subports") } golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/vlantransparent/000077500000000000000000000000001367513235700333055ustar00rootroot00000000000000doc.go000066400000000000000000000042501367513235700343230ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/vlantransparent/* Package vlantransparent provides the ability to retrieve and manage networks with the vlan-transparent extension through the Neutron API. Example of Listing Networks with the vlan-transparent extension iTrue := true networkListOpts := networks.ListOpts{} listOpts := vlantransparent.ListOptsExt{ ListOptsBuilder: networkListOpts, VLANTransparent: &iTrue, } type NetworkWithVLANTransparentExt struct { networks.Network vlantransparent.NetworkVLANTransparentExt } var allNetworks []NetworkWithVLANTransparentExt 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 of Getting a Network with the vlan-transparent extension var network struct { networks.Network vlantransparent.TransparentExt } err := networks.Get(networkClient, "db193ab3-96e3-4cb3-8fc5-05f4296d0324").ExtractInto(&network) if err != nil { panic(err) } fmt.Println("%+v\n", network) Example of Creating Network with the vlan-transparent extension iTrue := true networkCreateOpts := networks.CreateOpts{ Name: "private", } createOpts := vlantransparent.CreateOptsExt{ CreateOptsBuilder: &networkCreateOpts, VLANTransparent: &iTrue, } var network struct { networks.Network vlantransparent.TransparentExt } err := networks.Create(networkClient, createOpts).ExtractInto(&network) if err != nil { panic(err) } fmt.Println("%+v\n", network) Example of Updating Network with the vlan-transparent extension iFalse := false networkUpdateOpts := networks.UpdateOpts{ Name: "new_network_name", } updateOpts := vlantransparent.UpdateOptsExt{ UpdateOptsBuilder: &networkUpdateOpts, VLANTransparent: &iFalse, } var network struct { networks.Network vlantransparent.TransparentExt } err := networks.Update(networkClient, updateOpts).ExtractInto(&network) if err != nil { panic(err) } fmt.Println("%+v\n", network) */ package vlantransparent requests.go000066400000000000000000000045571367513235700354430ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/vlantransparentpackage vlantransparent import ( "net/url" "strconv" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack/networking/v2/networks" ) // ListOptsExt adds the vlan-transparent network options to the base ListOpts. type ListOptsExt struct { networks.ListOptsBuilder VLANTransparent *bool `q:"vlan_transparent"` } // ToNetworkListQuery adds the vlan_transparent 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.VLANTransparent != nil { v := strconv.FormatBool(*opts.VLANTransparent) params.Add("vlan_transparent", v) } q = &url.URL{RawQuery: params.Encode()} return q.String(), err } // CreateOptsExt is the structure used when creating new vlan-transparent // network resources. It embeds networks.CreateOpts and so inherits all of its // required and optional fields, with the addition of the VLANTransparent field. type CreateOptsExt struct { networks.CreateOptsBuilder VLANTransparent *bool `json:"vlan_transparent,omitempty"` } // ToNetworkCreateMap adds the vlan_transparent option 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.VLANTransparent == nil { return base, nil } networkMap := base["network"].(map[string]interface{}) networkMap["vlan_transparent"] = opts.VLANTransparent return base, nil } // UpdateOptsExt is the structure used when updating existing vlan-transparent // network resources. It embeds networks.UpdateOpts and so inherits all of its // required and optional fields, with the addition of the VLANTransparent field. type UpdateOptsExt struct { networks.UpdateOptsBuilder VLANTransparent *bool `json:"vlan_transparent,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.VLANTransparent == nil { return base, nil } networkMap := base["network"].(map[string]interface{}) networkMap["vlan_transparent"] = opts.VLANTransparent return base, nil } results.go000066400000000000000000000004421367513235700352560ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/vlantransparentpackage vlantransparent // TransparentExt represents a decorated form of a network with // "vlan-transparent" extension attributes. type TransparentExt struct { // VLANTransparent whether the network is a VLAN transparent network or not. VLANTransparent bool `json:"vlan_transparent"` } testing/000077500000000000000000000000001367513235700347035ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/vlantransparentdoc.go000066400000000000000000000000701367513235700357740ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/vlantransparent/testing// vlantransparent extension unit tests package testing fixtures.go000066400000000000000000000077131367513235700371130ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/vlantransparent/testingpackage testing // NetworksVLANTransparentListResult represents raw HTTP response for the List // request. const NetworksVLANTransparentListResult = ` { "networks": [ { "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, "vlan_transparent": true }, { "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 } ] }` // NetworksVLANTransparentGetResult represents raw HTTP response for the Get // request. const NetworksVLANTransparentGetResult = ` { "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": 1234567890, "provider:physical_network": null, "provider:network_type": "local", "router:external": false, "port_security_enabled": false, "vlan_transparent": true } }` // NetworksVLANTransparentCreateRequest represents raw HTTP Create request. const NetworksVLANTransparentCreateRequest = ` { "network": { "name": "private", "admin_state_up": true, "vlan_transparent": true } }` // NetworksVLANTransparentCreateResult represents raw HTTP response for the // Create request. const NetworksVLANTransparentCreateResult = ` { "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": 1234567890, "provider:physical_network": null, "provider:network_type": "local", "router:external": false, "port_security_enabled": false, "vlan_transparent": true } } ` // NetworksVLANTransparentUpdateRequest represents raw HTTP Update request. const NetworksVLANTransparentUpdateRequest = ` { "network": { "name": "new_network_name", "admin_state_up": false, "vlan_transparent": false } }` // NetworksVLANTransparentUpdateResult represents raw HTTP response for the // Update request. const NetworksVLANTransparentUpdateResult = ` { "network": { "status": "ACTIVE", "subnets": [ "08eae331-0402-425a-923c-34f7cfe39c1b" ], "name": "new_network_name", "admin_state_up": false, "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, "vlan_transparent": false } } ` requests_test.go000066400000000000000000000126371367513235700401550ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/vlantransparent/testingpackage testing import ( "fmt" "net/http" "testing" fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/vlantransparent" "github.com/gophercloud/gophercloud/openstack/networking/v2/networks" 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, NetworksVLANTransparentListResult) }) type networkVLANTransparentExt struct { networks.Network vlantransparent.TransparentExt } var actual []networkVLANTransparentExt 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, "db193ab3-96e3-4cb3-8fc5-05f4296d0324", actual[0].ID) th.AssertEquals(t, "private", actual[0].Name) th.AssertEquals(t, true, actual[0].AdminStateUp) th.AssertEquals(t, "ACTIVE", actual[0].Status) th.AssertDeepEquals(t, []string{"08eae331-0402-425a-923c-34f7cfe39c1b"}, actual[0].Subnets) th.AssertEquals(t, "26a7980765d0414dbc1fc1f88cdb7e6e", actual[0].TenantID) th.AssertEquals(t, false, actual[0].Shared) th.AssertEquals(t, true, actual[0].VLANTransparent) } func TestGet(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/networks/db193ab3-96e3-4cb3-8fc5-05f4296d0324", 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, NetworksVLANTransparentGetResult) }) var s struct { networks.Network vlantransparent.TransparentExt } err := networks.Get(fake.ServiceClient(), "db193ab3-96e3-4cb3-8fc5-05f4296d0324").ExtractInto(&s) th.AssertNoErr(t, err) th.AssertEquals(t, "db193ab3-96e3-4cb3-8fc5-05f4296d0324", s.ID) th.AssertEquals(t, "private", s.Name) th.AssertEquals(t, true, s.AdminStateUp) th.AssertEquals(t, "ACTIVE", s.Status) th.AssertDeepEquals(t, []string{"08eae331-0402-425a-923c-34f7cfe39c1b"}, s.Subnets) th.AssertEquals(t, "26a7980765d0414dbc1fc1f88cdb7e6e", s.TenantID) th.AssertEquals(t, false, s.Shared) th.AssertEquals(t, true, s.VLANTransparent) } 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, NetworksVLANTransparentCreateRequest) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) fmt.Fprintf(w, NetworksVLANTransparentCreateResult) }) iTrue := true networkCreateOpts := networks.CreateOpts{ Name: "private", AdminStateUp: &iTrue, } vlanTransparentCreateOpts := vlantransparent.CreateOptsExt{ CreateOptsBuilder: &networkCreateOpts, VLANTransparent: &iTrue, } var s struct { networks.Network vlantransparent.TransparentExt } err := networks.Create(fake.ServiceClient(), vlanTransparentCreateOpts).ExtractInto(&s) th.AssertNoErr(t, err) th.AssertEquals(t, "db193ab3-96e3-4cb3-8fc5-05f4296d0324", s.ID) th.AssertEquals(t, "private", s.Name) th.AssertEquals(t, true, s.AdminStateUp) th.AssertEquals(t, "ACTIVE", s.Status) th.AssertDeepEquals(t, []string{"08eae331-0402-425a-923c-34f7cfe39c1b"}, s.Subnets) th.AssertEquals(t, "26a7980765d0414dbc1fc1f88cdb7e6e", s.TenantID) th.AssertEquals(t, false, s.Shared) th.AssertEquals(t, true, s.VLANTransparent) } 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, NetworksVLANTransparentUpdateRequest) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, NetworksVLANTransparentUpdateResult) }) iFalse := false name := "new_network_name" networkUpdateOpts := networks.UpdateOpts{ Name: &name, AdminStateUp: &iFalse, } vlanTransparentUpdateOpts := vlantransparent.UpdateOptsExt{ UpdateOptsBuilder: &networkUpdateOpts, VLANTransparent: &iFalse, } var s struct { networks.Network vlantransparent.TransparentExt } err := networks.Update(fake.ServiceClient(), "4e8e5957-649f-477b-9e5b-f1f75b21c03c", vlanTransparentUpdateOpts).ExtractInto(&s) th.AssertNoErr(t, err) th.AssertEquals(t, "db193ab3-96e3-4cb3-8fc5-05f4296d0324", s.ID) th.AssertEquals(t, "new_network_name", s.Name) th.AssertEquals(t, false, s.AdminStateUp) th.AssertEquals(t, "ACTIVE", s.Status) th.AssertDeepEquals(t, []string{"08eae331-0402-425a-923c-34f7cfe39c1b"}, s.Subnets) th.AssertEquals(t, "26a7980765d0414dbc1fc1f88cdb7e6e", s.TenantID) th.AssertEquals(t, false, s.Shared) th.AssertEquals(t, false, s.VLANTransparent) } golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/vpnaas/000077500000000000000000000000001367513235700313535ustar00rootroot00000000000000endpointgroups/000077500000000000000000000000001367513235700343545ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/vpnaasdoc.go000066400000000000000000000024571367513235700354600ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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.go000066400000000000000000000114661367513235700365660ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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 } resp, err := c.Post(rootURL(c), b, &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get retrieves a particular endpoint group based on its unique ID. func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { resp, err := c.Get(resourceURL(c, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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) { resp, err := c.Delete(resourceURL(c, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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 } resp, err := c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } results.go000066400000000000000000000060261367513235700364100ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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/000077500000000000000000000000001367513235700360315ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/vpnaas/endpointgroupsrequests_test.go000066400000000000000000000155751367513235700413070ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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.go000066400000000000000000000005421367513235700356710ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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) } golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/vpnaas/ikepolicies/000077500000000000000000000000001367513235700336535ustar00rootroot00000000000000doc.go000066400000000000000000000026551367513235700347000ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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.go000066400000000000000000000170571367513235700360100ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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 } resp, err := c.Post(rootURL(c), b, &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get retrieves a particular IKE policy based on its unique ID. func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { resp, err := c.Get(resourceURL(c, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete will permanently delete a particular IKE policy based on its // unique ID. func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { resp, err := c.Delete(resourceURL(c, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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 } resp, err := c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } results.go000066400000000000000000000065521367513235700356340ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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/000077500000000000000000000000001367513235700352515ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/vpnaas/ikepoliciesrequests_test.go000066400000000000000000000204551367513235700405200ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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.go000066400000000000000000000005331367513235700351110ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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/000077500000000000000000000000001367513235700341275ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/vpnaasdoc.go000066400000000000000000000023321367513235700352230ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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.go000066400000000000000000000172561367513235700363440ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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 } resp, err := c.Post(rootURL(c), b, &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete will permanently delete a particular IPSec policy based on its // unique ID. func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { resp, err := c.Delete(resourceURL(c, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get retrieves a particular IPSec policy based on its unique ID. func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { resp, err := c.Get(resourceURL(c, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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 } resp, err := c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } results.go000066400000000000000000000066131367513235700361650ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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/000077500000000000000000000000001367513235700356045ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/vpnaas/ipsecpoliciesrequests_test.go000066400000000000000000000216471367513235700410570ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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.go000066400000000000000000000005371367513235700354500ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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) } golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/vpnaas/services/000077500000000000000000000000001367513235700331765ustar00rootroot00000000000000doc.go000066400000000000000000000026551367513235700342230ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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.go000066400000000000000000000121111367513235700353150ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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 } resp, err := c.Post(rootURL(c), b, &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete will permanently delete a particular VPN service based on its // unique ID. func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { resp, err := c.Delete(resourceURL(c, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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 } resp, err := c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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) { resp, err := c.Get(resourceURL(c, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } results.go000066400000000000000000000070201367513235700351460ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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/000077500000000000000000000000001367513235700345745ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/vpnaas/servicesrequests_test.go000066400000000000000000000173431367513235700400450ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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.go000066400000000000000000000005301367513235700344310ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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/000077500000000000000000000000001367513235700345035ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/vpnaasdoc.go000066400000000000000000000035051367513235700356020ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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.go000066400000000000000000000223251367513235700367110ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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 } resp, err := c.Post(rootURL(c), b, &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete will permanently delete a particular IPSec site connection based on its // unique ID. func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { resp, err := c.Delete(resourceURL(c, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get retrieves a particular IPSec site connection based on its unique ID. func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { resp, err := c.Get(resourceURL(c, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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 } resp, err := c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } results.go000066400000000000000000000121611367513235700365340ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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/000077500000000000000000000000001367513235700361605ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/extensions/vpnaas/siteconnectionsrequests_test.go000066400000000000000000000321761367513235700414320ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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.go000066400000000000000000000005521367513235700360210ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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.12.0/openstack/networking/v2/networks/000077500000000000000000000000001367513235700275405ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/networks/doc.go000066400000000000000000000030061367513235700306330ustar00rootroot00000000000000/* 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" name := "new_name" updateOpts := networks.UpdateOpts{ Name: &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 golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/networks/requests.go000066400000000000000000000123401367513235700317420ustar00rootroot00000000000000package 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"` Description string `q:"description"` 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"` Tags string `q:"tags"` TagsAny string `q:"tags-any"` NotTags string `q:"not-tags"` NotTagsAny string `q:"not-tags-any"` } // 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) { resp, err := c.Get(getURL(c, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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"` Description string `json:"description,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 } resp, err := c.Post(createURL(c), b, &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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"` Description *string `json:"description,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 } resp, err := c.Put(updateURL(c, networkID), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete accepts a unique ID and deletes the network associated with it. func Delete(c *gophercloud.ServiceClient, networkID string) (r DeleteResult) { resp, err := c.Delete(deleteURL(c, networkID), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/networks/results.go000066400000000000000000000110461367513235700315720ustar00rootroot00000000000000package networks 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 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"` // Description for the network Description string `json:"description"` // 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"` // UpdatedAt and CreatedAt contain ISO-8601 timestamps of when the state of the // network last changed, and when it was created. UpdatedAt time.Time `json:"-"` CreatedAt time.Time `json:"-"` // 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"` // Tags optionally set via extensions/attributestags Tags []string `json:"tags"` } func (r *Network) UnmarshalJSON(b []byte) error { type tmp Network // Support for older neutron time format var s1 struct { tmp CreatedAt gophercloud.JSONRFC3339NoZ `json:"created_at"` UpdatedAt gophercloud.JSONRFC3339NoZ `json:"updated_at"` } err := json.Unmarshal(b, &s1) if err == nil { *r = Network(s1.tmp) r.CreatedAt = time.Time(s1.CreatedAt) r.UpdatedAt = time.Time(s1.UpdatedAt) return nil } // Support for newer neutron time format var s2 struct { tmp CreatedAt time.Time `json:"created_at"` UpdatedAt time.Time `json:"updated_at"` } err = json.Unmarshal(b, &s2) if err != nil { return err } *r = Network(s2.tmp) r.CreatedAt = time.Time(s2.CreatedAt) r.UpdatedAt = time.Time(s2.UpdatedAt) return nil } // 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") } golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/networks/testing/000077500000000000000000000000001367513235700312155ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/networks/testing/doc.go000066400000000000000000000000471367513235700323120ustar00rootroot00000000000000// networks unit tests package testing golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/networks/testing/fixtures.go000066400000000000000000000143671367513235700334300ustar00rootroot00000000000000package testing import ( "time" "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", "created_at": "2019-06-30T04:15:37", "updated_at": "2019-06-30T05:18:49", "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, "dns_domain": "local.", "mtu": 1500 }, { "status": "ACTIVE", "subnets": [ "08eae331-0402-425a-923c-34f7cfe39c1b" ], "name": "private", "admin_state_up": true, "tenant_id": "26a7980765d0414dbc1fc1f88cdb7e6e", "created_at": "2019-06-30T04:15:37Z", "updated_at": "2019-06-30T05:18:49Z", "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, "dns_domain": "", "mtu": 1500 } ] }` const GetResponse = ` { "network": { "status": "ACTIVE", "subnets": [ "54d6f61d-db07-451c-9ab3-b9609b6b6f0b" ], "name": "public", "admin_state_up": true, "tenant_id": "4fd44f30292945e481c7b8a0c8908869", "created_at": "2019-06-30T04:15:37", "updated_at": "2019-06-30T05:18:49", "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, "dns_domain": "local.", "mtu": 1500 } }` 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", "created_at": "2019-06-30T04:15:37Z", "updated_at": "2019-06-30T05:18:49Z", "shared": false, "id": "db193ab3-96e3-4cb3-8fc5-05f4296d0324", "provider:segmentation_id": 9876543210, "provider:physical_network": null, "provider:network_type": "local", "dns_domain": "" } }` 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", "created_at": "2019-06-30T04:15:37Z", "updated_at": "2019-06-30T05:18:49Z", "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", "created_at": "2019-06-30T04:15:37Z", "updated_at": "2019-06-30T05:18:49Z", "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", "created_at": "2019-06-30T04:15:37Z", "updated_at": "2019-06-30T05:18:49Z", "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 createdTime, _ = time.Parse(time.RFC3339, "2019-06-30T04:15:37Z") var updatedTime, _ = time.Parse(time.RFC3339, "2019-06-30T05:18:49Z") var ( Network1 = networks.Network{ Status: "ACTIVE", Subnets: []string{"54d6f61d-db07-451c-9ab3-b9609b6b6f0b"}, Name: "public", AdminStateUp: true, TenantID: "4fd44f30292945e481c7b8a0c8908869", CreatedAt: createdTime, UpdatedAt: updatedTime, 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", CreatedAt: createdTime, UpdatedAt: updatedTime, Shared: false, ID: "db193ab3-96e3-4cb3-8fc5-05f4296d0324", } ) var ExpectedNetworkSlice = []networks.Network{Network1, Network2} requests_test.go000066400000000000000000000233451367513235700344060ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/networks/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/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 err := 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 }) th.AssertNoErr(t, err) 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") th.AssertEquals(t, allNetworks[0].CreatedAt.Format(time.RFC3339), "2019-06-30T04:15:37Z") th.AssertEquals(t, allNetworks[0].UpdatedAt.Format(time.RFC3339), "2019-06-30T05:18:49Z") th.AssertEquals(t, allNetworks[1].CreatedAt.Format(time.RFC3339), "2019-06-30T04:15:37Z") th.AssertEquals(t, allNetworks[1].UpdatedAt.Format(time.RFC3339), "2019-06-30T05:18:49Z") } 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) th.AssertEquals(t, n.CreatedAt.Format(time.RFC3339), "2019-06-30T04:15:37Z") th.AssertEquals(t, n.UpdatedAt.Format(time.RFC3339), "2019-06-30T05:18:49Z") } 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) th.AssertEquals(t, n.CreatedAt.Format(time.RFC3339), "2019-06-30T04:15:37Z") th.AssertEquals(t, n.UpdatedAt.Format(time.RFC3339), "2019-06-30T05:18:49Z") } 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 name := "new_network_name" options := networks.UpdateOpts{Name: &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") th.AssertEquals(t, n.CreatedAt.Format(time.RFC3339), "2019-06-30T04:15:37Z") th.AssertEquals(t, n.UpdatedAt.Format(time.RFC3339), "2019-06-30T05:18:49Z") } 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) } golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/networks/urls.go000066400000000000000000000012611367513235700310540ustar00rootroot00000000000000package 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.12.0/openstack/networking/v2/ports/000077500000000000000000000000001367513235700270335ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/ports/doc.go000066400000000000000000000034641367513235700301360ustar00rootroot00000000000000/* 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 golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/ports/requests.go000066400000000000000000000150151367513235700312370ustar00rootroot00000000000000package ports import ( "fmt" "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 { 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"` Description string `q:"description"` 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"` Tags string `q:"tags"` TagsAny string `q:"tags-any"` NotTags string `q:"not-tags"` NotTagsAny string `q:"not-tags-any"` FixedIPs []FixedIPOpts } type FixedIPOpts struct { IPAddress string IPAddressSubstr string SubnetID string } func (f FixedIPOpts) String() string { var res []string if f.IPAddress != "" { res = append(res, fmt.Sprintf("ip_address=%s", f.IPAddress)) } if f.IPAddressSubstr != "" { res = append(res, fmt.Sprintf("ip_address_substr=%s", f.IPAddressSubstr)) } if f.SubnetID != "" { res = append(res, fmt.Sprintf("subnet_id=%s", f.SubnetID)) } return strings.Join(res, ",") } // ToPortListQuery formats a ListOpts into a query string. func (opts ListOpts) ToPortListQuery() (string, error) { q, err := gophercloud.BuildQueryString(opts) params := q.Query() for _, fixedIP := range opts.FixedIPs { params.Add("fixed_ips", fixedIP.String()) } q = &url.URL{RawQuery: params.Encode()} 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) { resp, err := c.Get(getURL(c, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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"` Description string `json:"description,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 } resp, err := c.Post(createURL(c), b, &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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"` Description *string `json:"description,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 } resp, err := c.Put(updateURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete accepts a unique ID and deletes the port associated with it. func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { resp, err := c.Delete(deleteURL(c, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/ports/results.go000066400000000000000000000104251367513235700310650ustar00rootroot00000000000000package 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"` // Describes the port. Description string `json:"description"` // 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"` // Tags optionally set via extensions/attributestags Tags []string `json:"tags"` } // 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") } golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/ports/testing/000077500000000000000000000000001367513235700305105ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/ports/testing/doc.go000066400000000000000000000000441367513235700316020ustar00rootroot00000000000000// ports unit tests package testing golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/ports/testing/fixtures.go000066400000000000000000000447641367513235700327270ustar00rootroot00000000000000package 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": [], "dns_name": "test-port", "dns_assignment": [ { "hostname": "test-port", "ip_address": "172.24.4.2", "fqdn": "test-port.openstack.local." } ], "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": [], "dns_name": "test-port", "dns_assignment": [ { "hostname": "test-port", "ip_address": "172.24.4.2", "fqdn": "test-port.openstack.local." } ], "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": "" } } ` golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/ports/testing/requests_test.go000066400000000000000000000632441367513235700337620ustar00rootroot00000000000000package testing import ( "fmt" "net/http" "net/url" "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.AssertNoErr(t, err) 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) }) name := "new_port_name" options := ports.UpdateOpts{ Name: &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) }) name := "new_port_name" options := ports.UpdateOpts{ Name: &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) }) name := "new_port_name" options := ports.UpdateOpts{ Name: &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) }) name := "new_port_name" options := ports.UpdateOpts{ Name: &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) }) name := "new_port_name" options := ports.UpdateOpts{ Name: &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) }) name := "updated-port-with-dhcp-opts" portUpdateOpts := ports.UpdateOpts{ Name: &name, 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) } func TestPortsListOpts(t *testing.T) { for expected, opts := range map[string]ports.ListOpts{ newValue("fixed_ips", `ip_address=1.2.3.4,subnet_id=42`): ports.ListOpts{ FixedIPs: []ports.FixedIPOpts{{IPAddress: "1.2.3.4", SubnetID: "42"}}, }, } { actual, _ := opts.ToPortListQuery() th.AssertEquals(t, expected, actual) } } func newValue(param, value string) string { v := url.Values{} v.Add(param, value) return "?" + v.Encode() } golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/ports/urls.go000066400000000000000000000012501367513235700303450ustar00rootroot00000000000000package 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.12.0/openstack/networking/v2/subnets/000077500000000000000000000000001367513235700273475ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/subnets/doc.go000066400000000000000000000060631367513235700304500ustar00rootroot00000000000000/* 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" dnsNameservers := []string{"8.8.8.8"} name := "new_name" updateOpts := subnets.UpdateOpts{ Name: &name, DNSNameservers: &dnsNameservers, } 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 golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/subnets/requests.go000066400000000000000000000204611367513235700315540ustar00rootroot00000000000000package 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"` Description string `q:"description"` 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"` Tags string `q:"tags"` TagsAny string `q:"tags-any"` NotTags string `q:"not-tags"` NotTagsAny string `q:"not-tags-any"` } // 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) { resp, err := c.Get(getURL(c, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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"` // Description of the subnet. Description string `json:"description,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"` // Prefixlen is used when user creates a subnet from the subnetpool. It will // overwrite the "default_prefixlen" value of the referenced subnetpool. Prefixlen int `json:"prefixlen,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 } resp, err := c.Post(createURL(c), b, &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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"` // Description of the subnet. Description *string `json:"description,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 } resp, err := c.Put(updateURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete accepts a unique ID and deletes the subnet associated with it. func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { resp, err := c.Delete(deleteURL(c, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/subnets/results.go000066400000000000000000000106571367513235700314100ustar00rootroot00000000000000package 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"` // Description for the subnet. Description string `json:"description"` // 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"` // Tags optionally set via extensions/attributestags Tags []string `json:"tags"` } // 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 } golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/subnets/testing/000077500000000000000000000000001367513235700310245ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/subnets/testing/doc.go000066400000000000000000000000461367513235700321200ustar00rootroot00000000000000// subnets unit tests package testing golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/subnets/testing/fixtures.go000066400000000000000000000344451367513235700332360ustar00rootroot00000000000000package 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 SubnetCreateRequestWithPrefixlen = ` { "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", "prefixlen": 12 } } ` 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.go000066400000000000000000000457421367513235700342220ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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 TestCreateWithPrefixlen(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, SubnetCreateRequestWithPrefixlen) 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", Prefixlen: 12, } 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) }) dnsNameservers := []string{"foo"} name := "my_new_subnet" opts := subnets.UpdateOpts{ Name: &name, DNSNameservers: &dnsNameservers, 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" name := "my_new_subnet" opts := subnets.UpdateOpts{ Name: &name, 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 = "" name := "my_new_subnet" opts := subnets.UpdateOpts{ Name: &name, 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", }, } name := "my_new_subnet" opts := subnets.UpdateOpts{ Name: &name, 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) }) name := "my_new_subnet" opts := subnets.UpdateOpts{ Name: &name, 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) } golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/subnets/testing/results_test.go000066400000000000000000000026331367513235700341170ustar00rootroot00000000000000package 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") } golang-github-gophercloud-gophercloud-0.12.0/openstack/networking/v2/subnets/urls.go000066400000000000000000000012561367513235700306670ustar00rootroot00000000000000package 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.12.0/openstack/objectstorage/000077500000000000000000000000001367513235700260015ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/objectstorage/v1/000077500000000000000000000000001367513235700263275ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/objectstorage/v1/accounts/000077500000000000000000000000001367513235700301465ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/objectstorage/v1/accounts/doc.go000066400000000000000000000014241367513235700312430ustar00rootroot00000000000000/* 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 golang-github-gophercloud-gophercloud-0.12.0/openstack/objectstorage/v1/accounts/requests.go000066400000000000000000000053741367513235700323610ustar00rootroot00000000000000package 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}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, 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}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } golang-github-gophercloud-gophercloud-0.12.0/openstack/objectstorage/v1/accounts/results.go000066400000000000000000000054301367513235700322000ustar00rootroot00000000000000package accounts import ( "encoding/json" "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:"Content-Length,string"` 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 Date gophercloud.JSONRFC1123 `json:"Date"` } err := json.Unmarshal(b, &s) if err != nil { return err } *r = UpdateHeader(s.tmp) 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:"X-Account-Bytes-Used,string"` QuotaBytes *int64 `json:"X-Account-Meta-Quota-Bytes,string"` ContainerCount int64 `json:"X-Account-Container-Count,string"` ContentLength int64 `json:"Content-Length,string"` ObjectCount int64 `json:"X-Account-Object-Count,string"` 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 Date string `json:"Date"` } err := json.Unmarshal(b, &s) if err != nil { return err } *r = GetHeader(s.tmp) 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 } golang-github-gophercloud-gophercloud-0.12.0/openstack/objectstorage/v1/accounts/testing/000077500000000000000000000000001367513235700316235ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/objectstorage/v1/accounts/testing/doc.go000066400000000000000000000000471367513235700327200ustar00rootroot00000000000000// accounts unit tests package testing golang-github-gophercloud-gophercloud-0.12.0/openstack/objectstorage/v1/accounts/testing/fixtures.go000066400000000000000000000037321367513235700340300ustar00rootroot00000000000000package 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 UTC") 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 UTC") 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 UTC") w.WriteHeader(http.StatusNoContent) }) } requests_test.go000066400000000000000000000042651367513235700350140ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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" ) 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, time.UTC), } 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, time.UTC), } 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, time.UTC), } actual, err := res.Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, expected, actual) } golang-github-gophercloud-gophercloud-0.12.0/openstack/objectstorage/v1/accounts/urls.go000066400000000000000000000003221367513235700314570ustar00rootroot00000000000000package accounts import "github.com/gophercloud/gophercloud" func getURL(c *gophercloud.ServiceClient) string { return c.Endpoint } func updateURL(c *gophercloud.ServiceClient) string { return getURL(c) } golang-github-gophercloud-gophercloud-0.12.0/openstack/objectstorage/v1/containers/000077500000000000000000000000001367513235700304745ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/objectstorage/v1/containers/doc.go000066400000000000000000000042001367513235700315640ustar00rootroot00000000000000/* 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", }, RemoveMetadata: []string{ "foo", }, } 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 golang-github-gophercloud-gophercloud-0.12.0/openstack/objectstorage/v1/containers/requests.go000066400000000000000000000174561367513235700327130ustar00rootroot00000000000000package containers 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 { 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"` HistoryLocation string `h:"X-History-Location"` TempURLKey string `h:"X-Container-Meta-Temp-URL-Key"` TempURLKey2 string `h:"X-Container-Meta-Temp-URL-Key-2"` } // 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, url.QueryEscape(containerName)), &gophercloud.RequestOpts{ MoreHeaders: h, OkCodes: []int{201, 202, 204}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // BulkDelete is a function that bulk deletes containers. func BulkDelete(c *gophercloud.ServiceClient, containers []string) (r BulkDeleteResult) { // urlencode container names to be on the safe side // https://github.com/openstack/swift/blob/stable/train/swift/common/middleware/bulk.py#L160 // https://github.com/openstack/swift/blob/stable/train/swift/common/swob.py#L302 encodedContainers := make([]string, len(containers)) for i, v := range containers { encodedContainers[i] = url.QueryEscape(v) } b := strings.NewReader(strings.Join(encodedContainers, "\n") + "\n") resp, err := c.Post(bulkDeleteURL(c), b, &r.Body, &gophercloud.RequestOpts{ MoreHeaders: map[string]string{ "Accept": "application/json", "Content-Type": "text/plain", }, OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete is a function that deletes a container. func Delete(c *gophercloud.ServiceClient, containerName string) (r DeleteResult) { resp, err := c.Delete(deleteURL(c, url.QueryEscape(containerName)), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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 RemoveMetadata []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"` RemoveHistoryLocation string `h:"X-Remove-History-Location"` HistoryLocation string `h:"X-History-Location"` TempURLKey string `h:"X-Container-Meta-Temp-URL-Key"` TempURLKey2 string `h:"X-Container-Meta-Temp-URL-Key-2"` } // 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 } for _, k := range opts.RemoveMetadata { h["X-Remove-Container-Meta-"+k] = "remove" } 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, url.QueryEscape(containerName)), &gophercloud.RequestOpts{ MoreHeaders: h, OkCodes: []int{201, 202, 204}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, 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, url.QueryEscape(containerName)), &gophercloud.RequestOpts{ MoreHeaders: h, OkCodes: []int{200, 204}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } golang-github-gophercloud-gophercloud-0.12.0/openstack/objectstorage/v1/containers/results.go000066400000000000000000000175301367513235700325320ustar00rootroot00000000000000package containers import ( "encoding/json" "fmt" "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:"X-Container-Bytes-Used,string"` ContentLength int64 `json:"Content-Length,string"` ContentType string `json:"Content-Type"` Date time.Time `json:"-"` ObjectCount int64 `json:"X-Container-Object-Count,string"` Read []string `json:"-"` TransID string `json:"X-Trans-Id"` VersionsLocation string `json:"X-Versions-Location"` HistoryLocation string `json:"X-History-Location"` Write []string `json:"-"` StoragePolicy string `json:"X-Storage-Policy"` TempURLKey string `json:"X-Container-Meta-Temp-URL-Key"` TempURLKey2 string `json:"X-Container-Meta-Temp-URL-Key-2"` } func (r *GetHeader) UnmarshalJSON(b []byte) error { type tmp GetHeader var s struct { tmp 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) 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:"Content-Length,string"` 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 Date gophercloud.JSONRFC1123 `json:"Date"` } err := json.Unmarshal(b, &s) if err != nil { return err } *r = CreateHeader(s.tmp) 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:"Content-Length,string"` 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 Date gophercloud.JSONRFC1123 `json:"Date"` } err := json.Unmarshal(b, &s) if err != nil { return err } *r = UpdateHeader(s.tmp) 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:"Content-Length,string"` 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 Date gophercloud.JSONRFC1123 `json:"Date"` } err := json.Unmarshal(b, &s) if err != nil { return err } *r = DeleteHeader(s.tmp) r.Date = time.Time(s.Date) return err } // DeleteResult represents the result of a delete operation. To extract 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 } type BulkDeleteResponse struct { ResponseStatus string `json:"Response Status"` ResponseBody string `json:"Response Body"` Errors [][]string `json:"Errors"` NumberDeleted int `json:"Number Deleted"` NumberNotFound int `json:"Number Not Found"` } // BulkDeleteResult represents the result of a bulk delete operation. To extract // the response object from the HTTP response, call its Extract method. type BulkDeleteResult struct { gophercloud.Result } // Extract will return a BulkDeleteResponse struct returned from a BulkDelete // call. func (r BulkDeleteResult) Extract() (*BulkDeleteResponse, error) { var s BulkDeleteResponse err := r.ExtractInto(&s) return &s, err } golang-github-gophercloud-gophercloud-0.12.0/openstack/objectstorage/v1/containers/testing/000077500000000000000000000000001367513235700321515ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/objectstorage/v1/containers/testing/doc.go000066400000000000000000000000511367513235700332410ustar00rootroot00000000000000// containers unit tests package testing fixtures.go000066400000000000000000000135511367513235700342770ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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 UTC") 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) }) } const bulkDeleteResponse = ` { "Response Status": "foo", "Response Body": "bar", "Errors": [], "Number Deleted": 2, "Number Not Found": 0 } ` // HandleBulkDeleteSuccessfully creates an HTTP handler at `/` on the test // handler mux that responds with a `Delete` response. func HandleBulkDeleteSuccessfully(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, "Accept", "application/json") th.TestHeader(t, r, "Content-Type", "text/plain") th.TestFormValues(t, r, map[string]string{ "bulk-delete": "true", }) th.TestBody(t, r, "testContainer1\ntestContainer2\n") w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, bulkDeleteResponse) }) } // 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 UTC") 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.go000066400000000000000000000110301367513235700353260ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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"} ) 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, time.UTC), TransID: "tx554ed59667a64c61866f1-0058b4ba37", } actual, err := res.Extract() th.AssertNoErr(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.AssertNoErr(t, res.Err) } func TestBulkDelete(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleBulkDeleteSuccessfully(t) expected := containers.BulkDeleteResponse{ ResponseStatus: "foo", ResponseBody: "bar", NumberDeleted: 2, Errors: [][]string{}, } resp, err := containers.BulkDelete(fake.ServiceClient(), []string{"testContainer1", "testContainer2"}).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, expected, *resp) } 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.AssertNoErr(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.AssertNoErr(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, time.UTC), ObjectCount: 4, Read: []string{"test"}, TransID: "tx554ed59667a64c61866f1-0057b4ba37", Write: []string{"test2", "user4"}, StoragePolicy: "test_policy", } actual, err := res.Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, expected, actual) } golang-github-gophercloud-gophercloud-0.12.0/openstack/objectstorage/v1/containers/urls.go000066400000000000000000000012311367513235700320050ustar00rootroot00000000000000package 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) } func bulkDeleteURL(c *gophercloud.ServiceClient) string { return c.Endpoint + "?bulk-delete=true" } golang-github-gophercloud-gophercloud-0.12.0/openstack/objectstorage/v1/objects/000077500000000000000000000000001367513235700277605ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/objectstorage/v1/objects/doc.go000066400000000000000000000047371367513235700310670ustar00rootroot00000000000000/* 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) if object.Err != nil { panic(object.Err) } // if "ExtractContent" method is not called, the HTTP connection will remain consumed content, err := object.ExtractContent() if err != nil { panic(err) } */ package objects golang-github-gophercloud-gophercloud-0.12.0/openstack/objectstorage/v1/objects/errors.go000066400000000000000000000005111367513235700316200ustar00rootroot00000000000000package 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" } golang-github-gophercloud-gophercloud-0.12.0/openstack/objectstorage/v1/objects/requests.go000066400000000000000000000376251367513235700321770ustar00rootroot00000000000000package objects import ( "bytes" "crypto/hmac" "crypto/md5" "crypto/sha1" "fmt" "io" "io/ioutil" "net/url" "strings" "time" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack/objectstorage/v1/accounts" "github.com/gophercloud/gophercloud/openstack/objectstorage/v1/containers" "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, url.QueryEscape(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, url.QueryEscape(containerName), url.QueryEscape(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}, KeepResponseBody: true, }) r.Body, r.Header, r.Err = gophercloud.ParseResponse(resp, 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, url.QueryEscape(containerName), url.QueryEscape(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, b, nil, &gophercloud.RequestOpts{ MoreHeaders: h, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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, url.QueryEscape(containerName), url.QueryEscape(objectName)) resp, err := c.Request("COPY", url, &gophercloud.RequestOpts{ MoreHeaders: h, OkCodes: []int{201}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, 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, url.QueryEscape(containerName), url.QueryEscape(objectName)) if opts != nil { query, err := opts.ToObjectDeleteQuery() if err != nil { r.Err = err return } url += query } resp, err := c.Delete(url, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, 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, url.QueryEscape(containerName), url.QueryEscape(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}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, 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, url.QueryEscape(containerName), url.QueryEscape(objectName)) resp, err := c.Post(url, nil, nil, &gophercloud.RequestOpts{ MoreHeaders: h, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, 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 := containers.Get(c, url.QueryEscape(containerName), nil).Extract() if err != nil { return "", err } tempURLKey := getHeader.TempURLKey if tempURLKey == "" { // fallback to an account TempURL key getHeader, err := accounts.Get(c, nil).Extract() if err != nil { return "", err } tempURLKey = getHeader.TempURLKey } secretKey := []byte(tempURLKey) url := getURL(c, url.QueryEscape(containerName), url.QueryEscape(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 } // BulkDelete is a function that bulk deletes objects. func BulkDelete(c *gophercloud.ServiceClient, container string, objects []string) (r BulkDeleteResult) { // urlencode object names to be on the safe side // https://github.com/openstack/swift/blob/stable/train/swift/common/middleware/bulk.py#L160 // https://github.com/openstack/swift/blob/stable/train/swift/common/swob.py#L302 encodedObjects := make([]string, len(objects)) for i, v := range objects { encodedObjects[i] = strings.Join([]string{ url.QueryEscape(container), url.QueryEscape(v)}, "/") } b := strings.NewReader(strings.Join(encodedObjects, "\n") + "\n") resp, err := c.Post(bulkDeleteURL(c), b, &r.Body, &gophercloud.RequestOpts{ MoreHeaders: map[string]string{ "Accept": "application/json", "Content-Type": "text/plain", }, OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } golang-github-gophercloud-gophercloud-0.12.0/openstack/objectstorage/v1/objects/results.go000066400000000000000000000334431367513235700320170ustar00rootroot00000000000000package objects import ( "encoding/json" "fmt" "io" "io/ioutil" "net/url" "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:"Content-Length,string"` 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 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 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 } 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:"Content-Length,string"` 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 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 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:"Content-Length,string"` 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 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) 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:"Content-Length,string"` 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 Date gophercloud.JSONRFC1123 `json:"Date"` } err := json.Unmarshal(b, &s) if err != nil { return err } *r = UpdateHeader(s.tmp) 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:"Content-Length,string"` 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 Date gophercloud.JSONRFC1123 `json:"Date"` } err := json.Unmarshal(b, &s) if err != nil { return err } *r = DeleteHeader(s.tmp) 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:"Content-Length,string"` 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 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) 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 } type BulkDeleteResponse struct { ResponseStatus string `json:"Response Status"` ResponseBody string `json:"Response Body"` Errors [][]string `json:"Errors"` NumberDeleted int `json:"Number Deleted"` NumberNotFound int `json:"Number Not Found"` } // BulkDeleteResult represents the result of a bulk delete operation. To extract // the response object from the HTTP response, call its Extract method. type BulkDeleteResult struct { gophercloud.Result } // Extract will return a BulkDeleteResponse struct returned from a BulkDelete // call. func (r BulkDeleteResult) Extract() (*BulkDeleteResponse, error) { var s BulkDeleteResponse 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) } } golang-github-gophercloud-gophercloud-0.12.0/openstack/objectstorage/v1/objects/testing/000077500000000000000000000000001367513235700314355ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/objectstorage/v1/objects/testing/doc.go000066400000000000000000000000461367513235700325310ustar00rootroot00000000000000// objects unit tests package testing golang-github-gophercloud-gophercloud-0.12.0/openstack/objectstorage/v1/objects/testing/fixtures.go000066400000000000000000000237511367513235700336450ustar00rootroot00000000000000package 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 UTC") 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), 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") th.TestBody(t, r, `Did gyre and gimble in the wabe`) 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") th.TestBody(t, r, `All mimsy were the borogoves`) 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") th.TestBody(t, r, `The sky was the color of television, tuned to a dead channel.`) 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) }) } const bulkDeleteResponse = ` { "Response Status": "foo", "Response Body": "bar", "Errors": [], "Number Deleted": 2, "Number Not Found": 0 } ` // HandleBulkDeleteSuccessfully creates an HTTP handler at `/` on the test // handler mux that responds with a `BulkDelete` response. func HandleBulkDeleteSuccessfully(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, "Accept", "application/json") th.TestHeader(t, r, "Content-Type", "text/plain") th.TestFormValues(t, r, map[string]string{ "bulk-delete": "true", }) th.TestBody(t, r, "testContainer/testObject1\ntestContainer/testObject2\n") w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, bulkDeleteResponse) }) } // 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.go000066400000000000000000000213021367513235700346150ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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" ) 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, time.UTC), 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 TestBulkDelete(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleBulkDeleteSuccessfully(t) expected := objects.BulkDeleteResponse{ ResponseStatus: "foo", ResponseBody: "bar", NumberDeleted: 2, Errors: [][]string{}, } resp, err := objects.BulkDelete(fake.ServiceClient(), "testContainer", []string{"testObject1", "testObject2"}).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, expected, *resp) } 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) } golang-github-gophercloud-gophercloud-0.12.0/openstack/objectstorage/v1/objects/urls.go000066400000000000000000000017461367513235700313040ustar00rootroot00000000000000package 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) } func bulkDeleteURL(c *gophercloud.ServiceClient) string { return c.Endpoint + "?bulk-delete=true" } golang-github-gophercloud-gophercloud-0.12.0/openstack/objectstorage/v1/swauth/000077500000000000000000000000001367513235700276425ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/objectstorage/v1/swauth/doc.go000066400000000000000000000004561367513235700307430ustar00rootroot00000000000000/* 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 golang-github-gophercloud-gophercloud-0.12.0/openstack/objectstorage/v1/swauth/requests.go000066400000000000000000000033141367513235700320450ustar00rootroot00000000000000package 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}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, 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 } golang-github-gophercloud-gophercloud-0.12.0/openstack/objectstorage/v1/swauth/results.go000066400000000000000000000013331367513235700316720ustar00rootroot00000000000000package 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 } golang-github-gophercloud-gophercloud-0.12.0/openstack/objectstorage/v1/swauth/testing/000077500000000000000000000000001367513235700313175ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/objectstorage/v1/swauth/testing/doc.go000066400000000000000000000000451367513235700324120ustar00rootroot00000000000000// swauth unit tests package testing golang-github-gophercloud-gophercloud-0.12.0/openstack/objectstorage/v1/swauth/testing/fixtures.go000066400000000000000000000015631367513235700335240ustar00rootroot00000000000000package 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.go000066400000000000000000000014671367513235700345110ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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") } } golang-github-gophercloud-gophercloud-0.12.0/openstack/objectstorage/v1/swauth/urls.go000066400000000000000000000002301367513235700311510ustar00rootroot00000000000000package swauth import "github.com/gophercloud/gophercloud" func getURL(c *gophercloud.ProviderClient) string { return c.IdentityBase + "auth/v1.0" } golang-github-gophercloud-gophercloud-0.12.0/openstack/orchestration/000077500000000000000000000000001367513235700260325ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/orchestration/v1/000077500000000000000000000000001367513235700263605ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/orchestration/v1/apiversions/000077500000000000000000000000001367513235700307225ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/orchestration/v1/apiversions/doc.go000066400000000000000000000003271367513235700320200ustar00rootroot00000000000000// 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 golang-github-gophercloud-gophercloud-0.12.0/openstack/orchestration/v1/apiversions/requests.go000066400000000000000000000006341367513235700331270ustar00rootroot00000000000000package 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, listURL(c), func(r pagination.PageResult) pagination.Page { return APIVersionPage{pagination.SinglePageBase(r)} }) } golang-github-gophercloud-gophercloud-0.12.0/openstack/orchestration/v1/apiversions/results.go000066400000000000000000000020661367513235700327560ustar00rootroot00000000000000package 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 } golang-github-gophercloud-gophercloud-0.12.0/openstack/orchestration/v1/apiversions/testing/000077500000000000000000000000001367513235700323775ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/orchestration/v1/apiversions/testing/doc.go000066400000000000000000000000601367513235700334670ustar00rootroot00000000000000// orchestration_apiversions_v1 package testing requests_test.go000066400000000000000000000040011367513235700355540ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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 }) } golang-github-gophercloud-gophercloud-0.12.0/openstack/orchestration/v1/apiversions/urls.go000066400000000000000000000004731367513235700322420ustar00rootroot00000000000000package apiversions import ( "strings" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack/utils" ) func listURL(c *gophercloud.ServiceClient) string { baseEndpoint, _ := utils.BaseEndpoint(c.Endpoint) endpoint := strings.TrimRight(baseEndpoint, "/") + "/" return endpoint } golang-github-gophercloud-gophercloud-0.12.0/openstack/orchestration/v1/buildinfo/000077500000000000000000000000001367513235700303335ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/orchestration/v1/buildinfo/doc.go000066400000000000000000000001321367513235700314230ustar00rootroot00000000000000// Package buildinfo provides build information about heat deployments. package buildinfo golang-github-gophercloud-gophercloud-0.12.0/openstack/orchestration/v1/buildinfo/requests.go000066400000000000000000000004351367513235700325370ustar00rootroot00000000000000package buildinfo import "github.com/gophercloud/gophercloud" // Get retreives data for the given stack template. func Get(c *gophercloud.ServiceClient) (r GetResult) { resp, err := c.Get(getURL(c), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } golang-github-gophercloud-gophercloud-0.12.0/openstack/orchestration/v1/buildinfo/results.go000066400000000000000000000012271367513235700323650ustar00rootroot00000000000000package 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 } golang-github-gophercloud-gophercloud-0.12.0/openstack/orchestration/v1/buildinfo/testing/000077500000000000000000000000001367513235700320105ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/orchestration/v1/buildinfo/testing/doc.go000066400000000000000000000000561367513235700331050ustar00rootroot00000000000000// orchestration_buildinfo_v1 package testing fixtures.go000066400000000000000000000021761367513235700341370ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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.go000066400000000000000000000007651367513235700352020ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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) } golang-github-gophercloud-gophercloud-0.12.0/openstack/orchestration/v1/buildinfo/urls.go000066400000000000000000000002301367513235700316420ustar00rootroot00000000000000package buildinfo import "github.com/gophercloud/gophercloud" func getURL(c *gophercloud.ServiceClient) string { return c.ServiceURL("build_info") } golang-github-gophercloud-gophercloud-0.12.0/openstack/orchestration/v1/resourcetypes/000077500000000000000000000000001367513235700312745ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/orchestration/v1/resourcetypes/doc.go000066400000000000000000000011551367513235700323720ustar00rootroot00000000000000/* Package resourcetypes provides operations for listing available resource types, obtaining their properties schema, and generating example templates that can be customised to use as provider templates. Example of listing available resource types: listOpts := resourcetypes.ListOpts{ SupportStatus: resourcetypes.SupportStatusSupported, } resourceTypes, err := resourcetypes.List(client, listOpts).Extract() if err != nil { panic(err) } fmt.Println("Get Resource Type List") for _, rt := range resTypes { fmt.Println(rt.ResourceType) } */ package resourcetypes golang-github-gophercloud-gophercloud-0.12.0/openstack/orchestration/v1/resourcetypes/requests.go000066400000000000000000000077011367513235700335030ustar00rootroot00000000000000package resourcetypes import ( "github.com/gophercloud/gophercloud" ) // SupportStatus is a type for specifying by which support status to filter the // list of resource types. type SupportStatus string const ( // SupportStatusUnknown is returned when the resource type does not have a // support status. SupportStatusUnknown SupportStatus = "UNKNOWN" // SupportStatusSupported indicates a resource type that is expected to // work. SupportStatusSupported SupportStatus = "SUPPORTED" // SupportStatusDeprecated indicates a resource type that is in the process // being removed, and may or may not be replaced by something else. SupportStatusDeprecated SupportStatus = "DEPRECATED" // SupportStatusHidden indicates a resource type that has been removed. // Existing stacks that contain resources of this type can still be // deleted or updated to remove the resources, but they may not actually // do anything any more. SupportStatusHidden SupportStatus = "HIDDEN" // SupportStatusUnsupported indicates a resource type that is provided for // preview or other purposes and should not be relied upon. SupportStatusUnsupported SupportStatus = "UNSUPPORTED" ) // ListOptsBuilder allows extensions to add additional parameters to the // List request. type ListOptsBuilder interface { ToResourceTypeListQuery() (string, error) } // ListOpts allows the filtering of collections through the API. type ListOpts struct { // Filters the resource type list by a regex on the name. NameRegex string `q:"name"` // Filters the resource list by the specified SupportStatus. SupportStatus SupportStatus `q:"support_status"` // Return descriptions as well as names of resource types WithDescription bool `q:"with_description"` } // ToResourceTypeListQuery formats a ListOpts into a query string. func (opts ListOpts) ToResourceTypeListQuery() (string, error) { q, err := gophercloud.BuildQueryString(opts) return q.String(), err } // List makes a request against the API to list available resource types. func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) (r ListResult) { url := listURL(client) if opts == nil { opts = ListOpts{} } query, err := opts.ToResourceTypeListQuery() if err != nil { r.Err = err return } url += query resp, err := client.Get(url, &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // GetSchema retreives the schema for a given resource type. func GetSchema(client *gophercloud.ServiceClient, resourceType string) (r GetSchemaResult) { resp, err := client.Get(getSchemaURL(client, resourceType), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // GenerateTemplateOptsBuilder allows extensions to add additional parameters // to the GenerateTemplate request. type GenerateTemplateOptsBuilder interface { ToGenerateTemplateQuery() (string, error) } type GeneratedTemplateType string const ( TemplateTypeHOT GeneratedTemplateType = "hot" TemplateTypeCFn GeneratedTemplateType = "cfn" ) // GenerateTemplateOpts allows the filtering of collections through the API. type GenerateTemplateOpts struct { TemplateType GeneratedTemplateType `q:"template_type"` } // ToGenerateTemplateQuery formats a GenerateTemplateOpts into a query string. func (opts GenerateTemplateOpts) ToGenerateTemplateQuery() (string, error) { if opts.TemplateType == "" { opts.TemplateType = TemplateTypeHOT } q, err := gophercloud.BuildQueryString(opts) return q.String(), err } // GenerateTemplate retreives an example template for a given resource type. func GenerateTemplate(client *gophercloud.ServiceClient, resourceType string, opts GenerateTemplateOptsBuilder) (r TemplateResult) { url := generateTemplateURL(client, resourceType) if opts == nil { opts = GenerateTemplateOpts{} } query, err := opts.ToGenerateTemplateQuery() if err != nil { r.Err = err return } url += query resp, err := client.Get(url, &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } golang-github-gophercloud-gophercloud-0.12.0/openstack/orchestration/v1/resourcetypes/results.go000066400000000000000000000120071367513235700333240ustar00rootroot00000000000000package resourcetypes import ( "github.com/gophercloud/gophercloud" ) // ResourceTypeSummary contains the result of listing an available resource // type. type ResourceTypeSummary struct { ResourceType string `json:"resource_type"` Description string `json:"description"` } // PropertyType represents the expected type of a property or attribute value. type PropertyType string const ( // StringProperty indicates a string property type. StringProperty PropertyType = "string" // IntegerProperty indicates an integer property type. IntegerProperty PropertyType = "integer" // NumberProperty indicates a number property type. It may be an integer or // float. NumberProperty PropertyType = "number" // BooleanProperty indicates a boolean property type. BooleanProperty PropertyType = "boolean" // MapProperty indicates a map property type. MapProperty PropertyType = "map" // ListProperty indicates a list property type. ListProperty PropertyType = "list" // UntypedProperty indicates a property that could have any type. UntypedProperty PropertyType = "any" ) // AttributeSchema is the schema of a resource attribute type AttributeSchema struct { Description string `json:"description,omitempty"` Type PropertyType `json:"type"` } // MinMaxConstraint is a type of constraint with minimum and maximum values. // This is used for both Range and Length constraints. type MinMaxConstraint struct { Min float64 `json:"min,omitempty"` Max float64 `json:"max,omitempty"` } // ModuloConstraint constrains an integer to have a certain value given a // particular modulus. type ModuloConstraint struct { Step int `json:"step,omitempty"` Offset int `json:"offset,omitempty"` } // ConstraintSchema describes all possible types of constraints. Besides the // description, only one other field is ever set at a time. type ConstraintSchema struct { Description string `json:"description,omitempty"` Range *MinMaxConstraint `json:"range,omitempty"` Length *MinMaxConstraint `json:"length,omitempty"` Modulo *ModuloConstraint `json:"modulo,omitempty"` AllowedValues *[]interface{} `json:"allowed_values,omitempty"` AllowedPattern *string `json:"allowed_pattern,omitempty"` CustomConstraint *string `json:"custom_constraint,omitempty"` } // PropertySchema is the schema of a resource property. type PropertySchema struct { Type PropertyType `json:"type"` Description string `json:"description,omitempty"` Default interface{} `json:"default,omitempty"` Constraints []ConstraintSchema `json:"constraints,omitempty"` Required bool `json:"required"` Immutable bool `json:"immutable"` UpdateAllowed bool `json:"update_allowed"` Schema map[string]PropertySchema `json:"schema,omitempty"` } // SupportStatusDetails contains information about the support status of the // resource and its history. type SupportStatusDetails struct { Status SupportStatus `json:"status"` Message string `json:"message,omitempty"` Version string `json:"version,omitempty"` PreviousStatus *SupportStatusDetails `json:"previous_status,omitempty"` } // ResourceSchema is the schema for a resource type, its attributes, and // properties. type ResourceSchema struct { ResourceType string `json:"resource_type"` SupportStatus SupportStatusDetails `json:"support_status"` Attributes map[string]AttributeSchema `json:"attributes"` Properties map[string]PropertySchema `json:"properties"` } // ListResult represents the result of a List operation. type ListResult struct { gophercloud.Result } // Extract returns a slice of ResourceTypeSummary objects and is called after // a List operation. func (r ListResult) Extract() (rts []ResourceTypeSummary, err error) { var full struct { ResourceTypes []ResourceTypeSummary `json:"resource_types"` } err = r.ExtractInto(&full) if err == nil { rts = full.ResourceTypes return } var basic struct { ResourceTypes []string `json:"resource_types"` } err2 := r.ExtractInto(&basic) if err2 == nil { err = nil rts = make([]ResourceTypeSummary, len(basic.ResourceTypes)) for i, n := range basic.ResourceTypes { rts[i] = ResourceTypeSummary{ResourceType: n} } } return } // GetSchemaResult represents the result of a GetSchema operation. type GetSchemaResult struct { gophercloud.Result } // Extract returns a ResourceSchema object and is called after a GetSchema // operation. func (r GetSchemaResult) Extract() (rts ResourceSchema, err error) { err = r.ExtractInto(&rts) return } // TemplateResult represents the result of a Template get operation. type TemplateResult struct { gophercloud.Result } // Extract returns a Template object and is called after a Template get // operation. func (r TemplateResult) Extract() (template map[string]interface{}, err error) { err = r.ExtractInto(&template) return } golang-github-gophercloud-gophercloud-0.12.0/openstack/orchestration/v1/resourcetypes/testing/000077500000000000000000000000001367513235700327515ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/orchestration/v1/resourcetypes/testing/doc.go000066400000000000000000000000631367513235700340440ustar00rootroot00000000000000// orchestration_resourcetypes_v1 package testing fixtures.go000066400000000000000000000242331367513235700350760ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/orchestration/v1/resourcetypes/testingpackage testing import ( "fmt" "net/http" "testing" "github.com/gophercloud/gophercloud/openstack/orchestration/v1/resourcetypes" th "github.com/gophercloud/gophercloud/testhelper" fake "github.com/gophercloud/gophercloud/testhelper/client" ) const BasicListOutput = ` { "resource_types": [ "OS::Nova::Server", "OS::Heat::Stack" ] } ` var BasicListExpected = []resourcetypes.ResourceTypeSummary{ resourcetypes.ResourceTypeSummary{ ResourceType: "OS::Nova::Server", }, resourcetypes.ResourceTypeSummary{ ResourceType: "OS::Heat::Stack", }, } const FullListOutput = ` { "resource_types": [ { "description": "A Nova Server", "resource_type": "OS::Nova::Server" }, { "description": "A Heat Stack", "resource_type": "OS::Heat::Stack" } ] } ` var FullListExpected = []resourcetypes.ResourceTypeSummary{ resourcetypes.ResourceTypeSummary{ ResourceType: "OS::Nova::Server", Description: "A Nova Server", }, resourcetypes.ResourceTypeSummary{ ResourceType: "OS::Heat::Stack", Description: "A Heat Stack", }, } const listFilterRegex = "OS::Heat::.*" const FilteredListOutput = ` { "resource_types": [ { "description": "A Heat Stack", "resource_type": "OS::Heat::Stack" } ] } ` var FilteredListExpected = []resourcetypes.ResourceTypeSummary{ resourcetypes.ResourceTypeSummary{ ResourceType: "OS::Heat::Stack", Description: "A Heat Stack", }, } // HandleListSuccessfully creates an HTTP handler at `/resource_types` // on the test handler mux that responds with a `List` response. func HandleListSuccessfully(t *testing.T) { 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) r.ParseForm() var output string if r.Form.Get("with_description") == "true" { if r.Form.Get("name") == listFilterRegex { output = FilteredListOutput } else { output = FullListOutput } } else { output = BasicListOutput } fmt.Fprint(w, output) }) } var glanceImageConstraint = "glance.image" var GetSchemaExpected = resourcetypes.ResourceSchema{ ResourceType: "OS::Test::TestServer", SupportStatus: resourcetypes.SupportStatusDetails{ Status: resourcetypes.SupportStatusDeprecated, Message: "Bye bye.", Version: "10.0.0", PreviousStatus: &resourcetypes.SupportStatusDetails{ Status: resourcetypes.SupportStatusSupported, }, }, Attributes: map[string]resourcetypes.AttributeSchema{ "show": resourcetypes.AttributeSchema{ Description: "Detailed information about resource.", Type: resourcetypes.MapProperty, }, "tags": resourcetypes.AttributeSchema{ Description: "Tags from the server.", Type: resourcetypes.ListProperty, }, "name": resourcetypes.AttributeSchema{ Description: "Name of the server.", Type: resourcetypes.StringProperty, }, }, Properties: map[string]resourcetypes.PropertySchema{ "name": resourcetypes.PropertySchema{ Type: resourcetypes.StringProperty, Description: "Server name.", UpdateAllowed: true, }, "image": resourcetypes.PropertySchema{ Type: resourcetypes.StringProperty, Description: "The ID or name of the image to boot with.", Required: true, Constraints: []resourcetypes.ConstraintSchema{ resourcetypes.ConstraintSchema{ CustomConstraint: &glanceImageConstraint, }, }, }, "block_device_mapping": resourcetypes.PropertySchema{ Type: resourcetypes.ListProperty, Description: "Block device mappings for this server.", Schema: map[string]resourcetypes.PropertySchema{ "*": resourcetypes.PropertySchema{ Type: resourcetypes.MapProperty, Schema: map[string]resourcetypes.PropertySchema{ "ephemeral_format": resourcetypes.PropertySchema{ Type: resourcetypes.StringProperty, Description: "The format of the local ephemeral block device.", Constraints: []resourcetypes.ConstraintSchema{ resourcetypes.ConstraintSchema{ AllowedValues: &[]interface{}{ "ext3", "ext4", "xfs", }, }, }, }, "ephemeral_size": resourcetypes.PropertySchema{ Type: resourcetypes.IntegerProperty, Description: "The size of the local ephemeral block device, in GB.", Constraints: []resourcetypes.ConstraintSchema{ resourcetypes.ConstraintSchema{ Range: &resourcetypes.MinMaxConstraint{ Min: 1, }, }, }, }, "delete_on_termination": resourcetypes.PropertySchema{ Type: resourcetypes.BooleanProperty, Description: "Delete volume on server termination.", Default: true, Immutable: true, }, }, }, }, }, "image_update_policy": resourcetypes.PropertySchema{ Type: resourcetypes.StringProperty, Description: "Policy on how to apply an image-id update.", Default: "REBUILD", Constraints: []resourcetypes.ConstraintSchema{ resourcetypes.ConstraintSchema{ AllowedValues: &[]interface{}{ "REBUILD", "REPLACE", }, }, }, UpdateAllowed: true, }, }, } const GetSchemaOutput = ` { "resource_type": "OS::Test::TestServer", "support_status": { "status": "DEPRECATED", "message": "Bye bye.", "version": "10.0.0", "previous_status": { "status": "SUPPORTED", "message": null, "version": null, "previous_status": null } }, "attributes": { "show": { "type": "map", "description": "Detailed information about resource." }, "tags": { "type": "list", "description": "Tags from the server." }, "name": { "type": "string", "description": "Name of the server." } }, "properties": { "name": { "update_allowed": true, "required": false, "type": "string", "description": "Server name.", "immutable": false }, "image": { "description": "The ID or name of the image to boot with.", "required": true, "update_allowed": false, "type": "string", "immutable": false, "constraints": [ { "custom_constraint": "glance.image" } ] }, "block_device_mapping": { "description": "Block device mappings for this server.", "required": false, "update_allowed": false, "type": "list", "immutable": false, "schema": { "*": { "update_allowed": false, "required": false, "type": "map", "immutable": false, "schema": { "ephemeral_format": { "description": "The format of the local ephemeral block device.", "required": false, "update_allowed": false, "type": "string", "immutable": false, "constraints": [ { "allowed_values": [ "ext3", "ext4", "xfs" ] } ] }, "ephemeral_size": { "description": "The size of the local ephemeral block device, in GB.", "required": false, "update_allowed": false, "type": "integer", "immutable": false, "constraints": [ { "range": { "min": 1 } } ] }, "delete_on_termination": { "update_allowed": false, "default": true, "required": false, "type": "boolean", "description": "Delete volume on server termination.", "immutable": true } } } } }, "image_update_policy": { "description": "Policy on how to apply an image-id update.", "default": "REBUILD", "required": false, "update_allowed": true, "type": "string", "immutable": false, "constraints": [ { "allowed_values": [ "REBUILD", "REPLACE" ] } ] } } } ` // HandleGetSchemaSuccessfully creates an HTTP handler at // `/resource_types/OS::Test::TestServer` on the test handler mux that // responds with a `GetSchema` response. func HandleGetSchemaSuccessfully(t *testing.T) { th.Mux.HandleFunc("/resource_types/OS::Test::TestServer", 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.Fprint(w, GetSchemaOutput) }) } const GenerateTemplateOutput = ` { "outputs": { "OS::stack_id": { "value": { "get_resource": "NoneResource" } }, "show": { "description": "Detailed information about resource.", "value": { "get_attr": [ "NoneResource", "show" ] } } }, "heat_template_version": "2016-10-14", "description": "Initial template of NoneResource", "parameters": {}, "resources": { "NoneResource": { "type": "OS::Heat::None", "properties": {} } } } ` // HandleGenerateTemplateSuccessfully creates an HTTP handler at // `/resource_types/OS::Heat::None/template` on the test handler mux that // responds with a template. func HandleGenerateTemplateSuccessfully(t *testing.T) { th.Mux.HandleFunc("/resource_types/OS::Heat::None/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") r.ParseForm() if r.Form.Get("template_type") == "hot" { w.WriteHeader(http.StatusOK) fmt.Fprint(w, GenerateTemplateOutput) } else { w.WriteHeader(http.StatusBadRequest) } }) } requests_test.go000066400000000000000000000037521367513235700361420ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/orchestration/v1/resourcetypes/testingpackage testing import ( "testing" "github.com/gophercloud/gophercloud/openstack/orchestration/v1/resourcetypes" th "github.com/gophercloud/gophercloud/testhelper" fake "github.com/gophercloud/gophercloud/testhelper/client" ) func TestBasicListResourceTypes(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleListSuccessfully(t) result := resourcetypes.List(fake.ServiceClient(), nil) th.AssertNoErr(t, result.Err) actual, err := result.Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, BasicListExpected, actual) } func TestFullListResourceTypes(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleListSuccessfully(t) result := resourcetypes.List(fake.ServiceClient(), resourcetypes.ListOpts{ WithDescription: true, }) th.AssertNoErr(t, result.Err) actual, err := result.Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, FullListExpected, actual) } func TestFilteredListResourceTypes(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleListSuccessfully(t) result := resourcetypes.List(fake.ServiceClient(), resourcetypes.ListOpts{ NameRegex: listFilterRegex, WithDescription: true, }) th.AssertNoErr(t, result.Err) actual, err := result.Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, FilteredListExpected, actual) } func TestGetSchema(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleGetSchemaSuccessfully(t) result := resourcetypes.GetSchema(fake.ServiceClient(), "OS::Test::TestServer") th.AssertNoErr(t, result.Err) actual, err := result.Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, GetSchemaExpected, actual) } func TestGenerateTemplate(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleGenerateTemplateSuccessfully(t) result := resourcetypes.GenerateTemplate(fake.ServiceClient(), "OS::Heat::None", nil) th.AssertNoErr(t, result.Err) actual, err := result.Extract() th.AssertNoErr(t, err) th.AssertEquals(t, "2016-10-14", actual["heat_template_version"]) } golang-github-gophercloud-gophercloud-0.12.0/openstack/orchestration/v1/resourcetypes/urls.go000066400000000000000000000007401367513235700326110ustar00rootroot00000000000000package resourcetypes import "github.com/gophercloud/gophercloud" const ( resTypesPath = "resource_types" ) func listURL(c *gophercloud.ServiceClient) string { return c.ServiceURL(resTypesPath) } func getSchemaURL(c *gophercloud.ServiceClient, resourceType string) string { return c.ServiceURL(resTypesPath, resourceType) } func generateTemplateURL(c *gophercloud.ServiceClient, resourceType string) string { return c.ServiceURL(resTypesPath, resourceType, "template") } golang-github-gophercloud-gophercloud-0.12.0/openstack/orchestration/v1/stackevents/000077500000000000000000000000001367513235700307125ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/orchestration/v1/stackevents/doc.go000066400000000000000000000010071367513235700320040ustar00rootroot00000000000000/* 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 golang-github-gophercloud-gophercloud-0.12.0/openstack/orchestration/v1/stackevents/requests.go000066400000000000000000000174271367513235700331270ustar00rootroot00000000000000package 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) { resp, err := c.Get(findURL(c, stackName), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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) { resp, err := c.Get(getURL(c, stackName, stackID, resourceName, eventID), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } golang-github-gophercloud-gophercloud-0.12.0/openstack/orchestration/v1/stackevents/results.go000066400000000000000000000064171367513235700327520ustar00rootroot00000000000000package 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 } golang-github-gophercloud-gophercloud-0.12.0/openstack/orchestration/v1/stackevents/testing/000077500000000000000000000000001367513235700323675ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/orchestration/v1/stackevents/testing/doc.go000066400000000000000000000000601367513235700334570ustar00rootroot00000000000000// orchestration_stackevents_v1 package testing fixtures.go000066400000000000000000000422611367513235700345150ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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.go000066400000000000000000000037631367513235700355620ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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) } golang-github-gophercloud-gophercloud-0.12.0/openstack/orchestration/v1/stackevents/urls.go000066400000000000000000000013341367513235700322270ustar00rootroot00000000000000package 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) } golang-github-gophercloud-gophercloud-0.12.0/openstack/orchestration/v1/stackresources/000077500000000000000000000000001367513235700314205ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/orchestration/v1/stackresources/doc.go000066400000000000000000000041301367513235700325120ustar00rootroot00000000000000/* 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 golang-github-gophercloud-gophercloud-0.12.0/openstack/orchestration/v1/stackresources/requests.go000066400000000000000000000105511367513235700336240ustar00rootroot00000000000000package 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) { resp, err := c.Get(findURL(c, stackName), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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) { resp, err := c.Get(getURL(c, stackName, stackID, resourceName), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Metadata retreives the metadata for the given stack resource. func Metadata(c *gophercloud.ServiceClient, stackName, stackID, resourceName string) (r MetadataResult) { resp, err := c.Get(metadataURL(c, stackName, stackID, resourceName), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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) { resp, err := c.Get(schemaURL(c, resourceType), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Template retreives the template representation for the given resource type. func Template(c *gophercloud.ServiceClient, resourceType string) (r TemplateResult) { resp, err := c.Get(templateURL(c, resourceType), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // MarkUnhealthyOpts contains the common options struct used in this package's // MarkUnhealthy operations. type MarkUnhealthyOpts struct { // A boolean indicating whether the target resource should be marked as unhealthy. MarkUnhealthy bool `json:"mark_unhealthy"` // The reason for the current stack resource state. ResourceStatusReason string `json:"resource_status_reason,omitempty"` } // MarkUnhealthyOptsBuilder is the interface options structs have to satisfy in order // to be used in the MarkUnhealthy operation in this package type MarkUnhealthyOptsBuilder interface { ToMarkUnhealthyMap() (map[string]interface{}, error) } // ToMarkUnhealthyMap validates that a template was supplied and calls // the ToMarkUnhealthyMap private function. func (opts MarkUnhealthyOpts) ToMarkUnhealthyMap() (map[string]interface{}, error) { b, err := gophercloud.BuildRequestBody(opts, "") if err != nil { return nil, err } return b, nil } // MarkUnhealthy marks the specified resource in the stack as unhealthy. func MarkUnhealthy(c *gophercloud.ServiceClient, stackName, stackID, resourceName string, opts MarkUnhealthyOptsBuilder) (r MarkUnhealthyResult) { b, err := opts.ToMarkUnhealthyMap() if err != nil { r.Err = err return } resp, err := c.Patch(markUnhealthyURL(c, stackName, stackID, resourceName), b, nil, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } golang-github-gophercloud-gophercloud-0.12.0/openstack/orchestration/v1/stackresources/results.go000066400000000000000000000135411367513235700334540ustar00rootroot00000000000000package 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"` ParentResource string `json:"parent_resource"` 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 } // MarkUnhealthyResult represents the result of a mark unhealthy operation. type MarkUnhealthyResult struct { gophercloud.ErrResult } golang-github-gophercloud-gophercloud-0.12.0/openstack/orchestration/v1/stackresources/testing/000077500000000000000000000000001367513235700330755ustar00rootroot00000000000000doc.go000066400000000000000000000000631367513235700341110ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/orchestration/v1/stackresources/testing// orchestration_stackresources_v1 package testing fixtures.go000066400000000000000000000402511367513235700352200ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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) }) } // HandleMarkUnhealthySuccessfully creates an HTTP handler at `/stacks/teststack/0b1771bd-9336-4f2b-ae86-a80f971faf1e/resources/wordpress_instance` // on the test handler mux that responds with a `MarkUnhealthy` response. func HandleMarkUnhealthySuccessfully(t *testing.T) { th.Mux.HandleFunc("/stacks/teststack/0b1771bd-9336-4f2b-ae86-a80f971faf1e/resources/wordpress_instance", 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.StatusOK) }) } requests_test.go000066400000000000000000000067671367513235700362770ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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)) } func TestMarkUnhealthyResource(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleMarkUnhealthySuccessfully(t) markUnhealthyOpts := &stackresources.MarkUnhealthyOpts{ MarkUnhealthy: true, ResourceStatusReason: "Kubelet.Ready is Unknown more than 10 mins.", } err := stackresources.MarkUnhealthy(fake.ServiceClient(), "teststack", "0b1771bd-9336-4f2b-ae86-a80f971faf1e", "wordpress_instance", markUnhealthyOpts).ExtractErr() th.AssertNoErr(t, err) } golang-github-gophercloud-gophercloud-0.12.0/openstack/orchestration/v1/stackresources/urls.go000066400000000000000000000023371367513235700327410ustar00rootroot00000000000000package 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") } func markUnhealthyURL(c *gophercloud.ServiceClient, stackName, stackID, resourceName string) string { return c.ServiceURL("stacks", stackName, stackID, "resources", resourceName) } golang-github-gophercloud-gophercloud-0.12.0/openstack/orchestration/v1/stacks/000077500000000000000000000000001367513235700276505ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/orchestration/v1/stacks/doc.go000066400000000000000000000155531367513235700307550ustar00rootroot00000000000000/* 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 Find Stack find_result := stacks.Find(client, stackIdentity) if find_result.Err != nil { panic(find_result.Err) } stack, err := find_result.Extract() if err != nil { panic(err) } fmt.Println("Find 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 golang-github-gophercloud-gophercloud-0.12.0/openstack/orchestration/v1/stacks/environment.go000066400000000000000000000075521367513235700325540ustar00rootroot00000000000000package 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 } golang-github-gophercloud-gophercloud-0.12.0/openstack/orchestration/v1/stacks/environment_test.go000066400000000000000000000146771367513235700336210ustar00rootroot00000000000000package 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) } golang-github-gophercloud-gophercloud-0.12.0/openstack/orchestration/v1/stacks/errors.go000066400000000000000000000014621367513235700315160ustar00rootroot00000000000000package 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.") } golang-github-gophercloud-gophercloud-0.12.0/openstack/orchestration/v1/stacks/fixtures.go000066400000000000000000000131141367513235700320500ustar00rootroot00000000000000package 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", }, }, } golang-github-gophercloud-gophercloud-0.12.0/openstack/orchestration/v1/stacks/requests.go000066400000000000000000000423151367513235700320570ustar00rootroot00000000000000package 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 } resp, err := c.Post(createURL(c), b, &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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 } resp, err := c.Post(adoptURL(c), b, &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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. type ListOpts struct { // TenantID is the UUID of the tenant. A tenant is also known as // a project. TenantID string `q:"tenant_id"` // ID filters the stack list by a stack ID ID string `q:"id"` // Status filters the stack list by a status. Status string `q:"status"` // Name filters the stack list by a name. Name string `q:"name"` // Marker is the ID of last-seen item. Marker string `q:"marker"` // Limit is an integer value for the limit of values to return. Limit int `q:"limit"` // SortKey allows you to sort by stack_name, stack_status, creation_time, or // update_time key. SortKey SortKey `q:"sort_keys"` // SortDir sets the direction, and is either `asc` or `desc`. SortDir SortDir `q:"sort_dir"` // AllTenants is a bool to show all tenants. AllTenants bool `q:"global_tenant"` // ShowDeleted set to `true` to include deleted stacks in the list. ShowDeleted bool `q:"show_deleted"` // ShowNested set to `true` to include nested stacks in the list. ShowNested bool `q:"show_nested"` // Tags lists stacks that contain one or more simple string tags. Tags string `q:"tags"` // TagsAny lists stacks that contain one or more simple string tags. TagsAny string `q:"tags_any"` // NotTags lists stacks that do not contain one or more simple string tags. NotTags string `q:"not_tags"` // NotTagsAny lists stacks that do not contain one or more simple string tags. NotTagsAny string `q:"not_tags_any"` } // 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) { resp, err := c.Get(getURL(c, stackName, stackID), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Find retrieves a stack based on the stack name or stack ID. func Find(c *gophercloud.ServiceClient, stackIdentity string) (r GetResult) { resp, err := c.Get(findURL(c, stackIdentity), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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 } resp, err := c.Put(updateURL(c, stackName, stackID), b, nil, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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 } resp, err := c.Patch(updateURL(c, stackName, stackID), b, nil, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete deletes a stack based on the stack name and stack ID. func Delete(c *gophercloud.ServiceClient, stackName, stackID string) (r DeleteResult) { resp, err := c.Delete(deleteURL(c, stackName, stackID), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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 } resp, err := c.Post(previewURL(c), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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) { resp, err := c.Delete(abandonURL(c, stackName, stackID), &gophercloud.RequestOpts{ JSONResponse: &r.Body, OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } golang-github-gophercloud-gophercloud-0.12.0/openstack/orchestration/v1/stacks/results.go000066400000000000000000000207231367513235700317040ustar00rootroot00000000000000package 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 } golang-github-gophercloud-gophercloud-0.12.0/openstack/orchestration/v1/stacks/template.go000066400000000000000000000104601367513235700320130ustar00rootroot00000000000000package 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.Fetch(); 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.Parse(); err == nil { if err := childTemplate.Validate(); err == nil { 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 } golang-github-gophercloud-gophercloud-0.12.0/openstack/orchestration/v1/stacks/template_test.go000066400000000000000000000125231367513235700330540ustar00rootroot00000000000000package 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 TestGetFileContentsWithType(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) } func TestGetFileContentsWithFile(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() baseurl, err := getBasePath() th.AssertNoErr(t, err) fakeURL := strings.Join([]string{baseurl, "somefile"}, "/") urlparsed, err := url.Parse(fakeURL) th.AssertNoErr(t, err) somefile := `Welcome!` th.Mux.HandleFunc(urlparsed.Path, func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") w.Header().Set("Content-Type", "text/plain") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, somefile) }) client := fakeClient{BaseClient: getHTTPClient()} te := new(Template) te.Bin = []byte(`heat_template_version: 2015-04-30 resources: test_resource: type: OS::Heat::TestResource properties: value: {get_file: somefile }`) 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{ "somefile": "Welcome!", } th.AssertEquals(t, expectedFiles["somefile"], te.Files[fakeURL]) te.fixFileRefs() expectedParsed := map[string]interface{}{ "heat_template_version": "2015-04-30", "resources": map[string]interface{}{ "test_resource": map[string]interface{}{ "type": "OS::Heat::TestResource", "properties": map[string]interface{}{ "value": map[string]interface{}{ "get_file": fakeURL, }, }, }, }, } te.Parse() th.AssertDeepEquals(t, expectedParsed, te.Parsed) } golang-github-gophercloud-gophercloud-0.12.0/openstack/orchestration/v1/stacks/testing/000077500000000000000000000000001367513235700313255ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/orchestration/v1/stacks/testing/doc.go000066400000000000000000000000531367513235700324170ustar00rootroot00000000000000// orchestration_stacks_v1 package testing golang-github-gophercloud-gophercloud-0.12.0/openstack/orchestration/v1/stacks/testing/fixtures.go000066400000000000000000000364521367513235700335370ustar00rootroot00000000000000package 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) }) } func HandleFindSuccessfully(t *testing.T, output string) { th.Mux.HandleFunc("/stacks/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.go000066400000000000000000000150211367513235700345060ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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 TestFindStack(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleFindSuccessfully(t, GetOutput) actual, err := stacks.Find(fake.ServiceClient(), "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) } golang-github-gophercloud-gophercloud-0.12.0/openstack/orchestration/v1/stacks/urls.go000066400000000000000000000016751367513235700311750ustar00rootroot00000000000000package 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 findURL(c *gophercloud.ServiceClient, identity string) string { return c.ServiceURL("stacks", identity) } 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") } golang-github-gophercloud-gophercloud-0.12.0/openstack/orchestration/v1/stacks/utils.go000066400000000000000000000104311367513235700313360ustar00rootroot00000000000000package stacks import ( "encoding/json" "fmt" "io/ioutil" "net/http" "path/filepath" "reflect" "strings" "github.com/gophercloud/gophercloud" yaml "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 } if !(resp.StatusCode >= 200 && resp.StatusCode < 300) { return fmt.Errorf("error fetching %s: %s", t.URL, resp.Status) } 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 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) } golang-github-gophercloud-gophercloud-0.12.0/openstack/orchestration/v1/stacks/utils_test.go000066400000000000000000000046021367513235700324000ustar00rootroot00000000000000package 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)) } golang-github-gophercloud-gophercloud-0.12.0/openstack/orchestration/v1/stacktemplates/000077500000000000000000000000001367513235700314045ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/orchestration/v1/stacktemplates/doc.go000066400000000000000000000024261367513235700325040ustar00rootroot00000000000000/* 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 golang-github-gophercloud-gophercloud-0.12.0/openstack/orchestration/v1/stacktemplates/requests.go000066400000000000000000000026051367513235700336110ustar00rootroot00000000000000package stacktemplates import "github.com/gophercloud/gophercloud" // Get retreives data for the given stack template. func Get(c *gophercloud.ServiceClient, stackName, stackID string) (r GetResult) { resp, err := c.Get(getURL(c, stackName, stackID), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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 } resp, err := c.Post(validateURL(c), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } golang-github-gophercloud-gophercloud-0.12.0/openstack/orchestration/v1/stacktemplates/results.go000066400000000000000000000021561367513235700334400ustar00rootroot00000000000000package 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 } golang-github-gophercloud-gophercloud-0.12.0/openstack/orchestration/v1/stacktemplates/testing/000077500000000000000000000000001367513235700330615ustar00rootroot00000000000000doc.go000066400000000000000000000000631367513235700340750ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/orchestration/v1/stacktemplates/testing// orchestration_stacktemplates_v1 package testing fixtures.go000066400000000000000000000063331367513235700352070ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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.go000066400000000000000000000030021367513235700362360ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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) } golang-github-gophercloud-gophercloud-0.12.0/openstack/orchestration/v1/stacktemplates/urls.go000066400000000000000000000004601367513235700327200ustar00rootroot00000000000000package 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.12.0/openstack/placement/000077500000000000000000000000001367513235700251165ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/placement/v1/000077500000000000000000000000001367513235700254445ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/placement/v1/resourceproviders/000077500000000000000000000000001367513235700312315ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/placement/v1/resourceproviders/doc.go000066400000000000000000000023511367513235700323260ustar00rootroot00000000000000/* Package resourceproviders creates and lists all resource providers from the OpenStack Placement service. Example to list resource providers allPages, err := resourceproviders.List(placementClient, resourceproviders.ListOpts{}).AllPages() if err != nil { panic(err) } allResourceProviders, err := resourceproviders.ExtractResourceProviders(allPages) if err != nil { panic(err) } for _, r := range allResourceProviders { fmt.Printf("%+v\n", r) } Example to create resource providers createOpts := resourceproviders.CreateOpts{ Name: "new-rp", UUID: "b99b3ab4-3aa6-4fba-b827-69b88b9c544a", } rp, err := resourceproviders.Create(placementClient, createOpts).Extract() if err != nil { panic(err) } Example to get resource providers usages rp, err := resourceproviders.GetUsages(placementClient, resourceProviderID).Extract() if err != nil { panic(err) } Example to get resource providers inventories rp, err := resourceproviders.GetInventories(placementClient, resourceProviderID).Extract() if err != nil { panic(err) } Example to get resource providers traits rp, err := resourceproviders.GetTraits(placementClient, resourceProviderID).Extract() if err != nil { panic(err) } */ package resourceproviders golang-github-gophercloud-gophercloud-0.12.0/openstack/placement/v1/resourceproviders/requests.go000066400000000000000000000074771367513235700334520ustar00rootroot00000000000000package resourceproviders import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // ListOptsBuilder allows extensions to add additional parameters to the // List request. type ListOptsBuilder interface { ToResourceProviderListQuery() (string, error) } // ListOpts allows the filtering resource providers. Filtering is achieved by // passing in struct field values that map to the resource provider attributes // you want to see returned. type ListOpts struct { // Name is the name of the resource provider to filter the list Name string `q:"name"` // UUID is the uuid of the resource provider to filter the list UUID string `q:"uuid"` // MemberOf is a string representing aggregate uuids to filter or exclude from the list MemberOf string `q:"member_of"` // Resources is a comma-separated list of string indicating an amount of resource // of a specified class that a provider must have the capacity and availability to serve Resources string `q:"resources"` // InTree is a string that represents a resource provider UUID. The returned resource // providers will be in the same provider tree as the specified provider. InTree string `q:"in_tree"` // Required is comma-delimited list of string trait names. Required string `q:"required"` } // ToResourceProviderListQuery formats a ListOpts into a query string. func (opts ListOpts) ToResourceProviderListQuery() (string, error) { q, err := gophercloud.BuildQueryString(opts) return q.String(), err } // List makes a request against the API to list resource providers. func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { url := resourceProvidersListURL(client) if opts != nil { query, err := opts.ToResourceProviderListQuery() if err != nil { return pagination.Pager{Err: err} } url += query } return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { return ResourceProvidersPage{pagination.SinglePageBase(r)} }) } // CreateOptsBuilder allows extensions to add additional parameters to the // Create request. type CreateOptsBuilder interface { ToResourceProviderCreateMap() (map[string]interface{}, error) } // CreateOpts represents options used to create a resource provider. type CreateOpts struct { Name string `json:"name"` UUID string `json:"uuid,omitempty"` } // ToResourceProviderCreateMap constructs a request body from CreateOpts. func (opts CreateOpts) ToResourceProviderCreateMap() (map[string]interface{}, error) { b, err := gophercloud.BuildRequestBody(opts, "") if err != nil { return nil, err } return b, nil } // Create makes a request against the API to create a resource provider func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToResourceProviderCreateMap() if err != nil { r.Err = err return } resp, err := client.Post(resourceProvidersListURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } func GetUsages(client *gophercloud.ServiceClient, resourceProviderID string) (r GetUsagesResult) { resp, err := client.Get(getResourceProviderUsagesURL(client, resourceProviderID), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } func GetInventories(client *gophercloud.ServiceClient, resourceProviderID string) (r GetInventoriesResult) { resp, err := client.Get(getResourceProviderInventoriesURL(client, resourceProviderID), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } func GetTraits(client *gophercloud.ServiceClient, resourceProviderID string) (r GetTraitsResult) { resp, err := client.Get(getResourceProviderTraitsURL(client, resourceProviderID), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } golang-github-gophercloud-gophercloud-0.12.0/openstack/placement/v1/resourceproviders/results.go000066400000000000000000000106061367513235700332640ustar00rootroot00000000000000package resourceproviders import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) type ResourceProviderLinks struct { Href string `json:"href"` Rel string `json:"rel"` } // ResourceProvider are entities which provider consumable inventory of one or more classes of resource type ResourceProvider struct { // Generation is a consistent view marker that assists with the management of concurrent resource provider updates. Generation int `json:"generation"` // UUID of a resource provider. UUID string `json:"uuid"` // Links is a list of links associated with one resource provider. Links []ResourceProviderLinks `json:"links"` // Name of one resource provider. Name string `json:"name"` // The ParentProviderUUID contains the UUID of the immediate parent of the resource provider. // Requires microversion 1.14 or above ParentProviderUUID string `json:"parent_provider_uuid"` // The RootProviderUUID contains the read-only UUID of the top-most provider in this provider tree. // Requires microversion 1.14 or above RootProviderUUID string `json:"root_provider_uuid"` } type ResourceProviderUsage struct { ResourceProviderGeneration int `json:"resource_provider_generation"` Usages map[string]int `json:"usages"` } type Inventory struct { AllocationRatio float32 `json:"allocation_ratio"` MaxUnit int `json:"max_unit"` MinUnit int `json:"min_unit"` Reserved int `json:"reserved"` StepSize int `json:"step_size"` Total int `json:"total"` } type ResourceProviderInventories struct { ResourceProviderGeneration int `json:"resource_provider_generation"` Inventories map[string]Inventory `json:"inventories"` } type ResourceProviderTraits struct { ResourceProviderGeneration int `json:"resource_provider_generation"` Traits []string `json:"traits"` } // resourceProviderResult is the response of a base ResourceProvider result. type resourceProviderResult struct { gophercloud.Result } // Extract interpets any resourceProviderResult-base result as a ResourceProvider. func (r resourceProviderResult) Extract() (*ResourceProvider, error) { var s ResourceProvider err := r.ExtractInto(&s) return &s, err } // CreateResult is the result of a Create operation. Call its Extract // method to interpret it as a ResourceProvider. type CreateResult struct { resourceProviderResult } // ResourceProvidersPage contains a single page of all resource providers from a List call. type ResourceProvidersPage struct { pagination.SinglePageBase } // IsEmpty determines if a ResourceProvidersPage contains any results. func (page ResourceProvidersPage) IsEmpty() (bool, error) { resourceProviders, err := ExtractResourceProviders(page) return len(resourceProviders) == 0, err } // ExtractResourceProviders returns a slice of ResourceProvider from a List operation. func ExtractResourceProviders(r pagination.Page) ([]ResourceProvider, error) { var s struct { ResourceProviders []ResourceProvider `json:"resource_providers"` } err := (r.(ResourceProvidersPage)).ExtractInto(&s) return s.ResourceProviders, err } // GetUsagesResult is the response of a Get usage operations. Call its Extract method // to interpret it as a ResourceProviderUsage. type GetUsagesResult struct { gophercloud.Result } // Extract interprets a GetUsagesResult as a ResourceProviderUsage. func (r GetUsagesResult) Extract() (*ResourceProviderUsage, error) { var s ResourceProviderUsage err := r.ExtractInto(&s) return &s, err } // GetInventoriesResult is the response of a Get inventories operations. Call its Extract method // to interpret it as a ResourceProviderInventories. type GetInventoriesResult struct { gophercloud.Result } // Extract interprets a GetInventoriesResult as a ResourceProviderInventories. func (r GetInventoriesResult) Extract() (*ResourceProviderInventories, error) { var s ResourceProviderInventories err := r.ExtractInto(&s) return &s, err } // GetTraitsResult is the response of a Get traits operations. Call its Extract method // to interpret it as a ResourceProviderTraits. type GetTraitsResult struct { gophercloud.Result } // Extract interprets a GetTraitsResult as a ResourceProviderTraits. func (r GetTraitsResult) Extract() (*ResourceProviderTraits, error) { var s ResourceProviderTraits err := r.ExtractInto(&s) return &s, err } golang-github-gophercloud-gophercloud-0.12.0/openstack/placement/v1/resourceproviders/testing/000077500000000000000000000000001367513235700327065ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/placement/v1/resourceproviders/testing/doc.go000066400000000000000000000000601367513235700337760ustar00rootroot00000000000000// placement resource providers package testing fixtures.go000066400000000000000000000150371367513235700350350ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/placement/v1/resourceproviders/testingpackage testing import ( "fmt" "net/http" "testing" "github.com/gophercloud/gophercloud/openstack/placement/v1/resourceproviders" th "github.com/gophercloud/gophercloud/testhelper" fake "github.com/gophercloud/gophercloud/testhelper/client" ) const ResourceProviderTestID = "99c09379-6e52-4ef8-9a95-b9ce6f68452e" const ResourceProvidersBody = ` { "resource_providers": [ { "generation": 1, "uuid": "99c09379-6e52-4ef8-9a95-b9ce6f68452e", "links": [ { "href": "/resource_providers/99c09379-6e52-4ef8-9a95-b9ce6f68452e", "rel": "self" } ], "name": "vgr.localdomain", "parent_provider_uuid": "542df8ed-9be2-49b9-b4db-6d3183ff8ec8", "root_provider_uuid": "542df8ed-9be2-49b9-b4db-6d3183ff8ec8" }, { "generation": 2, "uuid": "d0b381e9-8761-42de-8e6c-bba99a96d5f5", "links": [ { "href": "/resource_providers/d0b381e9-8761-42de-8e6c-bba99a96d5f5", "rel": "self" } ], "name": "pony1", "parent_provider_uuid": null, "root_provider_uuid": "d0b381e9-8761-42de-8e6c-bba99a96d5f5" } ] } ` const ResourceProviderCreateBody = ` { "generation": 1, "uuid": "99c09379-6e52-4ef8-9a95-b9ce6f68452e", "links": [ { "href": "/resource_providers/99c09379-6e52-4ef8-9a95-b9ce6f68452e", "rel": "self" } ], "name": "vgr.localdomain", "parent_provider_uuid": "542df8ed-9be2-49b9-b4db-6d3183ff8ec8", "root_provider_uuid": "542df8ed-9be2-49b9-b4db-6d3183ff8ec8" } ` const UsagesBody = ` { "resource_provider_generation": 1, "usages": { "DISK_GB": 1, "MEMORY_MB": 512, "VCPU": 1 } } ` const InventoriesBody = ` { "inventories": { "DISK_GB": { "allocation_ratio": 1.0, "max_unit": 35, "min_unit": 1, "reserved": 0, "step_size": 1, "total": 35 }, "MEMORY_MB": { "allocation_ratio": 1.5, "max_unit": 5825, "min_unit": 1, "reserved": 512, "step_size": 1, "total": 5825 }, "VCPU": { "allocation_ratio": 16.0, "max_unit": 4, "min_unit": 1, "reserved": 0, "step_size": 1, "total": 4 } }, "resource_provider_generation": 7 } ` const TraitsBody = ` { "resource_provider_generation": 1, "traits": [ "CUSTOM_HW_FPGA_CLASS1", "CUSTOM_HW_FPGA_CLASS3" ] } ` var ExpectedResourceProvider1 = resourceproviders.ResourceProvider{ Generation: 1, UUID: "99c09379-6e52-4ef8-9a95-b9ce6f68452e", Links: []resourceproviders.ResourceProviderLinks{ { Href: "/resource_providers/99c09379-6e52-4ef8-9a95-b9ce6f68452e", Rel: "self", }, }, Name: "vgr.localdomain", ParentProviderUUID: "542df8ed-9be2-49b9-b4db-6d3183ff8ec8", RootProviderUUID: "542df8ed-9be2-49b9-b4db-6d3183ff8ec8", } var ExpectedResourceProvider2 = resourceproviders.ResourceProvider{ Generation: 2, UUID: "d0b381e9-8761-42de-8e6c-bba99a96d5f5", Links: []resourceproviders.ResourceProviderLinks{ { Href: "/resource_providers/d0b381e9-8761-42de-8e6c-bba99a96d5f5", Rel: "self", }, }, Name: "pony1", ParentProviderUUID: "", RootProviderUUID: "d0b381e9-8761-42de-8e6c-bba99a96d5f5", } var ExpectedResourceProviders = []resourceproviders.ResourceProvider{ ExpectedResourceProvider1, ExpectedResourceProvider2, } var ExpectedUsages = resourceproviders.ResourceProviderUsage{ ResourceProviderGeneration: 1, Usages: map[string]int{ "DISK_GB": 1, "MEMORY_MB": 512, "VCPU": 1, }, } var ExpectedInventories = resourceproviders.ResourceProviderInventories{ ResourceProviderGeneration: 7, Inventories: map[string]resourceproviders.Inventory{ "DISK_GB": resourceproviders.Inventory{ AllocationRatio: 1.0, MaxUnit: 35, MinUnit: 1, Reserved: 0, StepSize: 1, Total: 35, }, "MEMORY_MB": resourceproviders.Inventory{ AllocationRatio: 1.5, MaxUnit: 5825, MinUnit: 1, Reserved: 512, StepSize: 1, Total: 5825, }, "VCPU": resourceproviders.Inventory{ AllocationRatio: 16.0, MaxUnit: 4, MinUnit: 1, Reserved: 0, StepSize: 1, Total: 4, }, }, } var ExpectedTraits = resourceproviders.ResourceProviderTraits{ ResourceProviderGeneration: 1, Traits: []string{ "CUSTOM_HW_FPGA_CLASS1", "CUSTOM_HW_FPGA_CLASS3", }, } func HandleResourceProviderList(t *testing.T) { th.Mux.HandleFunc("/resource_providers", 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, ResourceProvidersBody) }) } func HandleResourceProviderCreate(t *testing.T) { th.Mux.HandleFunc("/resource_providers", 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, ResourceProviderCreateBody) }) } func HandleResourceProviderGetUsages(t *testing.T) { usageTestUrl := fmt.Sprintf("/resource_providers/%s/usages", ResourceProviderTestID) th.Mux.HandleFunc(usageTestUrl, 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, UsagesBody) }) } func HandleResourceProviderGetInventories(t *testing.T) { inventoriesTestUrl := fmt.Sprintf("/resource_providers/%s/inventories", ResourceProviderTestID) th.Mux.HandleFunc(inventoriesTestUrl, 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, InventoriesBody) }) } func HandleResourceProviderGetTraits(t *testing.T) { traitsTestUrl := fmt.Sprintf("/resource_providers/%s/traits", ResourceProviderTestID) th.Mux.HandleFunc(traitsTestUrl, 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, TraitsBody) }) } requests_test.go000066400000000000000000000043571367513235700361010ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/placement/v1/resourceproviders/testingpackage testing import ( "testing" "github.com/gophercloud/gophercloud/openstack/placement/v1/resourceproviders" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" fake "github.com/gophercloud/gophercloud/testhelper/client" ) func TestListResourceProviders(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleResourceProviderList(t) count := 0 err := resourceproviders.List(fake.ServiceClient(), resourceproviders.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { count++ actual, err := resourceproviders.ExtractResourceProviders(page) if err != nil { t.Errorf("Failed to extract resource providers: %v", err) return false, err } th.AssertDeepEquals(t, ExpectedResourceProviders, actual) return true, nil }) th.AssertNoErr(t, err) if count != 1 { t.Errorf("Expected 1 page, got %d", count) } } func TestCreateResourceProvider(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleResourceProviderCreate(t) expected := ExpectedResourceProvider1 opts := resourceproviders.CreateOpts{ Name: ExpectedResourceProvider1.Name, UUID: ExpectedResourceProvider1.UUID, } actual, err := resourceproviders.Create(fake.ServiceClient(), opts).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, &expected, actual) } func TestGetResourceProvidersUsages(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleResourceProviderGetUsages(t) actual, err := resourceproviders.GetUsages(fake.ServiceClient(), ResourceProviderTestID).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, ExpectedUsages, *actual) } func TestGetResourceProvidersInventories(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleResourceProviderGetInventories(t) actual, err := resourceproviders.GetInventories(fake.ServiceClient(), ResourceProviderTestID).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, ExpectedInventories, *actual) } func TestGetResourceProvidersTraits(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleResourceProviderGetTraits(t) actual, err := resourceproviders.GetTraits(fake.ServiceClient(), ResourceProviderTestID).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, ExpectedTraits, *actual) } golang-github-gophercloud-gophercloud-0.12.0/openstack/placement/v1/resourceproviders/urls.go000066400000000000000000000013631367513235700325500ustar00rootroot00000000000000package resourceproviders import "github.com/gophercloud/gophercloud" const ( apiName = "resource_providers" ) func resourceProvidersListURL(client *gophercloud.ServiceClient) string { return client.ServiceURL(apiName) } func getResourceProviderUsagesURL(client *gophercloud.ServiceClient, resourceProviderID string) string { return client.ServiceURL(apiName, resourceProviderID, "usages") } func getResourceProviderInventoriesURL(client *gophercloud.ServiceClient, resourceProviderID string) string { return client.ServiceURL(apiName, resourceProviderID, "inventories") } func getResourceProviderTraitsURL(client *gophercloud.ServiceClient, resourceProviderID string) string { return client.ServiceURL(apiName, resourceProviderID, "traits") } golang-github-gophercloud-gophercloud-0.12.0/openstack/sharedfilesystems/000077500000000000000000000000001367513235700267045ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/sharedfilesystems/apiversions/000077500000000000000000000000001367513235700312465ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/sharedfilesystems/apiversions/doc.go000066400000000000000000000002521367513235700323410ustar00rootroot00000000000000// Package apiversions provides information and interaction with the different // API versions for the Shared File System service, code-named Manila. package apiversions golang-github-gophercloud-gophercloud-0.12.0/openstack/sharedfilesystems/apiversions/errors.go000066400000000000000000000010271367513235700331110ustar00rootroot00000000000000package 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) } golang-github-gophercloud-gophercloud-0.12.0/openstack/sharedfilesystems/apiversions/requests.go000066400000000000000000000012121367513235700334440ustar00rootroot00000000000000package 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) { resp, err := client.Get(getURL(client, v), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } golang-github-gophercloud-gophercloud-0.12.0/openstack/sharedfilesystems/apiversions/results.go000066400000000000000000000035451367513235700333050ustar00rootroot00000000000000package 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)} } } golang-github-gophercloud-gophercloud-0.12.0/openstack/sharedfilesystems/apiversions/testing/000077500000000000000000000000001367513235700327235ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/sharedfilesystems/apiversions/testing/doc.go000066400000000000000000000000421367513235700340130ustar00rootroot00000000000000// apiversions_v1 package testing fixtures.go000066400000000000000000000136421367513235700350520ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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.go000066400000000000000000000024641367513235700361130ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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") } golang-github-gophercloud-gophercloud-0.12.0/openstack/sharedfilesystems/apiversions/urls.go000066400000000000000000000010451367513235700325620ustar00rootroot00000000000000package apiversions import ( "strings" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack/utils" ) func getURL(c *gophercloud.ServiceClient, version string) string { baseEndpoint, _ := utils.BaseEndpoint(c.Endpoint) endpoint := strings.TrimRight(baseEndpoint, "/") + "/" + strings.TrimRight(version, "/") + "/" return endpoint } func listURL(c *gophercloud.ServiceClient) string { baseEndpoint, _ := utils.BaseEndpoint(c.Endpoint) endpoint := strings.TrimRight(baseEndpoint, "/") + "/" return endpoint } golang-github-gophercloud-gophercloud-0.12.0/openstack/sharedfilesystems/v2/000077500000000000000000000000001367513235700272335ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/sharedfilesystems/v2/availabilityzones/000077500000000000000000000000001367513235700327645ustar00rootroot00000000000000requests.go000066400000000000000000000006321367513235700351100ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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.go000066400000000000000000000030441367513235700347360ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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/000077500000000000000000000000001367513235700343625ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/sharedfilesystems/v2/availabilityzonesfixtures.go000066400000000000000000000014471367513235700365700ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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.go000066400000000000000000000015621367513235700376270ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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.go000066400000000000000000000002531367513235700342210ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/sharedfilesystems/v2/availabilityzonespackage availabilityzones import "github.com/gophercloud/gophercloud" func listURL(c *gophercloud.ServiceClient) string { return c.ServiceURL("os-availability-zone") } golang-github-gophercloud-gophercloud-0.12.0/openstack/sharedfilesystems/v2/errors/000077500000000000000000000000001367513235700305475ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/sharedfilesystems/v2/errors/errors.go000066400000000000000000000026331367513235700324160ustar00rootroot00000000000000package errors import ( "encoding/json" "fmt" "github.com/gophercloud/gophercloud" ) type ManilaError struct { Code int `json:"code"` Message string `json:"message"` Details string `json:"details"` } type ErrorDetails map[string]ManilaError // error types from provider_client.go func ExtractErrorInto(rawError error, errorDetails *ErrorDetails) (err error) { switch e := rawError.(type) { case gophercloud.ErrDefault400: err = json.Unmarshal(e.ErrUnexpectedResponseCode.Body, errorDetails) case gophercloud.ErrDefault401: err = json.Unmarshal(e.ErrUnexpectedResponseCode.Body, errorDetails) case gophercloud.ErrDefault403: err = json.Unmarshal(e.ErrUnexpectedResponseCode.Body, errorDetails) case gophercloud.ErrDefault404: err = json.Unmarshal(e.ErrUnexpectedResponseCode.Body, errorDetails) case gophercloud.ErrDefault405: err = json.Unmarshal(e.ErrUnexpectedResponseCode.Body, errorDetails) case gophercloud.ErrDefault408: err = json.Unmarshal(e.ErrUnexpectedResponseCode.Body, errorDetails) case gophercloud.ErrDefault429: err = json.Unmarshal(e.ErrUnexpectedResponseCode.Body, errorDetails) case gophercloud.ErrDefault500: err = json.Unmarshal(e.ErrUnexpectedResponseCode.Body, errorDetails) case gophercloud.ErrDefault503: err = json.Unmarshal(e.ErrUnexpectedResponseCode.Body, errorDetails) default: err = fmt.Errorf("Unable to extract detailed error message") } return err } golang-github-gophercloud-gophercloud-0.12.0/openstack/sharedfilesystems/v2/errors/testing/000077500000000000000000000000001367513235700322245ustar00rootroot00000000000000fixtures.go000066400000000000000000000023101367513235700343410ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/sharedfilesystems/v2/errors/testingpackage testing import ( "fmt" "net/http" "testing" th "github.com/gophercloud/gophercloud/testhelper" fake "github.com/gophercloud/gophercloud/testhelper/client" ) const shareEndpoint = "/shares" var createRequest = `{ "share": { "name": "my_test_share", "size": 1, "share_proto": "NFS", "snapshot_id": "70bfbebc-d3ff-4528-8bbb-58422daa280b" } }` var createResponse = `{ "itemNotFound": { "code": 404, "message": "ShareSnapshotNotFound: Snapshot 70bfbebc-d3ff-4528-8bbb-58422daa280b could not be found." } }` // 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.StatusNotFound) fmt.Fprintf(w, createResponse) }) } request_test.go000066400000000000000000000017531367513235700352310ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/sharedfilesystems/v2/errors/testingpackage testing import ( "testing" "github.com/gophercloud/gophercloud/openstack/sharedfilesystems/v2/errors" "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", SnapshotID: "70bfbebc-d3ff-4528-8bbb-58422daa280b"} _, err := shares.Create(client.ServiceClient(), options).Extract() if err == nil { t.Fatal("Expected error") } detailedErr := errors.ErrorDetails{} e := errors.ExtractErrorInto(err, &detailedErr) th.AssertNoErr(t, e) for k, msg := range detailedErr { th.AssertEquals(t, k, "itemNotFound") th.AssertEquals(t, msg.Code, 404) th.AssertEquals(t, msg.Message, "ShareSnapshotNotFound: Snapshot 70bfbebc-d3ff-4528-8bbb-58422daa280b could not be found.") } } golang-github-gophercloud-gophercloud-0.12.0/openstack/sharedfilesystems/v2/messages/000077500000000000000000000000001367513235700310425ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/sharedfilesystems/v2/messages/requests.go000066400000000000000000000046371367513235700332560ustar00rootroot00000000000000package messages import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // Delete will delete the existing Message with the provided ID. func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { resp, err := client.Delete(deleteURL(client, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // ListOptsBuilder allows extensions to add additional parameters to the List // request. type ListOptsBuilder interface { ToMessageListQuery() (string, error) } // ListOpts holds options for listing Messages. It is passed to the // messages.List function. type ListOpts struct { // The message ID ID string `q:"id"` // The ID of the action during which the message was created ActionID string `q:"action_id"` // The ID of the message detail DetailID string `q:"detail_id"` // The message level MessageLevel string `q:"message_level"` // The UUID of the request during which the message was created RequestID string `q:"request_id"` // The UUID of the resource for which the message was created ResourceID string `q:"resource_id"` // The type of the resource for which the message was created ResourceType string `q:"resource_type"` // The key to sort a list of messages SortKey string `q:"sort_key"` // The key to sort a list of messages SortDir string `q:"sort_dir"` // The maximum number of messages to return Limit int `q:"limit"` } // ToMessageListQuery formats a ListOpts into a query string. func (opts ListOpts) ToMessageListQuery() (string, error) { q, err := gophercloud.BuildQueryString(opts) return q.String(), err } // List returns Messages 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.ToMessageListQuery() if err != nil { return pagination.Pager{Err: err} } url += query } return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { return MessagePage{pagination.SinglePageBase(r)} }) } // Get retrieves the Message with the provided ID. To extract the Message // object from the response, call the Extract method on the GetResult. func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { resp, err := client.Get(getURL(client, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } golang-github-gophercloud-gophercloud-0.12.0/openstack/sharedfilesystems/v2/messages/results.go000066400000000000000000000052271367513235700331000ustar00rootroot00000000000000package messages import ( "encoding/json" "time" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // Message contains all the information associated with an OpenStack // Message. type Message struct { // The message ID ID string `json:"id"` // The UUID of the project where the message was created ProjectID string `json:"project_id"` // The ID of the action during which the message was created ActionID string `json:"action_id"` // The ID of the message detail DetailID string `json:"detail_id"` // The message level MessageLevel string `json:"message_level"` // The UUID of the request during which the message was created RequestID string `json:"request_id"` // The UUID of the resource for which the message was created ResourceID string `json:"resource_id"` // The type of the resource for which the message was created ResourceType string `json:"resource_type"` // The message text UserMessage string `json:"user_message"` // The date and time stamp when the message was created CreatedAt time.Time `json:"-"` // The date and time stamp when the message will expire ExpiresAt time.Time `json:"-"` } func (r *Message) UnmarshalJSON(b []byte) error { type tmp Message var s struct { tmp CreatedAt gophercloud.JSONRFC3339MilliNoZ `json:"created_at"` ExpiresAt gophercloud.JSONRFC3339MilliNoZ `json:"expires_at"` } err := json.Unmarshal(b, &s) if err != nil { return err } *r = Message(s.tmp) r.CreatedAt = time.Time(s.CreatedAt) r.ExpiresAt = time.Time(s.ExpiresAt) return nil } type commonResult struct { gophercloud.Result } // MessagePage is a pagination.pager that is returned from a call to the List function. type MessagePage struct { pagination.SinglePageBase } // IsEmpty returns true if a ListResult contains no Messages. func (r MessagePage) IsEmpty() (bool, error) { messages, err := ExtractMessages(r) return len(messages) == 0, err } // ExtractMessages extracts and returns Messages. It is used while // iterating over a messages.List call. func ExtractMessages(r pagination.Page) ([]Message, error) { var s struct { Messages []Message `json:"messages"` } err := (r.(MessagePage)).ExtractInto(&s) return s.Messages, err } // Extract will get the Message object out of the commonResult object. func (r commonResult) Extract() (*Message, error) { var s struct { Message *Message `json:"message"` } err := r.ExtractInto(&s) return s.Message, 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 } golang-github-gophercloud-gophercloud-0.12.0/openstack/sharedfilesystems/v2/messages/testing/000077500000000000000000000000001367513235700325175ustar00rootroot00000000000000fixtures.go000066400000000000000000000117321367513235700346440ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/sharedfilesystems/v2/messages/testingpackage testing import ( "fmt" "net/http" "testing" th "github.com/gophercloud/gophercloud/testhelper" fake "github.com/gophercloud/gophercloud/testhelper/client" ) func MockDeleteResponse(t *testing.T) { th.Mux.HandleFunc("/messages/messageID", 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("/messages", 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, ` { "messages": [ { "resource_id": "0d0b883f-95ef-406c-b930-55612ee48a6d", "message_level": "ERROR", "user_message": "create: Could not find an existing share server or allocate one on the share network provided. You may use a different share network, or verify the network details in the share network and retry your request. If this doesn't work, contact your administrator to troubleshoot issues with your network.", "expires_at": "2019-01-06T08:53:38.000000", "id": "143a6cc2-1998-44d0-8356-22070b0ebdaa", "created_at": "2018-12-07T08:53:38.000000", "detail_id": "004", "request_id": "req-21767eee-22ca-40a4-b6c0-ae7d35cd434f", "project_id": "a5e9d48232dc4aa59a716b5ced963584", "resource_type": "SHARE", "action_id": "002" }, { "resource_id": "4336d74f-3bdc-4f27-9657-c01ec63680bf", "message_level": "ERROR", "user_message": "create: Could not find an existing share server or allocate one on the share network provided. You may use a different share network, or verify the network details in the share network and retry your request. If this doesn't work, contact your administrator to troubleshoot issues with your network.", "expires_at": "2019-01-06T08:53:34.000000", "id": "2076373e-13a7-4b84-9e67-15ce8cceaff8", "created_at": "2018-12-07T08:53:34.000000", "detail_id": "004", "request_id": "req-957792ed-f38b-42db-a86a-850f815cbbe9", "project_id": "a5e9d48232dc4aa59a716b5ced963584", "resource_type": "SHARE", "action_id": "002" } ] }`) }) } func MockFilteredListResponse(t *testing.T) { th.Mux.HandleFunc("/messages", 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, ` { "messages": [ { "resource_id": "4336d74f-3bdc-4f27-9657-c01ec63680bf", "message_level": "ERROR", "user_message": "create: Could not find an existing share server or allocate one on the share network provided. You may use a different share network, or verify the network details in the share network and retry your request. If this doesn't work, contact your administrator to troubleshoot issues with your network.", "expires_at": "2019-01-06T08:53:34.000000", "id": "2076373e-13a7-4b84-9e67-15ce8cceaff8", "created_at": "2018-12-07T08:53:34.000000", "detail_id": "004", "request_id": "req-957792ed-f38b-42db-a86a-850f815cbbe9", "project_id": "a5e9d48232dc4aa59a716b5ced963584", "resource_type": "SHARE", "action_id": "002" } ] }`) }) } func MockGetResponse(t *testing.T) { th.Mux.HandleFunc("/messages/2076373e-13a7-4b84-9e67-15ce8cceaff8", 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, ` { "message": { "resource_id": "4336d74f-3bdc-4f27-9657-c01ec63680bf", "message_level": "ERROR", "user_message": "create: Could not find an existing share server or allocate one on the share network provided. You may use a different share network, or verify the network details in the share network and retry your request. If this doesn't work, contact your administrator to troubleshoot issues with your network.", "expires_at": "2019-01-06T08:53:34.000000", "id": "2076373e-13a7-4b84-9e67-15ce8cceaff8", "created_at": "2018-12-07T08:53:34.000000", "detail_id": "004", "request_id": "req-957792ed-f38b-42db-a86a-850f815cbbe9", "project_id": "a5e9d48232dc4aa59a716b5ced963584", "resource_type": "SHARE", "action_id": "002" } }`) }) } requests_test.go000066400000000000000000000114021367513235700356770ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/sharedfilesystems/v2/messages/testingpackage testing import ( "testing" "time" "github.com/gophercloud/gophercloud/openstack/sharedfilesystems/v2/messages" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) // Verifies that message deletion works func TestDelete(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() MockDeleteResponse(t) res := messages.Delete(client.ServiceClient(), "messageID") th.AssertNoErr(t, res.Err) } // Verifies that messages can be listed correctly func TestList(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() MockListResponse(t) allPages, err := messages.List(client.ServiceClient(), &messages.ListOpts{}).AllPages() th.AssertNoErr(t, err) actual, err := messages.ExtractMessages(allPages) th.AssertNoErr(t, err) expected := []messages.Message{ { ResourceID: "0d0b883f-95ef-406c-b930-55612ee48a6d", MessageLevel: "ERROR", UserMessage: "create: Could not find an existing share server or allocate one on the share network provided. You may use a different share network, or verify the network details in the share network and retry your request. If this doesn't work, contact your administrator to troubleshoot issues with your network.", ExpiresAt: time.Date(2019, 1, 6, 8, 53, 38, 0, time.UTC), ID: "143a6cc2-1998-44d0-8356-22070b0ebdaa", CreatedAt: time.Date(2018, 12, 7, 8, 53, 38, 0, time.UTC), DetailID: "004", RequestID: "req-21767eee-22ca-40a4-b6c0-ae7d35cd434f", ProjectID: "a5e9d48232dc4aa59a716b5ced963584", ResourceType: "SHARE", ActionID: "002", }, { ResourceID: "4336d74f-3bdc-4f27-9657-c01ec63680bf", MessageLevel: "ERROR", UserMessage: "create: Could not find an existing share server or allocate one on the share network provided. You may use a different share network, or verify the network details in the share network and retry your request. If this doesn't work, contact your administrator to troubleshoot issues with your network.", ExpiresAt: time.Date(2019, 1, 6, 8, 53, 34, 0, time.UTC), ID: "2076373e-13a7-4b84-9e67-15ce8cceaff8", CreatedAt: time.Date(2018, 12, 7, 8, 53, 34, 0, time.UTC), DetailID: "004", RequestID: "req-957792ed-f38b-42db-a86a-850f815cbbe9", ProjectID: "a5e9d48232dc4aa59a716b5ced963584", ResourceType: "SHARE", ActionID: "002", }, } th.CheckDeepEquals(t, expected, actual) } // Verifies that messages list can be called with query parameters func TestFilteredList(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() MockFilteredListResponse(t) options := &messages.ListOpts{ RequestID: "req-21767eee-22ca-40a4-b6c0-ae7d35cd434f", } allPages, err := messages.List(client.ServiceClient(), options).AllPages() th.AssertNoErr(t, err) actual, err := messages.ExtractMessages(allPages) th.AssertNoErr(t, err) expected := []messages.Message{ { ResourceID: "4336d74f-3bdc-4f27-9657-c01ec63680bf", MessageLevel: "ERROR", UserMessage: "create: Could not find an existing share server or allocate one on the share network provided. You may use a different share network, or verify the network details in the share network and retry your request. If this doesn't work, contact your administrator to troubleshoot issues with your network.", ExpiresAt: time.Date(2019, 1, 6, 8, 53, 34, 0, time.UTC), ID: "2076373e-13a7-4b84-9e67-15ce8cceaff8", CreatedAt: time.Date(2018, 12, 7, 8, 53, 34, 0, time.UTC), DetailID: "004", RequestID: "req-957792ed-f38b-42db-a86a-850f815cbbe9", ProjectID: "a5e9d48232dc4aa59a716b5ced963584", ResourceType: "SHARE", ActionID: "002", }, } th.CheckDeepEquals(t, expected, actual) } // Verifies that it is possible to get a message func TestGet(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() MockGetResponse(t) expected := messages.Message{ ResourceID: "4336d74f-3bdc-4f27-9657-c01ec63680bf", MessageLevel: "ERROR", UserMessage: "create: Could not find an existing share server or allocate one on the share network provided. You may use a different share network, or verify the network details in the share network and retry your request. If this doesn't work, contact your administrator to troubleshoot issues with your network.", ExpiresAt: time.Date(2019, 1, 6, 8, 53, 34, 0, time.UTC), ID: "2076373e-13a7-4b84-9e67-15ce8cceaff8", CreatedAt: time.Date(2018, 12, 7, 8, 53, 34, 0, time.UTC), DetailID: "004", RequestID: "req-957792ed-f38b-42db-a86a-850f815cbbe9", ProjectID: "a5e9d48232dc4aa59a716b5ced963584", ResourceType: "SHARE", ActionID: "002", } n, err := messages.Get(client.ServiceClient(), "2076373e-13a7-4b84-9e67-15ce8cceaff8").Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, &expected, n) } golang-github-gophercloud-gophercloud-0.12.0/openstack/sharedfilesystems/v2/messages/urls.go000066400000000000000000000005261367513235700323610ustar00rootroot00000000000000package messages import "github.com/gophercloud/gophercloud" func listURL(c *gophercloud.ServiceClient) string { return c.ServiceURL("messages") } func getURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL("messages", id) } func deleteURL(c *gophercloud.ServiceClient, id string) string { return getURL(c, id) } golang-github-gophercloud-gophercloud-0.12.0/openstack/sharedfilesystems/v2/securityservices/000077500000000000000000000000001367513235700326465ustar00rootroot00000000000000requests.go000066400000000000000000000157021367513235700347760ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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 organizational unit (OU). Minimum supported microversion for OU is 2.44. OU string `json:"ou,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 } resp, err := client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete will delete the existing SecurityService with the provided ID. func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { resp, err := client.Delete(deleteURL(client, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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 organizational unit (OU). Minimum supported microversion for OU is 2.44. OU string `q:"ou"` // 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) { resp, err := client.Get(getURL(client, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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 organizational unit (OU). Minimum supported microversion for OU is 2.44. OU *string `json:"ou,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 } resp, err := client.Put(updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } results.go000066400000000000000000000065301367513235700346230ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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 organizational unit (OU) OU string `json:"ou"` // 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/000077500000000000000000000000001367513235700342445ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/sharedfilesystems/v2/securityservicesfixtures.go000066400000000000000000000150161367513235700364470ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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.go000066400000000000000000000135451367513235700375150ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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", } name := "SecServ2" options := securityservices.UpdateOpts{Name: &name} s, err := securityservices.Update(client.ServiceClient(), "securityServiceID", options).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, &expected, s) } golang-github-gophercloud-gophercloud-0.12.0/openstack/sharedfilesystems/v2/securityservices/urls.go000066400000000000000000000010751367513235700341650ustar00rootroot00000000000000package 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) } golang-github-gophercloud-gophercloud-0.12.0/openstack/sharedfilesystems/v2/sharenetworks/000077500000000000000000000000001367513235700321325ustar00rootroot00000000000000requests.go000066400000000000000000000221621367513235700342600ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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 } resp, err := client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete will delete the existing ShareNetwork with the provided ID. func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { resp, err := client.Delete(deleteURL(client, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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) { resp, err := client.Get(getURL(client, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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 } resp, err := client.Put(updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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 } resp, err := client.Post(addSecurityServiceURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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 } resp, err := client.Post(removeSecurityServiceURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } golang-github-gophercloud-gophercloud-0.12.0/openstack/sharedfilesystems/v2/sharenetworks/results.go000066400000000000000000000112431367513235700341630ustar00rootroot00000000000000package 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 } golang-github-gophercloud-gophercloud-0.12.0/openstack/sharedfilesystems/v2/sharenetworks/testing/000077500000000000000000000000001367513235700336075ustar00rootroot00000000000000fixtures.go000066400000000000000000000276461367513235700357470ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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.go000066400000000000000000000204021367513235700367670ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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", } name := "net_my2" description := "new description" options := sharenetworks.UpdateOpts{ Name: &name, Description: &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", } name := "net_my2" description := "new description" options := sharenetworks.UpdateOpts{ Name: &name, Description: &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) } golang-github-gophercloud-gophercloud-0.12.0/openstack/sharedfilesystems/v2/sharenetworks/urls.go000066400000000000000000000015041367513235700334460ustar00rootroot00000000000000package 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") } golang-github-gophercloud-gophercloud-0.12.0/openstack/sharedfilesystems/v2/shares/000077500000000000000000000000001367513235700305205ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/sharedfilesystems/v2/shares/doc.go000066400000000000000000000021301367513235700316100ustar00rootroot00000000000000/* Package shares provides information and interaction with the different API versions for the Shared File System service, code-named Manila. For more information, see: https://docs.openstack.org/api-ref/shared-file-system/ Example to Revert a Share to a Snapshot ID opts := &shares.RevertOpts{ // snapshot ID to revert to SnapshotID: "ddeac769-9742-497f-b985-5bcfa94a3fd6", } manilaClient.Microversion = "2.27" err := shares.Revert(manilaClient, shareID, opts).ExtractErr() if err != nil { panic(err) } Example to Reset a Share Status opts := &shares.ResetStatusOpts{ // a new Share Status Status: "available", } manilaClient.Microversion = "2.7" err := shares.ResetStatus(manilaClient, shareID, opts).ExtractErr() if err != nil { panic(err) } Example to Force Delete a Share manilaClient.Microversion = "2.7" err := shares.ForceDelete(manilaClient, shareID).ExtractErr() if err != nil { panic(err) } Example to Unmanage a Share manilaClient.Microversion = "2.7" err := shares.Unmanage(manilaClient, shareID).ExtractErr() if err != nil { panic(err) } */ package shares golang-github-gophercloud-gophercloud-0.12.0/openstack/sharedfilesystems/v2/shares/requests.go000066400000000000000000000566141367513235700327360ustar00rootroot00000000000000package 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 } resp, err := client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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) { resp, err := client.Delete(deleteURL(client, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get will get a single share with given UUID func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { resp, err := client.Get(getURL(client, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // ListExportLocations will list shareID's export locations. // Client must have Microversion set; minimum supported microversion for ListExportLocations is 2.9. func ListExportLocations(client *gophercloud.ServiceClient, id string) (r ListExportLocationsResult) { resp, err := client.Get(listExportLocationsURL(client, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // GetExportLocation will get shareID's export location by an ID. // Client must have Microversion set; minimum supported microversion for GetExportLocation is 2.9. func GetExportLocation(client *gophercloud.ServiceClient, shareID string, id string) (r GetExportLocationResult) { resp, err := client.Get(getExportLocationURL(client, shareID, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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 } resp, err := client.Post(grantAccessURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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 } resp, err := client.Post(revokeAccessURL(client, id), b, nil, &gophercloud.RequestOpts{ OkCodes: []int{200, 202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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} resp, err := client.Post(listAccessRightsURL(client, id), requestBody, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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 } resp, err := client.Post(extendURL(client, id), b, nil, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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 } resp, err := client.Post(shrinkURL(client, id), b, nil, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // UpdateOptsBuilder allows extensions to add additional parameters to the // Update request. type UpdateOptsBuilder interface { ToShareUpdateMap() (map[string]interface{}, error) } // UpdateOpts contain options for updating an existing Share. This object is passed // to the share.Update function. For more information about the parameters, see // the Share object. type UpdateOpts struct { // Share name. Manila share update logic doesn't have a "name" alias. DisplayName *string `json:"display_name,omitempty"` // Share description. Manila share update logic doesn't have a "description" alias. DisplayDescription *string `json:"display_description,omitempty"` // Determines whether or not the share is public IsPublic *bool `json:"is_public,omitempty"` } // ToShareUpdateMap assembles a request body based on the contents of an // UpdateOpts. func (opts UpdateOpts) ToShareUpdateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "share") } // Update will update the Share with provided information. To extract the updated // Share from the response, call the Extract method on the UpdateResult. func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToShareUpdateMap() if err != nil { r.Err = err return } resp, err := client.Put(updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // GetMetadata retrieves metadata of the specified share. To extract the retrieved // metadata from the response, call the Extract method on the MetadataResult. func GetMetadata(client *gophercloud.ServiceClient, id string) (r MetadataResult) { resp, err := client.Get(getMetadataURL(client, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // GetMetadatum retrieves a single metadata item of the specified share. To extract the retrieved // metadata from the response, call the Extract method on the GetMetadatumResult. func GetMetadatum(client *gophercloud.ServiceClient, id, key string) (r GetMetadatumResult) { resp, err := client.Get(getMetadatumURL(client, id, key), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // SetMetadataOpts contains options for setting share metadata. // For more information about these parameters, please, refer to the shared file systems API v2, // Share Metadata, Show share metadata documentation. type SetMetadataOpts struct { Metadata map[string]string `json:"metadata"` } // ToSetMetadataMap assembles a request body based on the contents of an // SetMetadataOpts. func (opts SetMetadataOpts) ToSetMetadataMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "") } // SetMetadataOptsBuilder allows extensions to add additional parameters to the // SetMetadata request. type SetMetadataOptsBuilder interface { ToSetMetadataMap() (map[string]interface{}, error) } // SetMetadata sets metadata of the specified share. // Existing metadata items are either kept or overwritten by the metadata from the request. // To extract the updated metadata from the response, call the Extract // method on the MetadataResult. func SetMetadata(client *gophercloud.ServiceClient, id string, opts SetMetadataOptsBuilder) (r MetadataResult) { b, err := opts.ToSetMetadataMap() if err != nil { r.Err = err return } resp, err := client.Post(setMetadataURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // UpdateMetadataOpts contains options for updating share metadata. // For more information about these parameters, please, refer to the shared file systems API v2, // Share Metadata, Update share metadata documentation. type UpdateMetadataOpts struct { Metadata map[string]string `json:"metadata"` } // ToUpdateMetadataMap assembles a request body based on the contents of an // UpdateMetadataOpts. func (opts UpdateMetadataOpts) ToUpdateMetadataMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "") } // UpdateMetadataOptsBuilder allows extensions to add additional parameters to the // UpdateMetadata request. type UpdateMetadataOptsBuilder interface { ToUpdateMetadataMap() (map[string]interface{}, error) } // UpdateMetadata updates metadata of the specified share. // All existing metadata items are discarded and replaced by the metadata from the request. // To extract the updated metadata from the response, call the Extract // method on the MetadataResult. func UpdateMetadata(client *gophercloud.ServiceClient, id string, opts UpdateMetadataOptsBuilder) (r MetadataResult) { b, err := opts.ToUpdateMetadataMap() if err != nil { r.Err = err return } resp, err := client.Post(updateMetadataURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // DeleteMetadatum deletes a single key-value pair from the metadata of the specified share. func DeleteMetadatum(client *gophercloud.ServiceClient, id, key string) (r DeleteMetadatumResult) { resp, err := client.Delete(deleteMetadatumURL(client, id, key), &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // RevertOptsBuilder allows extensions to add additional parameters to the // Revert request. type RevertOptsBuilder interface { ToShareRevertMap() (map[string]interface{}, error) } // RevertOpts contains options for reverting a Share to a snapshot. // For more information about these parameters, please, refer to the shared file systems API v2, // Share Actions, Revert share documentation. // Available only since Manila Microversion 2.27 type RevertOpts struct { // SnapshotID is a Snapshot ID to revert a Share to SnapshotID string `json:"snapshot_id"` } // ToShareRevertMap assembles a request body based on the contents of a // RevertOpts. func (opts RevertOpts) ToShareRevertMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "revert") } // Revert will revert the existing share to a Snapshot. RevertResult contains only the error. // To extract it, call the ExtractErr method on the RevertResult. // Client must have Microversion set; minimum supported microversion for Revert is 2.27. func Revert(client *gophercloud.ServiceClient, id string, opts RevertOptsBuilder) (r RevertResult) { b, err := opts.ToShareRevertMap() if err != nil { r.Err = err return } resp, err := client.Post(revertURL(client, id), b, nil, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // ResetStatusOptsBuilder allows extensions to add additional parameters to the // ResetStatus request. type ResetStatusOptsBuilder interface { ToShareResetStatusMap() (map[string]interface{}, error) } // ResetStatusOpts contains options for resetting a Share status. // For more information about these parameters, please, refer to the shared file systems API v2, // Share Actions, ResetStatus share documentation. type ResetStatusOpts struct { // Status is a share status to reset to. Must be "new", "error" or "active". Status string `json:"status"` } // ToShareResetStatusMap assembles a request body based on the contents of a // ResetStatusOpts. func (opts ResetStatusOpts) ToShareResetStatusMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "reset_status") } // ResetStatus will reset the existing share status. ResetStatusResult contains only the error. // To extract it, call the ExtractErr method on the ResetStatusResult. // Client must have Microversion set; minimum supported microversion for ResetStatus is 2.7. func ResetStatus(client *gophercloud.ServiceClient, id string, opts ResetStatusOptsBuilder) (r ResetStatusResult) { b, err := opts.ToShareResetStatusMap() if err != nil { r.Err = err return } resp, err := client.Post(resetStatusURL(client, id), b, nil, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // ForceDelete will delete the existing share in any state. ForceDeleteResult contains only the error. // To extract it, call the ExtractErr method on the ForceDeleteResult. // Client must have Microversion set; minimum supported microversion for ForceDelete is 2.7. func ForceDelete(client *gophercloud.ServiceClient, id string) (r ForceDeleteResult) { b := map[string]interface{}{ "force_delete": nil, } resp, err := client.Post(forceDeleteURL(client, id), b, nil, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Unmanage will remove a share from the management of the Shared File System // service without deleting the share. UnmanageResult contains only the error. // To extract it, call the ExtractErr method on the UnmanageResult. // Client must have Microversion set; minimum supported microversion for Unmanage is 2.7. func Unmanage(client *gophercloud.ServiceClient, id string) (r UnmanageResult) { b := map[string]interface{}{ "unmanage": nil, } resp, err := client.Post(unmanageURL(client, id), b, nil, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } golang-github-gophercloud-gophercloud-0.12.0/openstack/sharedfilesystems/v2/shares/results.go000066400000000000000000000252561367513235700325620ustar00rootroot00000000000000package 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:"-"` // Timestamp when the share was updated UpdatedAt time.Time `json:"-"` } func (r *Share) UnmarshalJSON(b []byte) error { type tmp Share 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 = Share(s.tmp) r.CreatedAt = time.Time(s.CreatedAt) r.UpdatedAt = time.Time(s.UpdatedAt) 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 } // UpdateResult contains the response body and error from an Update request. type UpdateResult struct { commonResult } // ListExportLocationsResult contains the result body and error from a // ListExportLocations request. type ListExportLocationsResult struct { gophercloud.Result } // GetExportLocationResult contains the result body and error from a // GetExportLocation request. type GetExportLocationResult 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 ListExportLocationsResult func (r ListExportLocationsResult) Extract() ([]ExportLocation, error) { var s struct { ExportLocations []ExportLocation `json:"export_locations"` } err := r.ExtractInto(&s) return s.ExportLocations, err } // Extract will get the Export Location from the GetExportLocationResult func (r GetExportLocationResult) Extract() (*ExportLocation, error) { var s struct { ExportLocation *ExportLocation `json:"export_location"` } err := r.ExtractInto(&s) return s.ExportLocation, 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 } // GetMetadatumResult contains the response body and error from a GetMetadatum request. type GetMetadatumResult struct { gophercloud.Result } // Extract will get the string-string map from GetMetadatumResult func (r GetMetadatumResult) Extract() (map[string]string, error) { var s struct { Meta map[string]string `json:"meta"` } err := r.ExtractInto(&s) return s.Meta, err } // MetadataResult contains the response body and error from GetMetadata, SetMetadata or UpdateMetadata requests. type MetadataResult struct { gophercloud.Result } // Extract will get the string-string map from MetadataResult 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 } // DeleteMetadatumResult contains the response body and error from a DeleteMetadatum request. type DeleteMetadatumResult struct { gophercloud.ErrResult } // RevertResult contains the response error from an Revert request. type RevertResult struct { gophercloud.ErrResult } // ResetStatusResult contains the response error from an ResetStatus request. type ResetStatusResult struct { gophercloud.ErrResult } // ForceDeleteResult contains the response error from an ForceDelete request. type ForceDeleteResult struct { gophercloud.ErrResult } // UnmanageResult contains the response error from an Unmanage request. type UnmanageResult struct { gophercloud.ErrResult } golang-github-gophercloud-gophercloud-0.12.0/openstack/sharedfilesystems/v2/shares/testing/000077500000000000000000000000001367513235700321755ustar00rootroot00000000000000fixtures.go000066400000000000000000000455131367513235700343260ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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 updateRequest = `{ "share": { "display_name": "my_new_test_share", "display_description": "", "is_public": false } }` var updateResponse = ` { "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", "export_locations": [], "share_server_id": "e268f4aa-d571-43dd-9ab3-f49ad06ffaef", "share_group_id": null, "snapshot_id": null, "id": "011d21e2-fbc3-4e4a-9993-9ea223f73264", "size": 1, "share_type": "25747776-08e5-494f-ab40-a64b9d20d8f7", "share_type_name": "default", "export_location": null, "project_id": "16e1ab15c35a457e9c2b2aa189f544e1", "metadata": { "project": "my_app", "aim": "doc" }, "status": "error", "description": "", "host": "manila2@generic1#GENERIC1", "task_state": null, "is_public": false, "snapshot_support": true, "name": "my_new_test_share", "created_at": "2015-09-18T10:25:24.000000", "share_proto": "NFS", "volume_type": "default" } } ` func MockUpdateResponse(t *testing.T) { th.Mux.HandleFunc(shareEndpoint+"/"+shareID, 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) }) } 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 listExportLocationsResponse = `{ "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 } ] }` // MockListExportLocationsResponse creates a mock get export locations response func MockListExportLocationsResponse(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, listExportLocationsResponse) }) } var getExportLocationResponse = `{ "export_location": { "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 } }` // MockGetExportLocationResponse creates a mock get export location response func MockGetExportLocationResponse(t *testing.T) { th.Mux.HandleFunc(shareEndpoint+"/"+shareID+"/export_locations/80ed63fc-83bc-4afc-b881-da4a345ac83d", 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, getExportLocationResponse) }) } 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) }) } var getMetadataResponse = `{ "metadata": { "foo": "bar" } }` // MockGetMetadataResponse creates a mock get metadata response func MockGetMetadataResponse(t *testing.T) { th.Mux.HandleFunc(shareEndpoint+"/"+shareID+"/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().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprint(w, getMetadataResponse) }) } var getMetadatumResponse = `{ "meta": { "foo": "bar" } }` // MockGetMetadatumResponse creates a mock get metadatum response func MockGetMetadatumResponse(t *testing.T, key string) { th.Mux.HandleFunc(shareEndpoint+"/"+shareID+"/metadata/"+key, 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().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprint(w, getMetadatumResponse) }) } var setMetadataRequest = `{ "metadata": { "foo": "bar" } }` var setMetadataResponse = `{ "metadata": { "foo": "bar" } }` // MockSetMetadataResponse creates a mock set metadata response func MockSetMetadataResponse(t *testing.T) { th.Mux.HandleFunc(shareEndpoint+"/"+shareID+"/metadata", 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, setMetadataRequest) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprint(w, setMetadataResponse) }) } var updateMetadataRequest = `{ "metadata": { "foo": "bar" } }` var updateMetadataResponse = `{ "metadata": { "foo": "bar" } }` // MockUpdateMetadataResponse creates a mock update metadata response func MockUpdateMetadataResponse(t *testing.T) { th.Mux.HandleFunc(shareEndpoint+"/"+shareID+"/metadata", 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, updateMetadataRequest) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprint(w, updateMetadataResponse) }) } var deleteMetadatumRequest = `{ "metadata": { "foo": "bar" } }` // MockDeleteMetadatumResponse creates a mock unset metadata response func MockDeleteMetadatumResponse(t *testing.T, key string) { th.Mux.HandleFunc(shareEndpoint+"/"+shareID+"/metadata/"+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.StatusOK) }) } var revertRequest = `{ "revert": { "snapshot_id": "ddeac769-9742-497f-b985-5bcfa94a3fd6" } }` // MockRevertResponse creates a mock revert share response func MockRevertResponse(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, revertRequest) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusAccepted) }) } var resetStatusRequest = `{ "reset_status": { "status": "error" } }` // MockResetStatusResponse creates a mock reset status share response func MockResetStatusResponse(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, resetStatusRequest) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusAccepted) }) } var forceDeleteRequest = `{ "force_delete": null }` // MockForceDeleteResponse creates a mock force delete share response func MockForceDeleteResponse(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, forceDeleteRequest) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusAccepted) }) } var unmanageRequest = `{ "unmanage": null }` // MockUnmanageResponse creates a mock unmanage share response func MockUnmanageResponse(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, unmanageRequest) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusAccepted) }) } request_test.go000066400000000000000000000272211367513235700352000ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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 TestUpdate(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() MockUpdateResponse(t) name := "my_new_test_share" description := "" iFalse := false options := &shares.UpdateOpts{ DisplayName: &name, DisplayDescription: &description, IsPublic: &iFalse, } n, err := shares.Update(client.ServiceClient(), shareID, options).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, n.Name, "my_new_test_share") th.AssertEquals(t, n.Description, "") th.AssertEquals(t, n.IsPublic, false) } 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{ { 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 TestListExportLocationsSuccess(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() MockListExportLocationsResponse(t) c := client.ServiceClient() // Client c must have Microversion set; minimum supported microversion for List Export Locations is 2.9 c.Microversion = "2.9" s, err := shares.ListExportLocations(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 TestGetExportLocationSuccess(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() MockGetExportLocationResponse(t) c := client.ServiceClient() // Client c must have Microversion set; minimum supported microversion for Get Export Location is 2.9 c.Microversion = "2.9" s, err := shares.GetExportLocation(c, shareID, "80ed63fc-83bc-4afc-b881-da4a345ac83d").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) } func TestGetMetadataSuccess(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() MockGetMetadataResponse(t) c := client.ServiceClient() actual, err := shares.GetMetadata(c, shareID).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, map[string]string{"foo": "bar"}, actual) } func TestGetMetadatumSuccess(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() MockGetMetadatumResponse(t, "foo") c := client.ServiceClient() actual, err := shares.GetMetadatum(c, shareID, "foo").Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, map[string]string{"foo": "bar"}, actual) } func TestSetMetadataSuccess(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() MockSetMetadataResponse(t) c := client.ServiceClient() actual, err := shares.SetMetadata(c, shareID, &shares.SetMetadataOpts{Metadata: map[string]string{"foo": "bar"}}).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, map[string]string{"foo": "bar"}, actual) } func TestUpdateMetadataSuccess(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() MockUpdateMetadataResponse(t) c := client.ServiceClient() actual, err := shares.UpdateMetadata(c, shareID, &shares.UpdateMetadataOpts{Metadata: map[string]string{"foo": "bar"}}).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, map[string]string{"foo": "bar"}, actual) } func TestUnsetMetadataSuccess(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() MockDeleteMetadatumResponse(t, "foo") c := client.ServiceClient() err := shares.DeleteMetadatum(c, shareID, "foo").ExtractErr() th.AssertNoErr(t, err) } func TestRevertSuccess(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() MockRevertResponse(t) c := client.ServiceClient() // Client c must have Microversion set; minimum supported microversion for Revert is 2.27 c.Microversion = "2.27" err := shares.Revert(c, shareID, &shares.RevertOpts{SnapshotID: "ddeac769-9742-497f-b985-5bcfa94a3fd6"}).ExtractErr() th.AssertNoErr(t, err) } func TestResetStatusSuccess(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() MockResetStatusResponse(t) c := client.ServiceClient() // Client c must have Microversion set; minimum supported microversion for ResetStatus is 2.7 c.Microversion = "2.7" err := shares.ResetStatus(c, shareID, &shares.ResetStatusOpts{Status: "error"}).ExtractErr() th.AssertNoErr(t, err) } func TestForceDeleteSuccess(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() MockForceDeleteResponse(t) c := client.ServiceClient() // Client c must have Microversion set; minimum supported microversion for ForceDelete is 2.7 c.Microversion = "2.7" err := shares.ForceDelete(c, shareID).ExtractErr() th.AssertNoErr(t, err) } func TestUnmanageSuccess(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() MockUnmanageResponse(t) c := client.ServiceClient() // Client c must have Microversion set; minimum supported microversion for Unmanage is 2.7 c.Microversion = "2.7" err := shares.Unmanage(c, shareID).ExtractErr() th.AssertNoErr(t, err) } golang-github-gophercloud-gophercloud-0.12.0/openstack/sharedfilesystems/v2/shares/urls.go000066400000000000000000000047321367513235700320420ustar00rootroot00000000000000package 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 updateURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL("shares", id) } func listExportLocationsURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL("shares", id, "export_locations") } func getExportLocationURL(c *gophercloud.ServiceClient, shareID, id string) string { return c.ServiceURL("shares", shareID, "export_locations", id) } 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") } func revertURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL("shares", id, "action") } func resetStatusURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL("shares", id, "action") } func forceDeleteURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL("shares", id, "action") } func unmanageURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL("shares", id, "action") } func getMetadataURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL("shares", id, "metadata") } func getMetadatumURL(c *gophercloud.ServiceClient, id, key string) string { return c.ServiceURL("shares", id, "metadata", key) } func setMetadataURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL("shares", id, "metadata") } func updateMetadataURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL("shares", id, "metadata") } func deleteMetadatumURL(c *gophercloud.ServiceClient, id, key string) string { return c.ServiceURL("shares", id, "metadata", key) } golang-github-gophercloud-gophercloud-0.12.0/openstack/sharedfilesystems/v2/sharetypes/000077500000000000000000000000001367513235700314225ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/sharedfilesystems/v2/sharetypes/requests.go000066400000000000000000000171171367513235700336330ustar00rootroot00000000000000package 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 } resp, err := client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete will delete the existing ShareType with the provided ID. func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { resp, err := client.Delete(deleteURL(client, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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) { resp, err := client.Get(getDefaultURL(client), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // GetExtraSpecs will retrieve the extra specifications for a given ShareType. func GetExtraSpecs(client *gophercloud.ServiceClient, id string) (r GetExtraSpecsResult) { resp, err := client.Get(getExtraSpecsURL(client, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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 } resp, err := client.Post(setExtraSpecsURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // UnsetExtraSpecs will unset an extra specification for an existing ShareType. func UnsetExtraSpecs(client *gophercloud.ServiceClient, id string, key string) (r UnsetExtraSpecsResult) { resp, err := client.Delete(unsetExtraSpecsURL(client, id, key), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // ShowAccess will show access details for an existing ShareType. func ShowAccess(client *gophercloud.ServiceClient, id string) (r ShowAccessResult) { resp, err := client.Get(showAccessURL(client, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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 } resp, err := client.Post(addAccessURL(client, id), b, nil, &gophercloud.RequestOpts{ OkCodes: []int{200, 202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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 } resp, err := client.Post(removeAccessURL(client, id), b, nil, &gophercloud.RequestOpts{ OkCodes: []int{200, 202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } golang-github-gophercloud-gophercloud-0.12.0/openstack/sharedfilesystems/v2/sharetypes/results.go000066400000000000000000000076501367513235700334620ustar00rootroot00000000000000package 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 } golang-github-gophercloud-gophercloud-0.12.0/openstack/sharedfilesystems/v2/sharetypes/testing/000077500000000000000000000000001367513235700330775ustar00rootroot00000000000000fixtures.go000066400000000000000000000215151367513235700352240ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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.go000066400000000000000000000137401367513235700362660ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/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) } golang-github-gophercloud-gophercloud-0.12.0/openstack/sharedfilesystems/v2/sharetypes/urls.go000066400000000000000000000021751367513235700327430ustar00rootroot00000000000000package 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.12.0/openstack/sharedfilesystems/v2/snapshots/000077500000000000000000000000001367513235700312555ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/sharedfilesystems/v2/snapshots/requests.go000066400000000000000000000141141367513235700334600ustar00rootroot00000000000000package 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 the options for create a Snapshot. This object is // passed to snapshots.Create(). For more information about these parameters, // please refer to the Snapshot object, or the shared file systems API v2 // documentation type CreateOpts struct { // The UUID of the share from which to create a snapshot ShareID string `json:"share_id" required:"true"` // Defines the snapshot name Name string `json:"name,omitempty"` // Defines the snapshot 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"` } // 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 } resp, err := client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201, 202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // ListOpts holds options for listing Snapshots. It is passed to the // snapshots.List function. type ListOpts struct { // (Admin only). Defines whether to list the requested resources for all projects. AllTenants bool `q:"all_tenants"` // The snapshot name. Name string `q:"name"` // Filter by a snapshot description. Description string `q:"description"` // Filters by a share from which the snapshot was created. ShareID string `q:"share_id"` // Filters by a snapshot size in GB. Size int `q:"size"` // Filters by a snapshot status. Status string `q:"status"` // The maximum number of snapshots to return. Limit int `q:"limit"` // The offset to define start point of snapshot or snapshot group listing. Offset int `q:"offset"` // The key to sort a list of snapshots. SortKey string `q:"sort_key"` // The direction to sort a list of snapshots. SortDir string `q:"sort_dir"` // The UUID of the project in which the snapshot was created. Useful with all_tenants parameter. ProjectID string `q:"project_id"` // The name pattern that can be used to filter snapshots, snapshot snapshots, snapshot networks or snapshot groups. NamePattern string `q:"name~"` // The description pattern that can be used to filter snapshots, snapshot snapshots, snapshot networks or snapshot groups. DescriptionPattern string `q:"description~"` } // ListOptsBuilder allows extensions to add additional parameters to the List // request. type ListOptsBuilder interface { ToSnapshotListQuery() (string, error) } // ToSnapshotListQuery formats a ListOpts into a query string. func (opts ListOpts) ToSnapshotListQuery() (string, error) { q, err := gophercloud.BuildQueryString(opts) return q.String(), err } // ListDetail returns []Snapshot 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.ToSnapshotListQuery() if err != nil { return pagination.Pager{Err: err} } url += query } return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { p := SnapshotPage{pagination.MarkerPageBase{PageResult: r}} p.MarkerPageBase.Owner = p return p }) } // Delete will delete an existing Snapshot with the given UUID. func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { resp, err := client.Delete(deleteURL(client, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get will get a single snapshot with given UUID func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { resp, err := client.Get(getURL(client, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // UpdateOptsBuilder allows extensions to add additional parameters to the // Update request. type UpdateOptsBuilder interface { ToSnapshotUpdateMap() (map[string]interface{}, error) } // UpdateOpts contain options for updating an existing Snapshot. This object is passed // to the snapshot.Update function. For more information about the parameters, see // the Snapshot object. type UpdateOpts struct { // Snapshot name. Manila snapshot update logic doesn't have a "name" alias. DisplayName *string `json:"display_name,omitempty"` // Snapshot description. Manila snapshot update logic doesn't have a "description" alias. DisplayDescription *string `json:"display_description,omitempty"` } // ToSnapshotUpdateMap assembles a request body based on the contents of an // UpdateOpts. func (opts UpdateOpts) ToSnapshotUpdateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "snapshot") } // Update will update the Snapshot with provided information. To extract the updated // Snapshot from the response, call the Extract method on the UpdateResult. func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToSnapshotUpdateMap() if err != nil { r.Err = err return } resp, err := client.Put(updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } golang-github-gophercloud-gophercloud-0.12.0/openstack/sharedfilesystems/v2/snapshots/results.go000066400000000000000000000076661367513235700333240ustar00rootroot00000000000000package snapshots import ( "encoding/json" "net/url" "strconv" "time" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) const ( invalidMarker = "-1" ) // Snapshot contains all information associated with an OpenStack Snapshot type Snapshot struct { // The UUID of the snapshot ID string `json:"id"` // The name of the snapshot Name string `json:"name,omitempty"` // A description of the snapshot Description string `json:"description,omitempty"` // UUID of the share from which the snapshot was created ShareID string `json:"share_id"` // The shared file system protocol ShareProto string `json:"share_proto"` // Size of the snapshot share in GB ShareSize int `json:"share_size"` // Size of the snapshot in GB Size int `json:"size"` // The snapshot status Status string `json:"status"` // The UUID of the project in which the snapshot was created ProjectID string `json:"project_id"` // Timestamp when the snapshot was created CreatedAt time.Time `json:"-"` // Snapshot links for pagination Links []map[string]string `json:"links"` } 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 nil } type commonResult struct { gophercloud.Result } // Extract will get the Snapshot object from the commonResult func (r commonResult) Extract() (*Snapshot, error) { var s struct { Snapshot *Snapshot `json:"snapshot"` } err := r.ExtractInto(&s) return s.Snapshot, err } // CreateResult contains the response body and error from a Create request. type CreateResult struct { commonResult } // SnapshotPage is a pagination.pager that is returned from a call to the List function. type SnapshotPage struct { pagination.MarkerPageBase } // NextPageURL generates the URL for the page of results after this one. func (r SnapshotPage) 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 SnapshotPage) LastMarker() (string, error) { snapshots, err := ExtractSnapshots(r) if err != nil { return invalidMarker, err } if len(snapshots) == 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 SnapshotPage) IsEmpty() (bool, error) { snapshots, err := ExtractSnapshots(r) return len(snapshots) == 0, err } // ExtractSnapshots extracts and returns a Snapshot slice. 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 } // 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 } golang-github-gophercloud-gophercloud-0.12.0/openstack/sharedfilesystems/v2/snapshots/testing/000077500000000000000000000000001367513235700327325ustar00rootroot00000000000000fixtures.go000066400000000000000000000136741367513235700350660ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/sharedfilesystems/v2/snapshots/testingpackage testing import ( "fmt" "net/http" "testing" th "github.com/gophercloud/gophercloud/testhelper" fake "github.com/gophercloud/gophercloud/testhelper/client" ) const ( snapshotEndpoint = "/snapshots" snapshotID = "bc082e99-3bdb-4400-b95e-b85c7a41622c" shareID = "19865c43-3b91-48c9-85a0-7ac4d6bb0efe" ) var createRequest = `{ "snapshot": { "share_id": "19865c43-3b91-48c9-85a0-7ac4d6bb0efe", "name": "test snapshot", "description": "test description" } }` var createResponse = `{ "snapshot": { "status": "creating", "share_id": "19865c43-3b91-48c9-85a0-7ac4d6bb0efe", "description": "test description", "links": [ { "href": "http://172.18.198.54:8786/v2/16e1ab15c35a457e9c2b2aa189f544e1/snapshots/9897f5ca-2559-4a4c-b761-d3439c0c9455", "rel": "self" }, { "href": "http://172.18.198.54:8786/16e1ab15c35a457e9c2b2aa189f544e1/snapshots/9897f5ca-2559-4a4c-b761-d3439c0c9455", "rel": "bookmark" } ], "id": "bc082e99-3bdb-4400-b95e-b85c7a41622c", "size": 1, "user_id": "619e2ad074321cf246b03a89e95afee95fb26bb0b2d1fc7ba3bd30fcca25588a", "name": "test snapshot", "created_at": "2019-01-09T10:22:39.613550", "share_proto": "NFS", "project_id": "16e1ab15c35a457e9c2b2aa189f544e1", "share_size": 1 } }` // MockCreateResponse creates a mock response func MockCreateResponse(t *testing.T) { th.Mux.HandleFunc(snapshotEndpoint, 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.StatusAccepted) fmt.Fprintf(w, createResponse) }) } // MockDeleteResponse creates a mock delete response func MockDeleteResponse(t *testing.T) { th.Mux.HandleFunc(snapshotEndpoint+"/"+snapshotID, 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 updateRequest = `{ "snapshot": { "display_name": "my_new_test_snapshot", "display_description": "" } }` var updateResponse = `{ "snapshot": { "status": "available", "share_id": "19865c43-3b91-48c9-85a0-7ac4d6bb0efe", "description": "", "links": [ { "href": "http://172.18.198.54:8786/v2/16e1ab15c35a457e9c2b2aa189f544e1/snapshots/9897f5ca-2559-4a4c-b761-d3439c0c9455", "rel": "self" }, { "href": "http://172.18.198.54:8786/16e1ab15c35a457e9c2b2aa189f544e1/snapshots/9897f5ca-2559-4a4c-b761-d3439c0c9455", "rel": "bookmark" } ], "id": "9897f5ca-2559-4a4c-b761-d3439c0c9455", "size": 1, "user_id": "619e2ad074321cf246b03a89e95afee95fb26bb0b2d1fc7ba3bd30fcca25588a", "name": "my_new_test_snapshot", "created_at": "2019-01-09T10:22:39.613550", "share_proto": "NFS", "project_id": "16e1ab15c35a457e9c2b2aa189f544e1", "share_size": 1 } }` func MockUpdateResponse(t *testing.T) { th.Mux.HandleFunc(snapshotEndpoint+"/"+snapshotID, 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) }) } var getResponse = `{ "snapshot": { "status": "available", "share_id": "19865c43-3b91-48c9-85a0-7ac4d6bb0efe", "description": null, "links": [ { "href": "http://172.18.198.54:8786/v2/16e1ab15c35a457e9c2b2aa189f544e1/snapshots/bc082e99-3bdb-4400-b95e-b85c7a41622c", "rel": "self" }, { "href": "http://172.18.198.54:8786/16e1ab15c35a457e9c2b2aa189f544e1/snapshots/bc082e99-3bdb-4400-b95e-b85c7a41622c", "rel": "bookmark" } ], "id": "bc082e99-3bdb-4400-b95e-b85c7a41622c", "size": 1, "user_id": "619e2ad074321cf246b03a89e95afee95fb26bb0b2d1fc7ba3bd30fcca25588a", "name": "new_app_snapshot", "created_at": "2019-01-06T11:11:02.000000", "share_proto": "NFS", "project_id": "16e1ab15c35a457e9c2b2aa189f544e1", "share_size": 1 } }` // MockGetResponse creates a mock get response func MockGetResponse(t *testing.T) { th.Mux.HandleFunc(snapshotEndpoint+"/"+snapshotID, 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 = `{ "snapshots": [ { "status": "available", "share_id": "19865c43-3b91-48c9-85a0-7ac4d6bb0efe", "description": null, "links": [ { "href": "http://172.18.198.54:8786/v2/16e1ab15c35a457e9c2b2aa189f544e1/snapshots/bc082e99-3bdb-4400-b95e-b85c7a41622c", "rel": "self" }, { "href": "http://172.18.198.54:8786/16e1ab15c35a457e9c2b2aa189f544e1/snapshots/bc082e99-3bdb-4400-b95e-b85c7a41622c", "rel": "bookmark" } ], "id": "bc082e99-3bdb-4400-b95e-b85c7a41622c", "size": 1, "user_id": "619e2ad074321cf246b03a89e95afee95fb26bb0b2d1fc7ba3bd30fcca25588a", "name": "new_app_snapshot", "created_at": "2019-01-06T11:11:02.000000", "share_proto": "NFS", "project_id": "16e1ab15c35a457e9c2b2aa189f544e1", "share_size": 1 } ] }` var listDetailEmptyResponse = `{"snapshots": []}` // MockListDetailResponse creates a mock detailed-list response func MockListDetailResponse(t *testing.T) { th.Mux.HandleFunc(snapshotEndpoint+"/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) } }) } request_test.go000066400000000000000000000065631367513235700357430ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/sharedfilesystems/v2/snapshots/testingpackage testing import ( "testing" "time" "github.com/gophercloud/gophercloud/openstack/sharedfilesystems/v2/snapshots" 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 := &snapshots.CreateOpts{ShareID: shareID, Name: "test snapshot", Description: "test description"} n, err := snapshots.Create(client.ServiceClient(), options).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, n.Name, "test snapshot") th.AssertEquals(t, n.Description, "test description") th.AssertEquals(t, n.ShareProto, "NFS") th.AssertEquals(t, n.ShareSize, 1) th.AssertEquals(t, n.Size, 1) } func TestUpdate(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() MockUpdateResponse(t) name := "my_new_test_snapshot" description := "" options := &snapshots.UpdateOpts{ DisplayName: &name, DisplayDescription: &description, } n, err := snapshots.Update(client.ServiceClient(), snapshotID, options).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, n.Name, "my_new_test_snapshot") th.AssertEquals(t, n.Description, "") } func TestDelete(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() MockDeleteResponse(t) result := snapshots.Delete(client.ServiceClient(), snapshotID) th.AssertNoErr(t, result.Err) } func TestGet(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() MockGetResponse(t) s, err := snapshots.Get(client.ServiceClient(), snapshotID).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, s, &snapshots.Snapshot{ ID: snapshotID, Name: "new_app_snapshot", Description: "", ShareID: "19865c43-3b91-48c9-85a0-7ac4d6bb0efe", ShareProto: "NFS", ShareSize: 1, Size: 1, Status: "available", ProjectID: "16e1ab15c35a457e9c2b2aa189f544e1", CreatedAt: time.Date(2019, time.January, 06, 11, 11, 02, 0, time.UTC), Links: []map[string]string{ { "href": "http://172.18.198.54:8786/v2/16e1ab15c35a457e9c2b2aa189f544e1/snapshots/bc082e99-3bdb-4400-b95e-b85c7a41622c", "rel": "self", }, { "href": "http://172.18.198.54:8786/16e1ab15c35a457e9c2b2aa189f544e1/snapshots/bc082e99-3bdb-4400-b95e-b85c7a41622c", "rel": "bookmark", }, }, }) } func TestListDetail(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() MockListDetailResponse(t) allPages, err := snapshots.ListDetail(client.ServiceClient(), &snapshots.ListOpts{}).AllPages() th.AssertNoErr(t, err) actual, err := snapshots.ExtractSnapshots(allPages) th.AssertNoErr(t, err) th.AssertDeepEquals(t, actual, []snapshots.Snapshot{ snapshots.Snapshot{ ID: snapshotID, Name: "new_app_snapshot", Description: "", ShareID: "19865c43-3b91-48c9-85a0-7ac4d6bb0efe", ShareProto: "NFS", ShareSize: 1, Size: 1, Status: "available", ProjectID: "16e1ab15c35a457e9c2b2aa189f544e1", CreatedAt: time.Date(2019, time.January, 06, 11, 11, 02, 0, time.UTC), Links: []map[string]string{ { "href": "http://172.18.198.54:8786/v2/16e1ab15c35a457e9c2b2aa189f544e1/snapshots/bc082e99-3bdb-4400-b95e-b85c7a41622c", "rel": "self", }, { "href": "http://172.18.198.54:8786/16e1ab15c35a457e9c2b2aa189f544e1/snapshots/bc082e99-3bdb-4400-b95e-b85c7a41622c", "rel": "bookmark", }, }, }, }) } golang-github-gophercloud-gophercloud-0.12.0/openstack/sharedfilesystems/v2/snapshots/urls.go000066400000000000000000000010761367513235700325750ustar00rootroot00000000000000package snapshots import "github.com/gophercloud/gophercloud" func createURL(c *gophercloud.ServiceClient) string { return c.ServiceURL("snapshots") } func listDetailURL(c *gophercloud.ServiceClient) string { return c.ServiceURL("snapshots", "detail") } func deleteURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL("snapshots", id) } func getURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL("snapshots", id) } func updateURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL("snapshots", id) } golang-github-gophercloud-gophercloud-0.12.0/openstack/testing/000077500000000000000000000000001367513235700246235ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/testing/client_test.go000066400000000000000000000204251367513235700274720ustar00rootroot00000000000000package 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.12.0/openstack/testing/doc.go000066400000000000000000000000351367513235700257150ustar00rootroot00000000000000// openstack package testing golang-github-gophercloud-gophercloud-0.12.0/openstack/testing/endpoint_location_test.go000066400000000000000000000162751367513235700317340ustar00rootroot00000000000000package testing import ( "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) { actual, err := openstack.V2EndpointURL(&catalog2, gophercloud.EndpointOpts{ Type: "same", Region: "same", Availability: gophercloud.AvailabilityPublic, }) th.AssertNoErr(t, err) th.AssertEquals(t, "https://public.correct.com/", actual) } 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) { actual, err := openstack.V3EndpointURL(&catalog3, gophercloud.EndpointOpts{ Type: "same", Region: "same", Availability: gophercloud.AvailabilityPublic, }) th.AssertNoErr(t, err) th.AssertEquals(t, "https://public.correct.com/", actual) } 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.12.0/openstack/utils/000077500000000000000000000000001367513235700243065ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/utils/base_endpoint.go000066400000000000000000000010111367513235700274400ustar00rootroot00000000000000package 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.12.0/openstack/utils/choose_version.go000066400000000000000000000054161367513235700276700ustar00rootroot00000000000000package 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.12.0/openstack/utils/testing/000077500000000000000000000000001367513235700257635ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/utils/testing/base_endpoint_test.go000066400000000000000000000042311367513235700321630ustar00rootroot00000000000000package 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) } } golang-github-gophercloud-gophercloud-0.12.0/openstack/utils/testing/choose_version_test.go000066400000000000000000000056731367513235700324110ustar00rootroot00000000000000package 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.12.0/openstack/utils/testing/doc.go000066400000000000000000000000301367513235700270500ustar00rootroot00000000000000//utils package testing golang-github-gophercloud-gophercloud-0.12.0/openstack/workflow/000077500000000000000000000000001367513235700250205ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/workflow/v2/000077500000000000000000000000001367513235700253475ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/workflow/v2/crontriggers/000077500000000000000000000000001367513235700300575ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/workflow/v2/crontriggers/doc.go000066400000000000000000000041011367513235700311470ustar00rootroot00000000000000/* 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. List cron triggers To filter cron triggers from a list request, you can use advanced filters with special FilterType to check for equality, non equality, values greater or lower, etc. Default Filter checks equality, but you can override it with provided filter type. listOpts := crontriggers.ListOpts{ WorkflowName: &executions.ListFilter{ Value: "Workflow1,Workflow2", Filter: executions.FilterIN, }, CreatedAt: &executions.ListDateFilter{ Value: time.Date(2018, time.January, 1, 0, 0, 0, 0, time.UTC), Filter: executions.FilterGTE, }, } allPages, err := crontriggers.List(mistralClient, listOpts).AllPages() if err != nil { panic(err) } allCrontriggers, err := crontriggers.ExtractCronTriggers(allPages) if err != nil { panic(err) } for _, ct := range allCrontriggers { fmt.Printf("%+v\n", ct) } Create a cron trigger. This example will start the workflow "echo" each day at 8am, and it will end after 10 executions. createOpts := &crontriggers.CreateOpts{ Name: "daily", Pattern: "0 8 * * *", WorkflowName: "echo", RemainingExecutions: 10, WorkflowParams: map[string]interface{}{ "msg": "hello", }, WorkflowInput: map[string]interface{}{ "msg": "world", }, } crontrigger, err := crontriggers.Create(mistralClient, createOpts).Extract() if err != nil { panic(err) } Get a cron trigger crontrigger, err := crontriggers.Get(mistralClient, "0520ffd8-f7f1-4f2e-845b-55d953a1cf46").Extract() if err != nil { panic(err) } fmt.Printf(%+v\n", crontrigger) Delete a cron trigger res := crontriggers.Delete(mistralClient, "0520ffd8-f7f1-4f2e-845b-55d953a1cf46") if res.Err != nil { panic(res.Err) } */ package crontriggers golang-github-gophercloud-gophercloud-0.12.0/openstack/workflow/v2/crontriggers/requests.go000066400000000000000000000200321367513235700322560ustar00rootroot00000000000000package crontriggers import ( "encoding/json" "fmt" "net/url" "reflect" "time" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // 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 } resp, err := client.Post(createURL(client), b, &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete deletes the specified cron trigger. func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { resp, err := client.Delete(deleteURL(client, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get retrieves details of a single cron trigger. // Use Extract to convert its result into an CronTrigger. func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { resp, err := client.Get(getURL(client, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // ListOptsBuilder allows extension to add additional parameters to the List request. type ListOptsBuilder interface { ToCronTriggerListQuery() (string, error) } // ListOpts filters the result returned by the List() function. type ListOpts struct { // WorkflowName allows to filter by workflow name. WorkflowName *ListFilter `q:"-"` // WorkflowID allows to filter by workflow id. WorkflowID string `q:"workflow_id"` // WorkflowInput allows to filter by specific workflow inputs. WorkflowInput map[string]interface{} `q:"-"` // WorkflowParams allows to filter by specific workflow parameters. WorkflowParams map[string]interface{} `q:"-"` // Scope filters by the trigger's scope. // Values can be "private" or "public". Scope string `q:"scope"` // Name allows to filter by trigger name. Name *ListFilter `q:"-"` // Pattern allows to filter by pattern. Pattern *ListFilter `q:"-"` // RemainingExecutions allows to filter by remaining executions. RemainingExecutions *ListIntFilter `q:"-"` // FirstExecutionTime allows to filter by first execution time. FirstExecutionTime *ListDateFilter `q:"-"` // NextExecutionTime allows to filter by next execution time. NextExecutionTime *ListDateFilter `q:"-"` // CreatedAt allows to filter by trigger creation date. CreatedAt *ListDateFilter `q:"-"` // UpdatedAt allows to filter by trigger last update date. UpdatedAt *ListDateFilter `q:"-"` // ProjectID allows to filter by given project id. Admin required. ProjectID string `q:"project_id"` // AllProjects requests to get executions of all projects. Admin required. AllProjects int `q:"all_projects"` // SortDirs allows to select sort direction. // It can be "asc" or "desc" (default). SortDirs string `q:"sort_dirs"` // SortKeys allows to sort by one of the cron trigger attributes. SortKeys string `q:"sort_keys"` // 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"` } // ListFilter allows to filter string parameters with different filters. // Empty value for Filter checks for equality. type ListFilter struct { Filter FilterType Value string } func (l ListFilter) String() string { if l.Filter != "" { return fmt.Sprintf("%s:%s", l.Filter, l.Value) } return l.Value } // ListDateFilter allows to filter date parameters with different filters. // Empty value for Filter checks for equality. type ListDateFilter struct { Filter FilterType Value time.Time } func (l ListDateFilter) String() string { v := l.Value.Format(gophercloud.RFC3339ZNoTNoZ) if l.Filter != "" { return fmt.Sprintf("%s:%s", l.Filter, v) } return v } // ListIntFilter allows to filter integer parameters with different filters. // Empty value for Filter checks for equality. type ListIntFilter struct { Filter FilterType Value int } func (l ListIntFilter) String() string { v := fmt.Sprintf("%d", l.Value) if l.Filter != "" { return fmt.Sprintf("%s:%s", l.Filter, v) } return v } // FilterType represents a valid filter to use for filtering executions. type FilterType string const ( // FilterEQ checks equality. FilterEQ = "eq" // FilterNEQ checks non equality. FilterNEQ = "neq" // FilterIN checks for belonging in a list, comma separated. FilterIN = "in" // FilterNIN checks for values that does not belong from a list, comma separated. FilterNIN = "nin" // FilterGT checks for values strictly greater. FilterGT = "gt" // FilterGTE checks for values greater or equal. FilterGTE = "gte" // FilterLT checks for values strictly lower. FilterLT = "lt" // FilterLTE checks for values lower or equal. FilterLTE = "lte" // FilterHas checks for values that contains the requested parameter. FilterHas = "has" ) // ToCronTriggerListQuery formats a ListOpts into a query string. func (opts ListOpts) ToCronTriggerListQuery() (string, error) { q, err := gophercloud.BuildQueryString(opts) if err != nil { return "", err } params := q.Query() for queryParam, value := range map[string]map[string]interface{}{"workflow_params": opts.WorkflowParams, "workflow_input": opts.WorkflowInput} { if value != nil { b, err := json.Marshal(value) if err != nil { return "", err } params.Add(queryParam, string(b)) } } for queryParam, value := range map[string]fmt.Stringer{ "workflow_name": opts.WorkflowName, "name": opts.Name, "pattern": opts.Pattern, "remaining_executions": opts.RemainingExecutions, "first_execution_time": opts.FirstExecutionTime, "next_execution_time": opts.NextExecutionTime, "created_at": opts.CreatedAt, "updated_at": opts.UpdatedAt, } { if !reflect.ValueOf(value).IsNil() { params.Add(queryParam, value.String()) } } q = &url.URL{RawQuery: params.Encode()} return q.String(), nil } // 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.ToCronTriggerListQuery() if err != nil { return pagination.Pager{Err: err} } url += query } return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { return CronTriggerPage{pagination.LinkedPageBase{PageResult: r}} }) } golang-github-gophercloud-gophercloud-0.12.0/openstack/workflow/v2/crontriggers/results.go000066400000000000000000000105251367513235700321120ustar00rootroot00000000000000package crontriggers import ( "encoding/json" "time" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) 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 } // GetResult is the response of Get operations. Call its Extract method to interpret it as a CronTrigger. type GetResult struct { commonResult } // 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 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 s.WorkflowInput != "" { if err := json.Unmarshal([]byte(s.WorkflowInput), &r.WorkflowInput); err != nil { return err } } if s.WorkflowParams != "" { if err := json.Unmarshal([]byte(s.WorkflowParams), &r.WorkflowParams); err != nil { return err } } return nil } // CronTriggerPage contains a single page of all cron triggers from a List call. type CronTriggerPage struct { pagination.LinkedPageBase } // IsEmpty checks if an CronTriggerPage contains any results. func (r CronTriggerPage) IsEmpty() (bool, error) { exec, err := ExtractCronTriggers(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 CronTriggerPage) NextPageURL() (string, error) { var s struct { Next string `json:"next"` } err := r.ExtractInto(&s) if err != nil { return "", err } return s.Next, nil } // ExtractCronTriggers get the list of cron triggers from a page acquired from the List call. func ExtractCronTriggers(r pagination.Page) ([]CronTrigger, error) { var s struct { CronTriggers []CronTrigger `json:"cron_triggers"` } err := (r.(CronTriggerPage)).ExtractInto(&s) return s.CronTriggers, err } golang-github-gophercloud-gophercloud-0.12.0/openstack/workflow/v2/crontriggers/testing/000077500000000000000000000000001367513235700315345ustar00rootroot00000000000000requests_test.go000066400000000000000000000210511367513235700347150ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/workflow/v2/crontriggers/testingpackage testing import ( "fmt" "net/http" "net/url" "reflect" "testing" "time" "github.com/gophercloud/gophercloud/openstack/workflow/v2/crontriggers" "github.com/gophercloud/gophercloud/pagination" 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": "2018-09-12 15:48:18", "first_execution_time": "2018-09-12 17:48:00", "id": "0520ffd8-f7f1-4f2e-845b-55d953a1cf46", "name": "crontrigger", "next_execution_time": "2018-09-12 17:48:00", "pattern": "0 0 1 1 *", "project_id": "778c0f25df0d492a9a868ee9e2fbb513", "remaining_executions": 42, "scope": "private", "updated_at": null, "workflow_id": "604a3a1e-94e3-4066-a34a-aa56873ef236", "workflow_input": "{\"msg\": \"hello\"}", "workflow_name": "workflow_echo", "workflow_params": "{\"msg\": \"world\"}" } `) }) firstExecution := time.Date(2018, time.September, 12, 17, 48, 0, 0, time.UTC) opts := &crontriggers.CreateOpts{ WorkflowID: "604a3a1e-94e3-4066-a34a-aa56873ef236", 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: "0520ffd8-f7f1-4f2e-845b-55d953a1cf46", Name: "crontrigger", Pattern: "0 0 1 1 *", ProjectID: "778c0f25df0d492a9a868ee9e2fbb513", RemainingExecutions: 42, Scope: "private", WorkflowID: "604a3a1e-94e3-4066-a34a-aa56873ef236", WorkflowName: "workflow_echo", WorkflowParams: map[string]interface{}{ "msg": "world", }, WorkflowInput: map[string]interface{}{ "msg": "hello", }, CreatedAt: time.Date(2018, time.September, 12, 15, 48, 18, 0, time.UTC), FirstExecutionTime: &firstExecution, NextExecutionTime: &firstExecution, } if !reflect.DeepEqual(expected, actual) { t.Errorf("Expected %#v, but was %#v", expected, actual) } } func TestDeleteCronTrigger(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/cron_triggers/0520ffd8-f7f1-4f2e-845b-55d953a1cf46", 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 := crontriggers.Delete(fake.ServiceClient(), "0520ffd8-f7f1-4f2e-845b-55d953a1cf46") th.AssertNoErr(t, res.Err) } func TestGetCronTrigger(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/cron_triggers/0520ffd8-f7f1-4f2e-845b-55d953a1cf46", 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": "2018-09-12 15:48:18", "first_execution_time": "2018-09-12 17:48:00", "id": "0520ffd8-f7f1-4f2e-845b-55d953a1cf46", "name": "crontrigger", "next_execution_time": "2018-09-12 17:48:00", "pattern": "0 0 1 1 *", "project_id": "778c0f25df0d492a9a868ee9e2fbb513", "remaining_executions": 42, "scope": "private", "updated_at": null, "workflow_id": "604a3a1e-94e3-4066-a34a-aa56873ef236", "workflow_input": "{\"msg\": \"hello\"}", "workflow_name": "workflow_echo", "workflow_params": "{\"msg\": \"world\"}" } `) }) actual, err := crontriggers.Get(fake.ServiceClient(), "0520ffd8-f7f1-4f2e-845b-55d953a1cf46").Extract() if err != nil { t.Fatalf("Unable to get cron trigger: %v", err) } firstExecution := time.Date(2018, time.September, 12, 17, 48, 0, 0, time.UTC) expected := &crontriggers.CronTrigger{ ID: "0520ffd8-f7f1-4f2e-845b-55d953a1cf46", Name: "crontrigger", Pattern: "0 0 1 1 *", ProjectID: "778c0f25df0d492a9a868ee9e2fbb513", RemainingExecutions: 42, Scope: "private", WorkflowID: "604a3a1e-94e3-4066-a34a-aa56873ef236", WorkflowName: "workflow_echo", WorkflowParams: map[string]interface{}{ "msg": "world", }, WorkflowInput: map[string]interface{}{ "msg": "hello", }, CreatedAt: time.Date(2018, time.September, 12, 15, 48, 18, 0, time.UTC), FirstExecutionTime: &firstExecution, NextExecutionTime: &firstExecution, } if !reflect.DeepEqual(expected, actual) { t.Errorf("Expected %#v, but was %#v", expected, actual) } } func TestListCronTriggers(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/cron_triggers", 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, `{ "cron_triggers": [ { "created_at": "2018-09-12 15:48:18", "first_execution_time": "2018-09-12 17:48:00", "id": "0520ffd8-f7f1-4f2e-845b-55d953a1cf46", "name": "crontrigger", "next_execution_time": "2018-09-12 17:48:00", "pattern": "0 0 1 1 *", "project_id": "778c0f25df0d492a9a868ee9e2fbb513", "remaining_executions": 42, "scope": "private", "updated_at": null, "workflow_id": "604a3a1e-94e3-4066-a34a-aa56873ef236", "workflow_input": "{\"msg\": \"hello\"}", "workflow_name": "workflow_echo", "workflow_params": "{\"msg\": \"world\"}" } ], "next": "%s/cron_triggers?marker=0520ffd8-f7f1-4f2e-845b-55d953a1cf46" }`, th.Server.URL) case "0520ffd8-f7f1-4f2e-845b-55d953a1cf46": fmt.Fprintf(w, `{ "cron_triggers": [] }`) default: t.Fatalf("Unexpected marker: [%s]", marker) } }) pages := 0 // Get all cron triggers err := crontriggers.List(fake.ServiceClient(), nil).EachPage(func(page pagination.Page) (bool, error) { pages++ actual, err := crontriggers.ExtractCronTriggers(page) if err != nil { return false, err } firstExecution := time.Date(2018, time.September, 12, 17, 48, 0, 0, time.UTC) expected := []crontriggers.CronTrigger{ crontriggers.CronTrigger{ ID: "0520ffd8-f7f1-4f2e-845b-55d953a1cf46", Name: "crontrigger", Pattern: "0 0 1 1 *", ProjectID: "778c0f25df0d492a9a868ee9e2fbb513", RemainingExecutions: 42, Scope: "private", WorkflowID: "604a3a1e-94e3-4066-a34a-aa56873ef236", WorkflowName: "workflow_echo", WorkflowParams: map[string]interface{}{ "msg": "world", }, WorkflowInput: map[string]interface{}{ "msg": "hello", }, CreatedAt: time.Date(2018, time.September, 12, 15, 48, 18, 0, time.UTC), FirstExecutionTime: &firstExecution, NextExecutionTime: &firstExecution, }, } 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 TestToExecutionListQuery(t *testing.T) { for expected, opts := range map[string]*crontriggers.ListOpts{ newValue("workflow_input", `{"msg":"Hello"}`): &crontriggers.ListOpts{ WorkflowInput: map[string]interface{}{ "msg": "Hello", }, }, newValue("name", `neq:not_name`): &crontriggers.ListOpts{ Name: &crontriggers.ListFilter{ Filter: crontriggers.FilterNEQ, Value: "not_name", }, }, newValue("workflow_name", `eq:workflow`): &crontriggers.ListOpts{ WorkflowName: &crontriggers.ListFilter{ Filter: crontriggers.FilterEQ, Value: "workflow", }, }, newValue("created_at", `gt:2018-01-01 00:00:00`): &crontriggers.ListOpts{ CreatedAt: &crontriggers.ListDateFilter{ Filter: crontriggers.FilterGT, Value: time.Date(2018, time.January, 1, 0, 0, 0, 0, time.UTC), }, }, } { actual, _ := opts.ToCronTriggerListQuery() th.AssertEquals(t, expected, actual) } } func newValue(param, value string) string { v := url.Values{} v.Add(param, value) return "?" + v.Encode() } golang-github-gophercloud-gophercloud-0.12.0/openstack/workflow/v2/crontriggers/urls.go000066400000000000000000000007771367513235700314060ustar00rootroot00000000000000package crontriggers import "github.com/gophercloud/gophercloud" func createURL(client *gophercloud.ServiceClient) string { return client.ServiceURL("cron_triggers") } func deleteURL(client *gophercloud.ServiceClient, id string) string { return client.ServiceURL("cron_triggers", id) } func getURL(client *gophercloud.ServiceClient, id string) string { return client.ServiceURL("cron_triggers", id) } func listURL(client *gophercloud.ServiceClient) string { return client.ServiceURL("cron_triggers") } golang-github-gophercloud-gophercloud-0.12.0/openstack/workflow/v2/executions/000077500000000000000000000000001367513235700275355ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/workflow/v2/executions/doc.go000066400000000000000000000037141367513235700306360ustar00rootroot00000000000000/* Package executions provides interaction with the execution API in the OpenStack Mistral service. An execution is a one-shot execution of a specific workflow. Each execution contains all information about workflow itself, about execution process, state, input and output data. An execution represents also the execution of a cron trigger. Each run of a cron trigger will generate an execution. List executions To filter executions from a list request, you can use advanced filters with special FilterType to check for equality, non equality, values greater or lower, etc. Default Filter checks equality, but you can override it with provided filter type. // List all executions from a given workflow list with a creation date upper than 2018-01-01 00:00:00 listOpts := executions.ListOpts{ WorkflowName: &executions.ListFilter{ Value: "Workflow1,Workflow2", Filter: executions.FilterIN, }, CreatedAt: &executions.ListDateFilter{ Value: time.Date(2018, time.January, 1, 0, 0, 0, 0, time.UTC), Filter: executions.FilterGTE, }, } 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) } 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, createOpts).Extract() if err != nil { panic(err) } Get an execution execution, err := executions.Get(mistralClient, "50bb59f1-eb77-4017-a77f-6d575b002667").Extract() if err != nil { panic(err) } fmt.Printf(%+v\n", execution) Delete an execution res := executions.Delete(mistralClient, "50bb59f1-eb77-4017-a77f-6d575b002667") if res.Err != nil { panic(res.Err) } */ package executions golang-github-gophercloud-gophercloud-0.12.0/openstack/workflow/v2/executions/requests.go000066400000000000000000000167671367513235700317600ustar00rootroot00000000000000package executions import ( "encoding/json" "fmt" "net/url" "reflect" "time" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // 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 } resp, err := client.Post(createURL(client), b, &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get retrieves details of a single execution. // Use ExtractExecution to convert its result into an Execution. func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { resp, err := client.Get(getURL(client, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete deletes the specified execution. func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { resp, err := client.Delete(deleteURL(client, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // ListOptsBuilder allows extension to add additional parameters to the List request. type ListOptsBuilder interface { ToExecutionListQuery() (string, error) } // ListOpts filters the result returned by the List() function. type ListOpts struct { // WorkflowName allows to filter by workflow name. WorkflowName *ListFilter `q:"-"` // WorkflowID allows to filter by workflow id. WorkflowID string `q:"workflow_id"` // Description allows to filter by execution description. Description *ListFilter `q:"-"` // Params allows to filter by specific parameters. Params map[string]interface{} `q:"-"` // TaskExecutionID allows to filter with a specific task execution id. TaskExecutionID string `q:"task_execution_id"` // RootExecutionID allows to filter with a specific root execution id. RootExecutionID string `q:"root_execution_id"` // State allows to filter by execution state. // Possible values are IDLE, RUNNING, PAUSED, SUCCESS, ERROR, CANCELLED. State *ListFilter `q:"-"` // StateInfo allows to filter by state info. StateInfo *ListFilter `q:"-"` // Input allows to filter by specific input. Input map[string]interface{} `q:"-"` // Output allows to filter by specific output. Output map[string]interface{} `q:"-"` // CreatedAt allows to filter by execution creation date. CreatedAt *ListDateFilter `q:"-"` // UpdatedAt allows to filter by last execution update date. UpdatedAt *ListDateFilter `q:"-"` // IncludeOutput requests to include the output for all executions in the list. IncludeOutput bool `q:"-"` // ProjectID allows to filter by given project id. Admin required. ProjectID string `q:"project_id"` // AllProjects requests to get executions of all projects. Admin required. AllProjects int `q:"all_projects"` // SortDir allows to select sort direction. // It can be "asc" or "desc" (default). SortDirs string `q:"sort_dirs"` // SortKey allows to sort by one of the execution attributes. SortKeys string `q:"sort_keys"` // 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 // executions. Limit int `q:"limit"` } // ListFilter allows to filter string parameters with different filters. // Empty value for Filter checks for equality. type ListFilter struct { Filter FilterType Value string } func (l ListFilter) String() string { if l.Filter != "" { return fmt.Sprintf("%s:%s", l.Filter, l.Value) } return l.Value } // ListDateFilter allows to filter date parameters with different filters. // Empty value for Filter checks for equality. type ListDateFilter struct { Filter FilterType Value time.Time } func (l ListDateFilter) String() string { v := l.Value.Format(gophercloud.RFC3339ZNoTNoZ) if l.Filter != "" { return fmt.Sprintf("%s:%s", l.Filter, v) } return v } // FilterType represents a valid filter to use for filtering executions. type FilterType string const ( // FilterEQ checks equality. FilterEQ = "eq" // FilterNEQ checks non equality. FilterNEQ = "neq" // FilterIN checks for belonging in a list, comma separated. FilterIN = "in" // FilterNIN checks for values that does not belong from a list, comma separated. FilterNIN = "nin" // FilterGT checks for values strictly greater. FilterGT = "gt" // FilterGTE checks for values greater or equal. FilterGTE = "gte" // FilterLT checks for values strictly lower. FilterLT = "lt" // FilterLTE checks for values lower or equal. FilterLTE = "lte" // FilterHas checks for values that contains the requested parameter. FilterHas = "has" ) // ToExecutionListQuery formats a ListOpts into a query string. func (opts ListOpts) ToExecutionListQuery() (string, error) { q, err := gophercloud.BuildQueryString(opts) if err != nil { return "", err } params := q.Query() if opts.IncludeOutput { params.Add("include_output", "1") } for queryParam, value := range map[string]map[string]interface{}{"params": opts.Params, "input": opts.Input, "output": opts.Output} { if value != nil { b, err := json.Marshal(value) if err != nil { return "", err } params.Add(queryParam, string(b)) } } for queryParam, value := range map[string]fmt.Stringer{ "created_at": opts.CreatedAt, "updated_at": opts.UpdatedAt, "workflow_name": opts.WorkflowName, "description": opts.Description, "state": opts.State, "state_info": opts.StateInfo, } { if !reflect.ValueOf(value).IsNil() { params.Add(queryParam, value.String()) } } q = &url.URL{RawQuery: params.Encode()} return q.String(), nil } // List performs a call to list executions. // You may provide options to filter the executions. func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { url := listURL(client) if opts != nil { query, err := opts.ToExecutionListQuery() if err != nil { return pagination.Pager{Err: err} } url += query } return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { return ExecutionPage{pagination.LinkedPageBase{PageResult: r}} }) } golang-github-gophercloud-gophercloud-0.12.0/openstack/workflow/v2/executions/results.go000066400000000000000000000103401367513235700315630ustar00rootroot00000000000000package executions import ( "encoding/json" "time" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) 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 } // GetResult is the response of Get operations. Call its Extract method to interpret it as an Execution. type GetResult 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 } // DeleteResult is the result from a Delete operation. Call its ExtractErr method to determine the success of the call. type DeleteResult struct { gophercloud.ErrResult } // 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 s.Input != "" { if err := json.Unmarshal([]byte(s.Input), &r.Input); err != nil { return err } } if s.Output != "" { if err := json.Unmarshal([]byte(s.Output), &r.Output); err != nil { return err } } if s.Params != "" { if err := json.Unmarshal([]byte(s.Params), &r.Params); err != nil { return err } } return nil } // ExecutionPage contains a single page of all executions from a List call. type ExecutionPage struct { pagination.LinkedPageBase } // IsEmpty checks if an ExecutionPage contains any results. func (r ExecutionPage) IsEmpty() (bool, error) { exec, err := ExtractExecutions(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 ExecutionPage) NextPageURL() (string, error) { var s struct { Next string `json:"next"` } err := r.ExtractInto(&s) if err != nil { return "", err } return s.Next, nil } // ExtractExecutions get the list of executions from a page acquired from the List call. func ExtractExecutions(r pagination.Page) ([]Execution, error) { var s struct { Executions []Execution `json:"executions"` } err := (r.(ExecutionPage)).ExtractInto(&s) return s.Executions, err } golang-github-gophercloud-gophercloud-0.12.0/openstack/workflow/v2/executions/testing/000077500000000000000000000000001367513235700312125ustar00rootroot00000000000000requests_test.go000066400000000000000000000171551367513235700344050ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/workflow/v2/executions/testingpackage testing import ( "fmt" "net/http" "net/url" "reflect" "testing" "time" "github.com/gophercloud/gophercloud/openstack/workflow/v2/executions" "github.com/gophercloud/gophercloud/pagination" 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) } } func TestGetExecution(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/executions/50bb59f1-eb77-4017-a77f-6d575b002667", 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": "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": "" } `) }) actual, err := executions.Get(fake.ServiceClient(), "50bb59f1-eb77-4017-a77f-6d575b002667").Extract() if err != nil { t.Fatalf("Unable to get 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) } } func TestDeleteExecution(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/executions/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 := executions.Delete(fake.ServiceClient(), "1") th.AssertNoErr(t, res.Err) } func TestListExecutions(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/executions", 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, `{ "executions": [ { "created_at": "2018-09-12 14:48:49", "description": "description", "id": "50bb59f1-eb77-4017-a77f-6d575b002667", "input": "{\"msg\": \"Hello\"}", "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": "" } ], "next": "%s/executions?marker=50bb59f1-eb77-4017-a77f-6d575b002667" }`, th.Server.URL) case "50bb59f1-eb77-4017-a77f-6d575b002667": fmt.Fprintf(w, `{ "executions": [] }`) default: t.Fatalf("Unexpected marker: [%s]", marker) } }) pages := 0 // Get all executions err := executions.List(fake.ServiceClient(), nil).EachPage(func(page pagination.Page) (bool, error) { pages++ actual, err := executions.ExtractExecutions(page) if err != nil { return false, err } expected := []executions.Execution{ 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{}{}, }, 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) } return true, nil }) if err != nil { t.Fatal(err) } if pages != 1 { t.Errorf("Expected one page, got %d", pages) } } func TestToExecutionListQuery(t *testing.T) { for expected, opts := range map[string]*executions.ListOpts{ newValue("input", `{"msg":"Hello"}`): &executions.ListOpts{ Input: map[string]interface{}{ "msg": "Hello", }, }, newValue("description", `neq:not_description`): &executions.ListOpts{ Description: &executions.ListFilter{ Filter: executions.FilterNEQ, Value: "not_description", }, }, newValue("created_at", `gt:2018-01-01 00:00:00`): &executions.ListOpts{ CreatedAt: &executions.ListDateFilter{ Filter: executions.FilterGT, Value: time.Date(2018, time.January, 1, 0, 0, 0, 0, time.UTC), }, }, } { actual, _ := opts.ToExecutionListQuery() th.AssertEquals(t, expected, actual) } } func newValue(param, value string) string { v := url.Values{} v.Add(param, value) return "?" + v.Encode() } golang-github-gophercloud-gophercloud-0.12.0/openstack/workflow/v2/executions/urls.go000066400000000000000000000007611367513235700310550ustar00rootroot00000000000000package executions import "github.com/gophercloud/gophercloud" func createURL(client *gophercloud.ServiceClient) string { return client.ServiceURL("executions") } func getURL(client *gophercloud.ServiceClient, id string) string { return client.ServiceURL("executions", id) } func deleteURL(client *gophercloud.ServiceClient, id string) string { return client.ServiceURL("executions", id) } func listURL(client *gophercloud.ServiceClient) string { return client.ServiceURL("executions") } golang-github-gophercloud-gophercloud-0.12.0/openstack/workflow/v2/workflows/000077500000000000000000000000001367513235700274045ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/workflow/v2/workflows/doc.go000066400000000000000000000034301367513235700305000ustar00rootroot00000000000000/* 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. Workflow definition is written in Mistral Workflow Language v2. You can find all specification here: https://docs.openstack.org/mistral/latest/user/wf_lang_v2.html 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) } Get a workflow workflow, err := workflows.Get(mistralClient, "604a3a1e-94e3-4066-a34a-aa56873ef236").Extract() if err != nil { t.Fatalf("Unable to get workflow %s: %v", id, err) } fmt.Printf("%+v\n", workflow) Create a workflow workflowDefinition := `--- version: '2.0' workflow_echo: description: Simple workflow example type: direct input: - msg tasks: test: action: std.echo output="<% $.msg %>"` createOpts := &workflows.CreateOpts{ Definition: strings.NewReader(workflowDefinition), Scope: "private", Namespace: "some-namespace", } workflow, err := workflows.Create(mistralClient, createOpts).Extract() if err != nil { panic(err) } fmt.Printf("%+v\n", workflow) Delete a workflow res := workflows.Delete(fake.ServiceClient(), "604a3a1e-94e3-4066-a34a-aa56873ef236") if res.Err != nil { panic(res.Err) } */ package workflows golang-github-gophercloud-gophercloud-0.12.0/openstack/workflow/v2/workflows/requests.go000066400000000000000000000142111367513235700316050ustar00rootroot00000000000000package workflows import ( "fmt" "io" "net/url" "reflect" "strings" "time" "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 } resp, err := client.Post(url, b, &r.Body, &gophercloud.RequestOpts{ MoreHeaders: map[string]string{ "Content-Type": "text/plain", "Accept": "", // Drop default JSON Accept header }, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete deletes the specified execution. func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { resp, err := client.Delete(deleteURL(client, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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) { resp, err := client.Get(getURL(client, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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 { // Scope filters by the workflow's scope. // Values can be "private" or "public". Scope string `q:"scope"` // CreatedAt allows to filter by workflow creation date. CreatedAt *ListDateFilter `q:"-"` // UpdatedAt allows to filter by last execution update date. UpdatedAt *ListDateFilter `q:"-"` // Name allows to filter by workflow name. Name *ListFilter `q:"-"` // Tags allows to filter by tags. Tags []string // Definition allows to filter by workflow definition. Definition *ListFilter `q:"-"` // Namespace allows to filter by workflow namespace. Namespace *ListFilter `q:"-"` // SortDirs allows to select sort direction. // It can be "asc" or "desc" (default). SortDirs string `q:"sort_dirs"` // SortKeys allows to sort by one of the cron trigger attributes. SortKeys string `q:"sort_keys"` // 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"` // ProjectID allows to filter by given project id. Admin required. ProjectID string `q:"project_id"` // AllProjects requests to get executions of all projects. Admin required. AllProjects int `q:"all_projects"` } // ListFilter allows to filter string parameters with different filters. // Empty value for Filter checks for equality. type ListFilter struct { Filter FilterType Value string } func (l ListFilter) String() string { if l.Filter != "" { return fmt.Sprintf("%s:%s", l.Filter, l.Value) } return l.Value } // ListDateFilter allows to filter date parameters with different filters. // Empty value for Filter checks for equality. type ListDateFilter struct { Filter FilterType Value time.Time } func (l ListDateFilter) String() string { v := l.Value.Format(gophercloud.RFC3339ZNoTNoZ) if l.Filter != "" { return fmt.Sprintf("%s:%s", l.Filter, v) } return v } // FilterType represents a valid filter to use for filtering executions. type FilterType string const ( // FilterEQ checks equality. FilterEQ = "eq" // FilterNEQ checks non equality. FilterNEQ = "neq" // FilterIN checks for belonging in a list, comma separated. FilterIN = "in" // FilterNIN checks for values that does not belong from a list, comma separated. FilterNIN = "nin" // FilterGT checks for values strictly greater. FilterGT = "gt" // FilterGTE checks for values greater or equal. FilterGTE = "gte" // FilterLT checks for values strictly lower. FilterLT = "lt" // FilterLTE checks for values lower or equal. FilterLTE = "lte" // FilterHas checks for values that contains the requested parameter. FilterHas = "has" ) // ToWorkflowListQuery formats a ListOpts into a query string. func (opts ListOpts) ToWorkflowListQuery() (string, error) { q, err := gophercloud.BuildQueryString(opts) if err != nil { return "", err } params := q.Query() if opts.Tags != nil { params.Add("tags", strings.Join(opts.Tags, ",")) } for queryParam, value := range map[string]fmt.Stringer{ "created_at": opts.CreatedAt, "updated_at": opts.UpdatedAt, "name": opts.Name, "definition": opts.Definition, "namespace": opts.Namespace, } { if !reflect.ValueOf(value).IsNil() { params.Add(queryParam, value.String()) } } q = &url.URL{RawQuery: params.Encode()} return q.String(), nil } // 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}} }) } golang-github-gophercloud-gophercloud-0.12.0/openstack/workflow/v2/workflows/results.go000066400000000000000000000065701367513235700314440ustar00rootroot00000000000000package 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 } golang-github-gophercloud-gophercloud-0.12.0/openstack/workflow/v2/workflows/testing/000077500000000000000000000000001367513235700310615ustar00rootroot00000000000000requests_test.go000066400000000000000000000176471367513235700342620ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/openstack/workflow/v2/workflows/testingpackage testing import ( "fmt" "net/http" "net/url" "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' workflow_echo: description: Simple workflow example type: direct input: - msg tasks: test: action: std.echo output="<% $.msg %>"` 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": "2018-09-12 15:48:17", "definition": "---\nversion: '2.0'\n\nworkflow_echo:\n description: Simple workflow example\n type: direct\n\n input:\n - msg\n\n tasks:\n test:\n action: std.echo output=\"<%% $.msg %%>\"", "id": "604a3a1e-94e3-4066-a34a-aa56873ef236", "input": "msg", "name": "workflow_echo", "namespace": "some-namespace", "project_id": "778c0f25df0d492a9a868ee9e2fbb513", "scope": "private", "tags": [], "updated_at": "2018-09-12 15:48:17" } ] }`) }) 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(2018, time.September, 12, 15, 48, 17, 0, time.UTC) expected := []workflows.Workflow{ workflows.Workflow{ ID: "604a3a1e-94e3-4066-a34a-aa56873ef236", Definition: "---\nversion: '2.0'\n\nworkflow_echo:\n description: Simple workflow example\n type: direct\n\n input:\n - msg\n\n tasks:\n test:\n action: std.echo output=\"<% $.msg %>\"", Name: "workflow_echo", Namespace: "some-namespace", Input: "msg", ProjectID: "778c0f25df0d492a9a868ee9e2fbb513", Scope: "private", Tags: []string{}, CreatedAt: time.Date(2018, time.September, 12, 15, 48, 17, 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/604a3a1e-94e3-4066-a34a-aa56873ef236", 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(), "604a3a1e-94e3-4066-a34a-aa56873ef236") 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": "2018-09-12 15:48:17", "definition": "---\nversion: '2.0'\n\nworkflow_echo:\n description: Simple workflow example\n type: direct\n\n input:\n - msg\n\n tasks:\n test:\n action: std.echo output=\"<%% $.msg %%>\"", "id": "604a3a1e-94e3-4066-a34a-aa56873ef236", "input": "msg", "name": "workflow_echo", "namespace": "some-namespace", "project_id": "778c0f25df0d492a9a868ee9e2fbb513", "scope": "private", "tags": [], "updated_at": "2018-09-12 15:48:17" } `) }) actual, err := workflows.Get(fake.ServiceClient(), "1").Extract() if err != nil { t.Fatalf("Unable to get workflow: %v", err) } updated := time.Date(2018, time.September, 12, 15, 48, 17, 0, time.UTC) expected := &workflows.Workflow{ ID: "604a3a1e-94e3-4066-a34a-aa56873ef236", Definition: "---\nversion: '2.0'\n\nworkflow_echo:\n description: Simple workflow example\n type: direct\n\n input:\n - msg\n\n tasks:\n test:\n action: std.echo output=\"<% $.msg %>\"", Name: "workflow_echo", Namespace: "some-namespace", Input: "msg", ProjectID: "778c0f25df0d492a9a868ee9e2fbb513", Scope: "private", Tags: []string{}, CreatedAt: time.Date(2018, time.September, 12, 15, 48, 17, 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=604a3a1e-94e3-4066-a34a-aa56873ef236", "workflows": [ { "created_at": "2018-09-12 15:48:17", "definition": "---\nversion: '2.0'\n\nworkflow_echo:\n description: Simple workflow example\n type: direct\n\n input:\n - msg\n\n tasks:\n test:\n action: std.echo output=\"<%% $.msg %%>\"", "id": "604a3a1e-94e3-4066-a34a-aa56873ef236", "input": "msg", "name": "workflow_echo", "namespace": "some-namespace", "project_id": "778c0f25df0d492a9a868ee9e2fbb513", "scope": "private", "tags": [], "updated_at": "2018-09-12 15:48:17" } ] }`, th.Server.URL) case "604a3a1e-94e3-4066-a34a-aa56873ef236": 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(2018, time.September, 12, 15, 48, 17, 0, time.UTC) expected := []workflows.Workflow{ workflows.Workflow{ ID: "604a3a1e-94e3-4066-a34a-aa56873ef236", Definition: "---\nversion: '2.0'\n\nworkflow_echo:\n description: Simple workflow example\n type: direct\n\n input:\n - msg\n\n tasks:\n test:\n action: std.echo output=\"<% $.msg %>\"", Name: "workflow_echo", Namespace: "some-namespace", Input: "msg", ProjectID: "778c0f25df0d492a9a868ee9e2fbb513", Scope: "private", Tags: []string{}, CreatedAt: time.Date(2018, time.September, 12, 15, 48, 17, 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) } } func TestToWorkflowListQuery(t *testing.T) { for expected, opts := range map[string]*workflows.ListOpts{ newValue("tags", `tag1,tag2`): &workflows.ListOpts{ Tags: []string{"tag1", "tag2"}, }, newValue("name", `neq:invalid_name`): &workflows.ListOpts{ Name: &workflows.ListFilter{ Filter: workflows.FilterNEQ, Value: "invalid_name", }, }, newValue("created_at", `gt:2018-01-01 00:00:00`): &workflows.ListOpts{ CreatedAt: &workflows.ListDateFilter{ Filter: workflows.FilterGT, Value: time.Date(2018, time.January, 1, 0, 0, 0, 0, time.UTC), }, }, } { actual, _ := opts.ToWorkflowListQuery() th.AssertEquals(t, expected, actual) } } func newValue(param, value string) string { v := url.Values{} v.Add(param, value) return "?" + v.Encode() } golang-github-gophercloud-gophercloud-0.12.0/openstack/workflow/v2/workflows/urls.go000066400000000000000000000007611367513235700307240ustar00rootroot00000000000000package 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.12.0/pagination/000077500000000000000000000000001367513235700233105ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/pagination/http.go000066400000000000000000000030741367513235700246220ustar00rootroot00000000000000package 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}, KeepResponseBody: true, }) } golang-github-gophercloud-gophercloud-0.12.0/pagination/linked.go000066400000000000000000000047421367513235700251140ustar00rootroot00000000000000package 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.12.0/pagination/marker.go000066400000000000000000000027301367513235700251220ustar00rootroot00000000000000package 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.12.0/pagination/pager.go000066400000000000000000000167171367513235700247510ustar00rootroot00000000000000package 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.12.0/pagination/pkg.go000066400000000000000000000002261367513235700244200ustar00rootroot00000000000000/* Package pagination contains utilities and convenience structs that implement common pagination idioms within OpenStack APIs. */ package pagination golang-github-gophercloud-gophercloud-0.12.0/pagination/single.go000066400000000000000000000016261367513235700251250ustar00rootroot00000000000000package 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.12.0/pagination/testing/000077500000000000000000000000001367513235700247655ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/pagination/testing/doc.go000066400000000000000000000000361367513235700260600ustar00rootroot00000000000000// pagination package testing golang-github-gophercloud-gophercloud-0.12.0/pagination/testing/linked_test.go000066400000000000000000000053461367513235700276310ustar00rootroot00000000000000package 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.12.0/pagination/testing/marker_test.go000066400000000000000000000056651367513235700276500ustar00rootroot00000000000000package 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.12.0/pagination/testing/pagination_test.go000066400000000000000000000004711367513235700305060ustar00rootroot00000000000000package 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.12.0/pagination/testing/single_test.go000066400000000000000000000034621367513235700276410ustar00rootroot00000000000000package 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.12.0/params.go000066400000000000000000000324121367513235700227730ustar00rootroot00000000000000package 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.Slice || (v.Kind() == reflect.Ptr && v.Elem().Kind() == reflect.Slice) { sliceValue := v if sliceValue.Kind() == reflect.Ptr { sliceValue = sliceValue.Elem() } for i := 0; i < sliceValue.Len(); i++ { element := sliceValue.Index(i) if element.Kind() == reflect.Struct || (element.Kind() == reflect.Ptr && element.Elem().Kind() == reflect.Struct) { _, err := BuildRequestBody(element.Interface(), "") if err != nil { return nil, err } } } } 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.Int64: 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.12.0/provider_client.go000066400000000000000000000415611367513235700247050ustar00rootroot00000000000000package gophercloud import ( "bytes" "context" "encoding/json" "errors" "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 // Throwaway determines whether if this client is a throw-away client. It's a copy of user's provider client // with the token and reauth func zeroed. Such client can be used to perform reauthorization. Throwaway bool // Context is the context passed to the HTTP request. Context context.Context // mut is a mutex for the client. It protects read and write access to client attributes such as getting // and setting the TokenID. mut *sync.RWMutex // reauthmut is a mutex for reauthentication it attempts to ensure that only one reauthentication // attempt happens at one time. reauthmut *reauthlock authResult AuthResult } // reauthlock represents a set of attributes used to help in the reauthentication process. type reauthlock struct { sync.RWMutex ongoing *reauthFuture } // reauthFuture represents future result of the reauthentication process. // while done channel is not closed, reauthentication is in progress. // when done channel is closed, err contains the result of reauthentication. type reauthFuture struct { done chan struct{} err error } func newReauthFuture() *reauthFuture { return &reauthFuture{ make(chan struct{}), nil, } } func (f *reauthFuture) Set(err error) { f.err = err close(f.done) } func (f *reauthFuture) Get() error { <-f.done return f.err } // AuthenticatedHeaders returns a map of HTTP headers that are common for all // authenticated service requests. Blocks if Reauthenticate is in progress. func (client *ProviderClient) AuthenticatedHeaders() (m map[string]string) { if client.IsThrowaway() { return } if client.reauthmut != nil { // If a Reauthenticate is in progress, wait for it to complete. client.reauthmut.Lock() ongoing := client.reauthmut.ongoing client.reauthmut.Unlock() if ongoing != nil { _ = ongoing.Get() } } 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) } // GetAuthResult returns the result from the request that was used to obtain a // provider client's Keystone token. // // The result is nil when authentication has not yet taken place, when the token // was set manually with SetToken(), or when a ReauthFunc was used that does not // record the AuthResult. func (client *ProviderClient) GetAuthResult() AuthResult { if client.mut != nil { client.mut.RLock() defer client.mut.RUnlock() } return client.authResult } // 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. // // WARNING: This function is deprecated. Use SetTokenAndAuthResult() instead. func (client *ProviderClient) SetToken(t string) { if client.mut != nil { client.mut.Lock() defer client.mut.Unlock() } client.TokenID = t client.authResult = nil } // SetTokenAndAuthResult safely sets the value of the auth token in the // ProviderClient and also records the AuthResult that was returned from the // token creation request. Applications may call this in a custom ReauthFunc. func (client *ProviderClient) SetTokenAndAuthResult(r AuthResult) error { tokenID := "" var err error if r != nil { tokenID, err = r.ExtractTokenID() if err != nil { return err } } if client.mut != nil { client.mut.Lock() defer client.mut.Unlock() } client.TokenID = tokenID client.authResult = r return nil } // CopyTokenFrom safely copies the token from another ProviderClient into the // this one. func (client *ProviderClient) CopyTokenFrom(other *ProviderClient) { if client.mut != nil { client.mut.Lock() defer client.mut.Unlock() } if other.mut != nil && other.mut != client.mut { other.mut.RLock() defer other.mut.RUnlock() } client.TokenID = other.TokenID client.authResult = other.authResult } // IsThrowaway safely reads the value of the client Throwaway field. func (client *ProviderClient) IsThrowaway() bool { if client.reauthmut != nil { client.reauthmut.RLock() defer client.reauthmut.RUnlock() } return client.Throwaway } // SetThrowaway safely sets the value of the client Throwaway field. func (client *ProviderClient) SetThrowaway(v bool) { if client.reauthmut != nil { client.reauthmut.Lock() defer client.reauthmut.Unlock() } client.Throwaway = v } // 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) error { if client.ReauthFunc == nil { return nil } if client.reauthmut == nil { return client.ReauthFunc() } future := newReauthFuture() // Check if a Reauthenticate is in progress, or start one if not. client.reauthmut.Lock() ongoing := client.reauthmut.ongoing if ongoing == nil { client.reauthmut.ongoing = future } client.reauthmut.Unlock() // If Reauthenticate is running elsewhere, wait for its result. if ongoing != nil { return ongoing.Get() } // Perform the actual reauthentication. var err error if previousToken == "" || client.TokenID == previousToken { err = client.ReauthFunc() } else { err = nil } // Mark Reauthenticate as finished. client.reauthmut.Lock() client.reauthmut.ongoing.Set(err) client.reauthmut.ongoing = nil client.reauthmut.Unlock() return err } // 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 // KeepResponseBody specifies whether to keep the HTTP response body. Usually used, when the HTTP // response body is considered for further use. Valid when JSONResponse is nil. KeepResponseBody bool } // requestState contains temporary state for a single ProviderClient.Request() call. type requestState struct { // This flag indicates if we have reauthenticated during this request because of a 401 response. // It ensures that we don't reauthenticate multiple times for a single request. If we // reauthenticate, but keep getting 401 responses with the fresh token, reauthenticating some more // will just get us into an infinite loop. hasReauthenticated bool } 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) { return client.doRequest(method, url, options, &requestState{ hasReauthenticated: false, }) } func (client *ProviderClient) doRequest(method, url string, options *RequestOpts, state *requestState) (*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 { return nil, errors.New("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 } // Return an error, when "KeepResponseBody" is true and "JSONResponse" is not nil if options.KeepResponseBody && options.JSONResponse != nil { return nil, errors.New("cannot use KeepResponseBody when JSONResponse is not nil") } if options.RawBody != nil { body = options.RawBody } // Construct the http.Request. req, err := http.NewRequest(method, url, body) if err != nil { return nil, err } if client.Context != nil { req = req.WithContext(client.Context) } // 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) } 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 okc := options.OkCodes if okc == nil { okc = defaultOkCodes(method) } // Validate the HTTP response status. var ok bool for _, code := range okc { 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, ResponseHeader: resp.Header, } 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 && !state.hasReauthenticated { 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) } } state.hasReauthenticated = true resp, err = client.doRequest(method, url, options, state) 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 http.StatusConflict: err = ErrDefault409{respErr} if error409er, ok := errType.(Err409er); ok { err = error409er.Error409(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() // Don't decode JSON when there is no content if resp.StatusCode == http.StatusNoContent { // read till EOF, otherwise the connection will be closed and cannot be reused _, err = io.Copy(ioutil.Discard, resp.Body) return resp, err } if err := json.NewDecoder(resp.Body).Decode(options.JSONResponse); err != nil { return nil, err } } // Close unused body to allow the HTTP connection to be reused if !options.KeepResponseBody && options.JSONResponse == nil { defer resp.Body.Close() // read till EOF, otherwise the connection will be closed and cannot be reused if _, err := io.Copy(ioutil.Discard, resp.Body); err != nil { return nil, err } } return resp, nil } func defaultOkCodes(method string) []int { switch method { case "GET", "HEAD": return []int{200} case "POST": return []int{201, 202} case "PUT": return []int{201, 202} case "PATCH": return []int{200, 202, 204} case "DELETE": return []int{202, 204} } return []int{} } golang-github-gophercloud-gophercloud-0.12.0/results.go000066400000000000000000000266101367513235700232140ustar00rootroot00000000000000package 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) if mSlice, ok := m[label].([]interface{}); ok { for _, v := range mSlice { // 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) // jtopjian: This was put into place to resolve the issue // described at // https://github.com/gophercloud/gophercloud/issues/1963 // // This probably isn't the best fix, but it appears to // be resolving the issue, so I'm going to implement it // for now. // // For future readers, this entire case statement could // use a review. return nil } } 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.12.0/script/000077500000000000000000000000001367513235700224635ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/script/acceptancetest000077500000000000000000000066161367513235700254100ustar00rootroot00000000000000#!/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. # list generated by: # find acceptance/openstack -name '*_test.go' -exec dirname {} \; | sort -n | uniq -c | awk '{print $2}' ACCEPTANCE_TESTS=( acceptance/openstack # IronicEndpoint is required # acceptance/openstack/baremetal/noauth # No suitable endpoint could be found in the service catalog # acceptance/openstack/baremetal/v1 acceptance/openstack/blockstorage acceptance/openstack/blockstorage/extensions # snapshots_test.go:16: Unable to create a blockstorage client: CinderEndpoint is required # acceptance/openstack/blockstorage/noauth # snapshots_test.go:21: Unable to retrieve snapshots: Resource not found # acceptance/openstack/blockstorage/v1 acceptance/openstack/blockstorage/v2 acceptance/openstack/blockstorage/v3 # No suitable endpoint could be found in the service catalog. # acceptance/openstack/clustering/v1 acceptance/openstack/compute/v2 # No suitable endpoint could be found in the service catalog. # acceptance/openstack/containerinfra/v1 acceptance/openstack/container/v1 # Unable to create a DB client: No suitable endpoint could be found in the service catalog. # acceptance/openstack/db/v1 acceptance/openstack/dns/v2 acceptance/openstack/identity/v2 acceptance/openstack/identity/v3 acceptance/openstack/imageservice/v2 acceptance/openstack/keymanager/v1 acceptance/openstack/loadbalancer/v2 # No suitable endpoint could be found in the service catalog. # acceptance/openstack/messaging/v2 acceptance/openstack/networking/v2 acceptance/openstack/networking/v2/extensions acceptance/openstack/networking/v2/extensions/agents acceptance/openstack/networking/v2/extensions/dns # Resource not found # acceptance/openstack/networking/v2/extensions/fwaas # acceptance/openstack/networking/v2/extensions/fwaas_v2 acceptance/openstack/networking/v2/extensions/layer3 # Unable to create: Resource not found # acceptance/openstack/networking/v2/extensions/lbaas # Resource not found # acceptance/openstack/networking/v2/extensions/lbaas_v2 acceptance/openstack/networking/v2/extensions/mtu acceptance/openstack/networking/v2/extensions/networkipavailabilities acceptance/openstack/networking/v2/extensions/portsbinding acceptance/openstack/networking/v2/extensions/qos/policies acceptance/openstack/networking/v2/extensions/qos/rules acceptance/openstack/networking/v2/extensions/qos/ruletypes acceptance/openstack/networking/v2/extensions/quotas acceptance/openstack/networking/v2/extensions/rbacpolicies acceptance/openstack/networking/v2/extensions/subnetpools acceptance/openstack/networking/v2/extensions/trunks acceptance/openstack/networking/v2/extensions/vlantransparent # Unable to create: Resource not found # acceptance/openstack/networking/v2/extensions/vpnaas acceptance/openstack/objectstorage/v1 acceptance/openstack/orchestration/v1 acceptance/openstack/placement/v1 acceptance/openstack/sharedfilesystems/v2 acceptance/openstack/sharedfilesystems/v2/messages # No suitable endpoint could be found in the service catalog # acceptance/openstack/workflow/v2 ) for acceptance_test in ${ACCEPTANCE_TESTS[@]}; do go test -v -timeout $timeout -tags "fixtures acceptance" ./${acceptance_test} if [[ $? != 0 ]]; then failed=1 fi done # If any of the test suites failed, exit 1 if [[ -n $failed ]]; then exit 1 fi exit 0 golang-github-gophercloud-gophercloud-0.12.0/script/acceptancetest-ironic000077500000000000000000000010051367513235700266540ustar00rootroot00000000000000#!/bin/bash # set -x source `dirname $0`/stackenv timeout="60m" failed= # Run the Ironic acceptance tests. # Check the error code after each suite, but do not exit early if a suite failed. ACCEPTANCE_TESTS=( acceptance/openstack/baremetal/v1 ) for acceptance_test in ${ACCEPTANCE_TESTS[@]}; do go test -v -timeout $timeout -tags "fixtures acceptance" ./${acceptance_test} if [[ $? != 0 ]]; then failed=1 fi done # If any of the test suites failed, exit 1 if [[ -n $failed ]]; then exit 1 fi exit 0 golang-github-gophercloud-gophercloud-0.12.0/script/bootstrap000077500000000000000000000011611367513235700244250ustar00rootroot00000000000000#!/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.12.0/script/cibuild000077500000000000000000000001161367513235700240220ustar00rootroot00000000000000#!/bin/bash # # Test script to be invoked by Travis. exec script/unittest -v golang-github-gophercloud-gophercloud-0.12.0/script/coverage000077500000000000000000000004521367513235700242050ustar00rootroot00000000000000#!/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.12.0/script/format000077500000000000000000000017621367513235700237070ustar00rootroot00000000000000#!/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.12.0/script/stackenv000066400000000000000000000023301367513235700242220ustar00rootroot00000000000000# 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) _SUBNET_ID=$(openstack subnet show private-subnet -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_SUBNET_ID="$_SUBNET_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_DOMAIN_ID=default >> openrc source openrc admin admin popd golang-github-gophercloud-gophercloud-0.12.0/script/test000077500000000000000000000001261367513235700233670ustar00rootroot00000000000000#!/bin/bash # # Run all the tests. exec go test -tags 'acceptance fixtures' ./... $@ golang-github-gophercloud-gophercloud-0.12.0/script/unittest000077500000000000000000000003131367513235700242650ustar00rootroot00000000000000#!/bin/bash # # Run the unit tests. # Do extra rounds of testing to help identify reauth concurrency issues. # All other packages are tested in the `coverage` tests. go test -v -race -count=5 ./testing golang-github-gophercloud-gophercloud-0.12.0/service_client.go000066400000000000000000000125131367513235700245060ustar00rootroot00000000000000package 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 case "baremetal": opts.MoreHeaders["X-OpenStack-Ironic-API-Version"] = client.Microversion case "baremetal-introspection": opts.MoreHeaders["X-OpenStack-Ironic-Inspector-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) } // ParseResponse is a helper function to parse http.Response to constituents. func ParseResponse(resp *http.Response, err error) (io.ReadCloser, http.Header, error) { if resp != nil { return resp.Body, resp.Header, err } return nil, nil, err } golang-github-gophercloud-gophercloud-0.12.0/testhelper/000077500000000000000000000000001367513235700233365ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/testhelper/client/000077500000000000000000000000001367513235700246145ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/testhelper/client/fake.go000066400000000000000000000007061367513235700260540ustar00rootroot00000000000000package 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.12.0/testhelper/convenience.go000066400000000000000000000237731367513235700261750ustar00rootroot00000000000000package 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()))) } } // AssertErr is a convenience function for checking whether an error value is // nil func AssertErr(t *testing.T, e error) { if e == nil { logFatal(t, fmt.Sprintf("expected error, got nil")) } } // 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.12.0/testhelper/doc.go000066400000000000000000000001461367513235700244330ustar00rootroot00000000000000/* Package testhelper container methods that are useful for writing unit tests. */ package testhelper golang-github-gophercloud-gophercloud-0.12.0/testhelper/fixture/000077500000000000000000000000001367513235700250245ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/testhelper/fixture/helper.go000066400000000000000000000012321367513235700266300ustar00rootroot00000000000000package 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.12.0/testhelper/http_responses.go000066400000000000000000000053511367513235700267510ustar00rootroot00000000000000package testhelper import ( "encoding/json" "fmt" "io/ioutil" "net" "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 listening specific port. func SetupPersistentPortHTTP(t *testing.T, port int) { l, err := net.Listen("tcp", fmt.Sprintf("127.0.0.1:%d", port)) if err != nil { t.Errorf("Failed to listen to 127.0.0.1:%d: %s", port, err) } Mux = http.NewServeMux() Server = httptest.NewUnstartedServer(Mux) Server.Listener = l Server.Start() } // 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.12.0/testing/000077500000000000000000000000001367513235700226345ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.12.0/testing/doc.go000066400000000000000000000000371367513235700237300ustar00rootroot00000000000000// gophercloud package testing golang-github-gophercloud-gophercloud-0.12.0/testing/endpoint_search_test.go000066400000000000000000000012131367513235700273640ustar00rootroot00000000000000package 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.12.0/testing/errors_test.go000066400000000000000000000011311367513235700255320ustar00rootroot00000000000000package testing import ( "testing" "github.com/gophercloud/gophercloud" th "github.com/gophercloud/gophercloud/testhelper" ) func TestGetResponseCode(t *testing.T) { respErr := gophercloud.ErrUnexpectedResponseCode{ URL: "http://example.com", Method: "GET", Expected: []int{200}, Actual: 404, Body: nil, ResponseHeader: nil, } var err404 error = gophercloud.ErrDefault404{ErrUnexpectedResponseCode: respErr} err, ok := err404.(gophercloud.StatusCodeError) th.AssertEquals(t, true, ok) th.AssertEquals(t, err.GetStatusCode(), 404) } golang-github-gophercloud-gophercloud-0.12.0/testing/params_test.go000066400000000000000000000156231367513235700255140ustar00rootroot00000000000000package 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"` ContentLength int64 `h:"Content-Length"` Num int `h:"Number" required:"true"` Style bool `h:"Style"` }{ Accept: "application/json", ContentLength: 256, Num: 4, Style: true, } expected := map[string]string{"Accept": "application/json", "Number": "4", "Style": "true", "Content-Length": "256"} 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.12.0/testing/provider_client_test.go000066400000000000000000000252451367513235700274220ustar00rootroot00000000000000package testing import ( "context" "fmt" "io/ioutil" "net" "net/http" "net/http/httptest" "strings" "sync" "sync/atomic" "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 failedAuths int mut *sync.RWMutex }{ 0, 0, new(sync.RWMutex), } numconc := 20 prereauthTok := client.TokenID postreauthTok := "12345678" p := new(gophercloud.ProviderClient) p.UseTokenLock() p.SetToken(prereauthTok) p.ReauthFunc = func() error { p.SetThrowaway(true) time.Sleep(1 * time.Second) p.AuthenticatedHeaders() info.mut.Lock() info.numreauths++ info.mut.Unlock() p.TokenID = postreauthTok p.SetThrowaway(false) 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) info.mut.Lock() info.failedAuths++ info.mut.Unlock() 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.KeepResponseBody = true 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) { var info = struct { reauthAttempts int maxReauthReached bool mut *sync.RWMutex }{ 0, false, new(sync.RWMutex), } numconc := 20 mut := new(sync.RWMutex) p := new(gophercloud.ProviderClient) p.UseTokenLock() p.SetToken(client.TokenID) p.ReauthFunc = func() error { info.mut.Lock() defer info.mut.Unlock() if info.reauthAttempts > 5 { info.maxReauthReached = true return fmt.Errorf("Max reauthentication attempts reached") } p.SetThrowaway(true) p.AuthenticatedHeaders() p.SetThrowaway(false) info.reauthAttempts++ 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) // counters for the upcoming errors errAfter := 0 errUnable := 0 wg := new(sync.WaitGroup) for i := 0; i < numconc; i++ { wg.Add(1) go func() { defer wg.Done() _, err := p.Request("GET", fmt.Sprintf("%s/route", th.Endpoint()), reqopts) mut.Lock() defer mut.Unlock() // ErrErrorAfter... will happen after a successful reauthentication, // but the service still responds with a 401. if _, ok := err.(*gophercloud.ErrErrorAfterReauthentication); ok { errAfter++ } // ErrErrorUnable... will happen when the custom reauth func reports // an error. if _, ok := err.(*gophercloud.ErrUnableToReauthenticate); ok { errUnable++ } }() } wg.Wait() th.AssertEquals(t, info.reauthAttempts, 6) th.AssertEquals(t, info.maxReauthReached, true) th.AssertEquals(t, errAfter > 1, true) th.AssertEquals(t, errUnable < 20, true) } func TestRequestThatCameDuringReauthWaitsUntilItIsCompleted(t *testing.T) { var info = struct { numreauths int failedAuths int reauthCh chan struct{} mut *sync.RWMutex }{ 0, 0, make(chan struct{}), new(sync.RWMutex), } numconc := 20 prereauthTok := client.TokenID postreauthTok := "12345678" p := new(gophercloud.ProviderClient) p.UseTokenLock() p.SetToken(prereauthTok) p.ReauthFunc = func() error { info.mut.RLock() if info.numreauths == 0 { info.mut.RUnlock() close(info.reauthCh) time.Sleep(1 * time.Second) } else { info.mut.RUnlock() } p.SetThrowaway(true) p.AuthenticatedHeaders() info.mut.Lock() info.numreauths++ info.mut.Unlock() p.TokenID = postreauthTok p.SetThrowaway(false) 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 { info.mut.Lock() info.failedAuths++ info.mut.Unlock() 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.KeepResponseBody = true reqopts.MoreHeaders = map[string]string{ "X-Auth-Token": prereauthTok, } for i := 0; i < numconc; i++ { wg.Add(1) go func(i int) { defer wg.Done() if i != 0 { <-info.reauthCh } 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) }(i) } wg.Wait() th.AssertEquals(t, 1, info.numreauths) th.AssertEquals(t, 1, info.failedAuths) } func TestRequestReauthsAtMostOnce(t *testing.T) { // There was an issue where Gophercloud would go into an infinite // reauthentication loop with buggy services that send 401 even for fresh // tokens. This test simulates such a service and checks that a call to // ProviderClient.Request() will not try to reauthenticate more than once. reauthCounter := 0 var reauthCounterMutex sync.Mutex p := new(gophercloud.ProviderClient) p.UseTokenLock() p.SetToken(client.TokenID) p.ReauthFunc = func() error { reauthCounterMutex.Lock() reauthCounter++ reauthCounterMutex.Unlock() //The actual token value does not matter, the endpoint does not check it. return nil } th.SetupHTTP() defer th.TeardownHTTP() requestCounter := 0 var requestCounterMutex sync.Mutex th.Mux.HandleFunc("/route", func(w http.ResponseWriter, r *http.Request) { requestCounterMutex.Lock() requestCounter++ //avoid infinite loop if requestCounter == 10 { http.Error(w, "too many requests", http.StatusTooManyRequests) return } requestCounterMutex.Unlock() //always reply 401, even immediately after reauthenticate http.Error(w, "unauthorized", http.StatusUnauthorized) }) // The expected error message indicates that we reauthenticated once (that's // the part before the colon), but when encountering another 401 response, we // did not attempt reauthentication again and just passed that 401 response to // the caller as ErrDefault401. _, err := p.Request("GET", th.Endpoint()+"/route", &gophercloud.RequestOpts{}) expectedErrorMessage := "Successfully re-authenticated, but got error executing request: Authentication failed" th.AssertEquals(t, expectedErrorMessage, err.Error()) } func TestRequestWithContext(t *testing.T) { ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { fmt.Fprintln(w, "OK") })) defer ts.Close() ctx, cancel := context.WithCancel(context.Background()) p := &gophercloud.ProviderClient{Context: ctx} res, err := p.Request("GET", ts.URL, &gophercloud.RequestOpts{KeepResponseBody: true}) th.AssertNoErr(t, err) _, err = ioutil.ReadAll(res.Body) th.AssertNoErr(t, err) err = res.Body.Close() th.AssertNoErr(t, err) cancel() res, err = p.Request("GET", ts.URL, &gophercloud.RequestOpts{}) if err == nil { t.Fatal("expecting error, got nil") } if !strings.Contains(err.Error(), ctx.Err().Error()) { t.Fatalf("expecting error to contain: %q, got %q", ctx.Err().Error(), err.Error()) } } func TestRequestConnectionReuse(t *testing.T) { ts := httptest.NewUnstartedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { fmt.Fprintln(w, "OK") })) // an amount of iterations var iter = 10000 // connections tracks an amount of connections made var connections int64 ts.Config.ConnState = func(_ net.Conn, s http.ConnState) { // track an amount of connections if s == http.StateNew { atomic.AddInt64(&connections, 1) } } ts.Start() defer ts.Close() p := &gophercloud.ProviderClient{} for i := 0; i < iter; i++ { _, err := p.Request("GET", ts.URL, &gophercloud.RequestOpts{KeepResponseBody: false}) th.AssertNoErr(t, err) } th.AssertEquals(t, int64(1), connections) } func TestRequestConnectionClose(t *testing.T) { ts := httptest.NewUnstartedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { fmt.Fprintln(w, "OK") })) // an amount of iterations var iter = 10 // connections tracks an amount of connections made var connections int64 ts.Config.ConnState = func(_ net.Conn, s http.ConnState) { // track an amount of connections if s == http.StateNew { atomic.AddInt64(&connections, 1) } } ts.Start() defer ts.Close() p := &gophercloud.ProviderClient{} for i := 0; i < iter; i++ { _, err := p.Request("GET", ts.URL, &gophercloud.RequestOpts{KeepResponseBody: true}) th.AssertNoErr(t, err) } th.AssertEquals(t, int64(iter), connections) } golang-github-gophercloud-gophercloud-0.12.0/testing/results_test.go000066400000000000000000000106241367513235700257260ustar00rootroot00000000000000package 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.12.0/testing/service_client_test.go000066400000000000000000000015741367513235700272270ustar00rootroot00000000000000package 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.12.0/testing/util_test.go000066400000000000000000000070561367513235700252070ustar00rootroot00000000000000package 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.12.0/util.go000066400000000000000000000052601367513235700224660ustar00rootroot00000000000000package 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 }