provider/clc: vendor deps, update to match latest SDK API
This commit is contained in:
parent
7775cc8ccc
commit
c4b23223ab
|
@ -150,6 +150,51 @@
|
||||||
"Comment": "v1.2-315-g1cb9dff",
|
"Comment": "v1.2-315-g1cb9dff",
|
||||||
"Rev": "1cb9dff8c37b2918ad1ebd7b294d01100a153d27"
|
"Rev": "1cb9dff8c37b2918ad1ebd7b294d01100a153d27"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "github.com/CenturyLinkCloud/clc-sdk",
|
||||||
|
"Comment": "0.0.2-17-gb0c6106",
|
||||||
|
"Rev": "b0c610652394ae6cac087e1e5d6de187c5f3f258"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "github.com/CenturyLinkCloud/clc-sdk/aa",
|
||||||
|
"Comment": "0.0.2-17-gb0c6106",
|
||||||
|
"Rev": "b0c610652394ae6cac087e1e5d6de187c5f3f258"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "github.com/CenturyLinkCloud/clc-sdk/alert",
|
||||||
|
"Comment": "0.0.2-17-gb0c6106",
|
||||||
|
"Rev": "b0c610652394ae6cac087e1e5d6de187c5f3f258"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "github.com/CenturyLinkCloud/clc-sdk/api",
|
||||||
|
"Comment": "0.0.2-17-gb0c6106",
|
||||||
|
"Rev": "b0c610652394ae6cac087e1e5d6de187c5f3f258"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "github.com/CenturyLinkCloud/clc-sdk/dc",
|
||||||
|
"Comment": "0.0.2-17-gb0c6106",
|
||||||
|
"Rev": "b0c610652394ae6cac087e1e5d6de187c5f3f258"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "github.com/CenturyLinkCloud/clc-sdk/group",
|
||||||
|
"Comment": "0.0.2-17-gb0c6106",
|
||||||
|
"Rev": "b0c610652394ae6cac087e1e5d6de187c5f3f258"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "github.com/CenturyLinkCloud/clc-sdk/lb",
|
||||||
|
"Comment": "0.0.2-17-gb0c6106",
|
||||||
|
"Rev": "b0c610652394ae6cac087e1e5d6de187c5f3f258"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "github.com/CenturyLinkCloud/clc-sdk/server",
|
||||||
|
"Comment": "0.0.2-17-gb0c6106",
|
||||||
|
"Rev": "b0c610652394ae6cac087e1e5d6de187c5f3f258"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "github.com/CenturyLinkCloud/clc-sdk/status",
|
||||||
|
"Comment": "0.0.2-17-gb0c6106",
|
||||||
|
"Rev": "b0c610652394ae6cac087e1e5d6de187c5f3f258"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/DreamItGetIT/statuscake",
|
"ImportPath": "github.com/DreamItGetIT/statuscake",
|
||||||
"Rev": "8cbe86575f00210a6df2c19cb2f59b00cd181de3"
|
"Rev": "8cbe86575f00210a6df2c19cb2f59b00cd181de3"
|
||||||
|
@ -814,6 +859,10 @@
|
||||||
"ImportPath": "github.com/mattn/go-isatty",
|
"ImportPath": "github.com/mattn/go-isatty",
|
||||||
"Rev": "56b76bdf51f7708750eac80fa38b952bb9f32639"
|
"Rev": "56b76bdf51f7708750eac80fa38b952bb9f32639"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "github.com/mikebeyer/env",
|
||||||
|
"Rev": "b6ce30ccdcad3ed9d5ca80329829f5aeaa856e99"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/mitchellh/cli",
|
"ImportPath": "github.com/mitchellh/cli",
|
||||||
"Rev": "cb6853d606ea4a12a15ac83cc43503df99fd28fb"
|
"Rev": "cb6853d606ea4a12a15ac83cc43503df99fd28fb"
|
||||||
|
|
|
@ -3,6 +3,7 @@ package clc
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
|
"net/url"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
clc "github.com/CenturyLinkCloud/clc-sdk"
|
clc "github.com/CenturyLinkCloud/clc-sdk"
|
||||||
|
@ -59,18 +60,21 @@ func Provider() terraform.ResourceProvider {
|
||||||
func providerConfigure(d *schema.ResourceData) (interface{}, error) {
|
func providerConfigure(d *schema.ResourceData) (interface{}, error) {
|
||||||
un := d.Get("username").(string)
|
un := d.Get("username").(string)
|
||||||
pw := d.Get("password").(string)
|
pw := d.Get("password").(string)
|
||||||
ac := d.Get("account").(string)
|
|
||||||
url := d.Get("url").(string)
|
|
||||||
|
|
||||||
config, config_err := api.NewConfig(un, pw, ac, url)
|
config, err := api.NewConfig(un, pw)
|
||||||
if config_err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("Failed to create CLC config with provided details: %v", config_err)
|
return nil, fmt.Errorf("Failed to create CLC config with provided details: %v", err)
|
||||||
}
|
}
|
||||||
config.UserAgent = "terraform-clc"
|
if urlStr := d.Get("url").(string); urlStr != "" {
|
||||||
|
uri, err := url.Parse(urlStr)
|
||||||
|
if err == nil {
|
||||||
|
config.BaseURL = uri
|
||||||
|
}
|
||||||
|
}
|
||||||
|
config.UserAgent = fmt.Sprintf("terraform-clc terraform/%s", terraform.Version)
|
||||||
|
|
||||||
client := clc.New(config)
|
client := clc.New(config)
|
||||||
err := client.Authenticate()
|
if err := client.Authenticate(); err != nil {
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("Failed authenticated with provided credentials: %v", err)
|
return nil, fmt.Errorf("Failed authenticated with provided credentials: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -137,12 +137,16 @@ func resourceCLCGroupUpdate(d *schema.ResourceData, meta interface{}) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("Failed updating group %v: %v", id, err)
|
return fmt.Errorf("Failed updating group %v: %v", id, err)
|
||||||
}
|
}
|
||||||
return resource.Retry(1*time.Minute, func() error {
|
return resource.Retry(1*time.Minute, func() *resource.RetryError {
|
||||||
_, err := client.Group.Get(id)
|
_, err := client.Group.Get(id)
|
||||||
if err == nil {
|
if err != nil {
|
||||||
return resourceCLCGroupRead(d, meta)
|
return resource.RetryableError(err)
|
||||||
}
|
}
|
||||||
return &resource.RetryError{Err: err}
|
err = resourceCLCGroupRead(d, meta)
|
||||||
|
if err != nil {
|
||||||
|
return resource.NonRetryableError(err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -62,12 +62,16 @@ func resourceCLCLoadBalancerCreate(d *schema.ResourceData, meta interface{}) err
|
||||||
return fmt.Errorf("Failed creating load balancer under %v/%v: %v", dc, name, err)
|
return fmt.Errorf("Failed creating load balancer under %v/%v: %v", dc, name, err)
|
||||||
}
|
}
|
||||||
d.SetId(l.ID)
|
d.SetId(l.ID)
|
||||||
return resource.Retry(1*time.Minute, func() error {
|
return resource.Retry(1*time.Minute, func() *resource.RetryError {
|
||||||
_, err := client.LB.Get(dc, l.ID)
|
_, err := client.LB.Get(dc, l.ID)
|
||||||
if err == nil {
|
if err != nil {
|
||||||
return resourceCLCLoadBalancerRead(d, meta)
|
return resource.RetryableError(err)
|
||||||
}
|
}
|
||||||
return &resource.RetryError{Err: err}
|
err = resourceCLCLoadBalancerRead(d, meta)
|
||||||
|
if err != nil {
|
||||||
|
return resource.NonRetryableError(err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
service_name: travis-ci
|
|
@ -0,0 +1,3 @@
|
||||||
|
*.swp
|
||||||
|
*.cov
|
||||||
|
Godeps/_workspace
|
|
@ -0,0 +1,8 @@
|
||||||
|
language: go
|
||||||
|
|
||||||
|
go:
|
||||||
|
- 1.5
|
||||||
|
|
||||||
|
install: make deps
|
||||||
|
script:
|
||||||
|
- make test
|
|
@ -0,0 +1,190 @@
|
||||||
|
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
|
||||||
|
|
||||||
|
Copyright 2015 Mike Beyer
|
||||||
|
|
||||||
|
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.
|
|
@ -0,0 +1,12 @@
|
||||||
|
VERSION=0.1
|
||||||
|
|
||||||
|
.PHONY : test cover deps
|
||||||
|
test:
|
||||||
|
godep go test ./...
|
||||||
|
cover:
|
||||||
|
./cover.sh
|
||||||
|
deps:
|
||||||
|
go get github.com/tools/godep
|
||||||
|
go get golang.org/x/tools/cmd/goimports
|
||||||
|
go get github.com/mattn/goveralls
|
||||||
|
godep restore
|
|
@ -0,0 +1,89 @@
|
||||||
|
CLC SDK (for go!) [![Build Status](https://travis-ci.org/CenturyLinkCloud/clc-sdk.svg?branch=master)](https://travis-ci.org/CenturyLinkCloud/clc-sdk) [![Coverage Status](https://coveralls.io/repos/mikebeyer/clc-sdk/badge.svg?branch=master&service=github)](https://coveralls.io/github/mikebeyer/clc-sdk?branch=master)
|
||||||
|
======
|
||||||
|
|
||||||
|
Installation
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
```sh
|
||||||
|
$ go get github.com/CenturyLinkCloud/clc-sdk
|
||||||
|
$ make deps
|
||||||
|
$ make test
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
Configuration
|
||||||
|
-------
|
||||||
|
The SDK supports the following helpers for creating your configuration
|
||||||
|
|
||||||
|
|
||||||
|
Reading from the environment
|
||||||
|
|
||||||
|
```go
|
||||||
|
config, _ := api.EnvConfig()
|
||||||
|
```
|
||||||
|
|
||||||
|
Reading from a file
|
||||||
|
|
||||||
|
|
||||||
|
```go
|
||||||
|
config, _ := api.FileConfig("./config.json")
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
Direct configuration
|
||||||
|
|
||||||
|
```go
|
||||||
|
config, _ := api.NewConfig(un, pwd)
|
||||||
|
// defaults:
|
||||||
|
config.Alias = "" // resolved on Authentication
|
||||||
|
config.UserAgent = "CenturyLinkCloud/clc-sdk"
|
||||||
|
config.BaseURI = "https://api.ctl.io/v2"
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
Enable http wire tracing with env var `DEBUG=on`.
|
||||||
|
|
||||||
|
Additionally, callers of the SDK should set `config.UserAgent` to identify to platform appropriately.
|
||||||
|
|
||||||
|
|
||||||
|
Examples
|
||||||
|
-------
|
||||||
|
To create a new server
|
||||||
|
|
||||||
|
```go
|
||||||
|
client := clc.New(api.EnvConfig())
|
||||||
|
|
||||||
|
server := server.Server{
|
||||||
|
Name: "server",
|
||||||
|
CPU: 1,
|
||||||
|
MemoryGB: 1,
|
||||||
|
GroupID: "GROUP-ID",
|
||||||
|
SourceServerID: "UBUNTU-14-64-TEMPLATE",
|
||||||
|
Type: "standard",
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, _ := client.Server.Create(server)
|
||||||
|
```
|
||||||
|
|
||||||
|
Check status of a server build
|
||||||
|
|
||||||
|
```go
|
||||||
|
resp, _ := client.Server.Create(server)
|
||||||
|
|
||||||
|
status, _ := client.Status.Get(resp.GetStatusID())
|
||||||
|
```
|
||||||
|
|
||||||
|
Async polling for complection
|
||||||
|
|
||||||
|
```go
|
||||||
|
resp, _ := client.Server.Create(server)
|
||||||
|
|
||||||
|
poll := make(chan *status.Response, 1)
|
||||||
|
service.Status.Poll(resp.GetStatusID(), poll)
|
||||||
|
|
||||||
|
status := <- poll
|
||||||
|
```
|
||||||
|
|
||||||
|
License
|
||||||
|
-------
|
||||||
|
This project is licensed under the [Apache License v2.0](http://www.apache.org/licenses/LICENSE-2.0.html).
|
|
@ -0,0 +1,67 @@
|
||||||
|
package aa
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/CenturyLinkCloud/clc-sdk/api"
|
||||||
|
)
|
||||||
|
|
||||||
|
func New(client api.HTTP) *Service {
|
||||||
|
return &Service{
|
||||||
|
client: client,
|
||||||
|
config: client.Config(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type Service struct {
|
||||||
|
client api.HTTP
|
||||||
|
config *api.Config
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) Get(id string) (*Policy, error) {
|
||||||
|
url := fmt.Sprintf("%s/antiAffinityPolicies/%s/%s", s.config.BaseURL, s.config.Alias, id)
|
||||||
|
policy := &Policy{}
|
||||||
|
err := s.client.Get(url, policy)
|
||||||
|
return policy, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) GetAll() (*Policies, error) {
|
||||||
|
url := fmt.Sprintf("%s/antiAffinityPolicies/%s", s.config.BaseURL, s.config.Alias)
|
||||||
|
policies := &Policies{}
|
||||||
|
err := s.client.Get(url, policies)
|
||||||
|
return policies, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) Create(name, location string) (*Policy, error) {
|
||||||
|
policy := &Policy{Name: name, Location: location}
|
||||||
|
resp := &Policy{}
|
||||||
|
url := fmt.Sprintf("%s/antiAffinityPolicies/%s", s.config.BaseURL, s.config.Alias)
|
||||||
|
err := s.client.Post(url, policy, resp)
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) Update(id string, name string) (*Policy, error) {
|
||||||
|
policy := &Policy{Name: name}
|
||||||
|
resp := &Policy{}
|
||||||
|
url := fmt.Sprintf("%s/antiAffinityPolicies/%s/%s", s.config.BaseURL, s.config.Alias, id)
|
||||||
|
err := s.client.Put(url, policy, resp)
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) Delete(id string) error {
|
||||||
|
url := fmt.Sprintf("%s/antiAffinityPolicies/%s/%s", s.config.BaseURL, s.config.Alias, id)
|
||||||
|
err := s.client.Delete(url, nil)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
type Policy struct {
|
||||||
|
ID string `json:"id,omitempty"`
|
||||||
|
Name string `json:"name,omitempty"`
|
||||||
|
Location string `json:"location,omitempty"`
|
||||||
|
Links api.Links `json:"links,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Policies struct {
|
||||||
|
Items []Policy `json:"items,omitempty"`
|
||||||
|
Links api.Links `json:"links,omitempty"`
|
||||||
|
}
|
|
@ -0,0 +1,80 @@
|
||||||
|
package alert
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/CenturyLinkCloud/clc-sdk/api"
|
||||||
|
)
|
||||||
|
|
||||||
|
func New(client api.HTTP) *Service {
|
||||||
|
return &Service{
|
||||||
|
client: client,
|
||||||
|
config: client.Config(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type Service struct {
|
||||||
|
client api.HTTP
|
||||||
|
config *api.Config
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) Get(id string) (*Alert, error) {
|
||||||
|
url := fmt.Sprintf("%s/alertPolicies/%s/%s", s.config.BaseURL, s.config.Alias, id)
|
||||||
|
resp := &Alert{}
|
||||||
|
err := s.client.Get(url, resp)
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) GetAll() (*Alerts, error) {
|
||||||
|
url := fmt.Sprintf("%s/alertPolicies/%s", s.config.BaseURL, s.config.Alias)
|
||||||
|
resp := &Alerts{}
|
||||||
|
err := s.client.Get(url, resp)
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) Create(alert Alert) (*Alert, error) {
|
||||||
|
url := fmt.Sprintf("%s/alertPolicies/%s", s.config.BaseURL, s.config.Alias)
|
||||||
|
resp := &Alert{}
|
||||||
|
err := s.client.Post(url, alert, resp)
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) Update(id string, alert Alert) (*Alert, error) {
|
||||||
|
url := fmt.Sprintf("%s/alertPolicies/%s/%s", s.config.BaseURL, s.config.Alias, id)
|
||||||
|
resp := &Alert{}
|
||||||
|
err := s.client.Put(url, alert, resp)
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) Delete(id string) error {
|
||||||
|
url := fmt.Sprintf("%s/alertPolicies/%s/%s", s.config.BaseURL, s.config.Alias, id)
|
||||||
|
return s.client.Delete(url, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
type Alerts struct {
|
||||||
|
Items []Alert `json:"items"`
|
||||||
|
Links api.Links `json:"links"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Alert struct {
|
||||||
|
ID string `json:"id,omitempty"`
|
||||||
|
Name string `json:"name,omitempty"`
|
||||||
|
Actions []Action `json:"actions,omitempty"`
|
||||||
|
Triggers []Trigger `json:"triggers,omitempty"`
|
||||||
|
Links api.Links `json:"links,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Action struct {
|
||||||
|
Action string `json:"action"`
|
||||||
|
Setting Setting `json:"settings"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Setting struct {
|
||||||
|
Recipients []string `json:"recipients"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Trigger struct {
|
||||||
|
Metric string `json:"metric"`
|
||||||
|
Duration string `json:"duration"`
|
||||||
|
Threshold float64 `json:"threshold"`
|
||||||
|
}
|
|
@ -0,0 +1,235 @@
|
||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httputil"
|
||||||
|
"net/url"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/mikebeyer/env"
|
||||||
|
)
|
||||||
|
|
||||||
|
var debug = os.Getenv("DEBUG") != ""
|
||||||
|
|
||||||
|
const baseUriDefault = "https://api.ctl.io/v2"
|
||||||
|
const userAgentDefault = "CenturyLinkCloud/clc-sdk"
|
||||||
|
|
||||||
|
func New(config Config) *Client {
|
||||||
|
return &Client{
|
||||||
|
config: config,
|
||||||
|
client: http.DefaultClient,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type HTTP interface {
|
||||||
|
Get(url string, resp interface{}) error
|
||||||
|
Post(url string, body, resp interface{}) error
|
||||||
|
Put(url string, body, resp interface{}) error
|
||||||
|
Patch(url string, body, resp interface{}) error
|
||||||
|
Delete(url string, resp interface{}) error
|
||||||
|
Config() *Config
|
||||||
|
}
|
||||||
|
|
||||||
|
type Client struct {
|
||||||
|
config Config
|
||||||
|
Token Token
|
||||||
|
|
||||||
|
client *http.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) Config() *Config {
|
||||||
|
return &c.config
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) Get(url string, resp interface{}) error {
|
||||||
|
return c.DoWithAuth("GET", url, nil, resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) Post(url string, body, resp interface{}) error {
|
||||||
|
return c.DoWithAuth("POST", url, body, resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) Put(url string, body, resp interface{}) error {
|
||||||
|
return c.DoWithAuth("PUT", url, body, resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) Patch(url string, body, resp interface{}) error {
|
||||||
|
return c.DoWithAuth("PATCH", url, body, resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) Delete(url string, resp interface{}) error {
|
||||||
|
return c.DoWithAuth("DELETE", url, nil, resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) Auth() error {
|
||||||
|
url := fmt.Sprintf("%s/authentication/login", c.config.BaseURL)
|
||||||
|
body, err := c.serialize(c.config.User)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
req, err := http.NewRequest("POST", url, body)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = c.Do(req, &c.Token)
|
||||||
|
if err == nil {
|
||||||
|
// set Alias from returned token
|
||||||
|
c.config.Alias = c.Token.Alias
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) Do(req *http.Request, ret interface{}) error {
|
||||||
|
if debug {
|
||||||
|
v, _ := httputil.DumpRequest(req, true)
|
||||||
|
log.Println(string(v))
|
||||||
|
}
|
||||||
|
|
||||||
|
req.Header.Add("User-Agent", c.config.UserAgent)
|
||||||
|
req.Header.Add("Accept", "application/json")
|
||||||
|
if req.Body != nil {
|
||||||
|
req.Header.Add("Content-Type", "application/json")
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := c.client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if debug && resp != nil {
|
||||||
|
v, _ := httputil.DumpResponse(resp, true)
|
||||||
|
log.Println(string(v))
|
||||||
|
}
|
||||||
|
if resp.StatusCode >= 400 {
|
||||||
|
b, _ := ioutil.ReadAll(resp.Body)
|
||||||
|
return fmt.Errorf("http err: [%s] - %s", resp.Status, b)
|
||||||
|
}
|
||||||
|
|
||||||
|
if ret == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME? empty body: check status=204 or content-length=0 before parsing
|
||||||
|
return json.NewDecoder(resp.Body).Decode(ret)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) DoWithAuth(method, url string, body, ret interface{}) error {
|
||||||
|
if !c.Token.Valid() {
|
||||||
|
err := c.Auth()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
b, err := c.serialize(body)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
req, err := http.NewRequest(method, url, b)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
req.Header.Add("Authorization", "Bearer "+c.Token.Token)
|
||||||
|
|
||||||
|
return c.Do(req, ret)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) serialize(body interface{}) (io.Reader, error) {
|
||||||
|
if body == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
b := new(bytes.Buffer)
|
||||||
|
err := json.NewEncoder(b).Encode(body)
|
||||||
|
return b, err
|
||||||
|
}
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
User User `json:"user"`
|
||||||
|
Alias string `json:"alias"`
|
||||||
|
BaseURL *url.URL `json:"-"`
|
||||||
|
UserAgent string `json:"agent,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c Config) Valid() bool {
|
||||||
|
return c.User.Username != "" && c.User.Password != "" && c.BaseURL != nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func EnvConfig() (Config, error) {
|
||||||
|
user := os.Getenv("CLC_USERNAME")
|
||||||
|
pass := os.Getenv("CLC_PASSWORD")
|
||||||
|
config, err := NewConfig(user, pass)
|
||||||
|
if err != nil {
|
||||||
|
return config, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !config.Valid() {
|
||||||
|
return config, fmt.Errorf("missing environment variables [%s]", config)
|
||||||
|
}
|
||||||
|
return config, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewConfig takes credentials and returns a Config object that may be further customized.
|
||||||
|
// Defaults for Alias, BaseURL, and UserAgent will be taken from respective env vars.
|
||||||
|
func NewConfig(username, password string) (Config, error) {
|
||||||
|
alias := os.Getenv("CLC_ALIAS")
|
||||||
|
agent := env.String("CLC_USER_AGENT", userAgentDefault)
|
||||||
|
base := env.String("CLC_BASE_URL", baseUriDefault)
|
||||||
|
uri, err := url.Parse(base)
|
||||||
|
return Config{
|
||||||
|
User: User{
|
||||||
|
Username: username,
|
||||||
|
Password: password,
|
||||||
|
},
|
||||||
|
Alias: alias,
|
||||||
|
BaseURL: uri,
|
||||||
|
UserAgent: agent,
|
||||||
|
}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func FileConfig(file string) (Config, error) {
|
||||||
|
config := Config{}
|
||||||
|
b, err := ioutil.ReadFile(file)
|
||||||
|
if err != nil {
|
||||||
|
return config, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = json.Unmarshal(b, &config)
|
||||||
|
|
||||||
|
u, err := url.Parse(baseUriDefault)
|
||||||
|
config.BaseURL = u
|
||||||
|
return config, err
|
||||||
|
}
|
||||||
|
|
||||||
|
type User struct {
|
||||||
|
Username string `json:"username"`
|
||||||
|
Password string `json:"password"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Token struct {
|
||||||
|
Username string `json:"userName"`
|
||||||
|
Alias string `json:"accountAlias"`
|
||||||
|
Location string `json:"locationAlias"`
|
||||||
|
Roles []string `json:"roles"`
|
||||||
|
Token string `json:"bearerToken"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Add some real validation logic
|
||||||
|
func (t Token) Valid() bool {
|
||||||
|
return t.Token != ""
|
||||||
|
}
|
||||||
|
|
||||||
|
type Update struct {
|
||||||
|
Op string `json:"op"`
|
||||||
|
Member string `json:"member"`
|
||||||
|
Value interface{} `json:"value"`
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
package api
|
||||||
|
|
||||||
|
type Link struct {
|
||||||
|
Rel string `json:"rel,omitempty"`
|
||||||
|
Href string `json:"href,omitempty"`
|
||||||
|
ID string `json:"id,omitempty"`
|
||||||
|
Name string `json:"name,omitempty"`
|
||||||
|
Verbs []string `json:"verbs,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Links []Link
|
||||||
|
|
||||||
|
func (l Links) GetID(rel string) (bool, string) {
|
||||||
|
if ok, v := l.GetLink(rel); ok {
|
||||||
|
return true, (*v).ID
|
||||||
|
}
|
||||||
|
return false, ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l Links) GetLink(rel string) (bool, *Link) {
|
||||||
|
for _, v := range l {
|
||||||
|
if v.Rel == rel {
|
||||||
|
return true, &v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type Customfields struct {
|
||||||
|
ID string `json:"id,omitempty"`
|
||||||
|
Name string `json:"name,omitempty"`
|
||||||
|
Value string `json:"value,omitempty"`
|
||||||
|
Displayvalue string `json:"displayValue,omitempty"`
|
||||||
|
}
|
|
@ -0,0 +1,49 @@
|
||||||
|
package clc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/CenturyLinkCloud/clc-sdk/aa"
|
||||||
|
"github.com/CenturyLinkCloud/clc-sdk/alert"
|
||||||
|
"github.com/CenturyLinkCloud/clc-sdk/api"
|
||||||
|
"github.com/CenturyLinkCloud/clc-sdk/dc"
|
||||||
|
"github.com/CenturyLinkCloud/clc-sdk/group"
|
||||||
|
"github.com/CenturyLinkCloud/clc-sdk/lb"
|
||||||
|
"github.com/CenturyLinkCloud/clc-sdk/server"
|
||||||
|
"github.com/CenturyLinkCloud/clc-sdk/status"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Client struct {
|
||||||
|
client *api.Client
|
||||||
|
|
||||||
|
Server *server.Service
|
||||||
|
Status *status.Service
|
||||||
|
AA *aa.Service
|
||||||
|
Alert *alert.Service
|
||||||
|
LB *lb.Service
|
||||||
|
Group *group.Service
|
||||||
|
DC *dc.Service
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(config api.Config) *Client {
|
||||||
|
c := &Client{
|
||||||
|
client: api.New(config),
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Server = server.New(c.client)
|
||||||
|
c.Status = status.New(c.client)
|
||||||
|
c.AA = aa.New(c.client)
|
||||||
|
c.Alert = alert.New(c.client)
|
||||||
|
c.LB = lb.New(c.client)
|
||||||
|
c.Group = group.New(c.client)
|
||||||
|
c.DC = dc.New(c.client)
|
||||||
|
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) Alias(alias string) *Client {
|
||||||
|
c.client.Config().Alias = alias
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) Authenticate() error {
|
||||||
|
return c.client.Auth()
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
#!/bin/bash
|
||||||
|
set -e
|
||||||
|
# Automatic checks
|
||||||
|
test -z "$(gofmt -l -w . | tee /dev/stderr)"
|
||||||
|
test -z "$(goimports -l -w . | tee /dev/stderr)"
|
||||||
|
godep go test -race ./...
|
||||||
|
|
||||||
|
# Run test coverage on each subdirectories and merge the coverage profile.
|
||||||
|
|
||||||
|
echo "mode: count" > profile.cov
|
||||||
|
|
||||||
|
# Standard go tooling behavior is to ignore dirs with leading underscors
|
||||||
|
for dir in $(find . -maxdepth 10 -not -path './.git*' -not -path '*/_*' -type d);
|
||||||
|
do
|
||||||
|
if ls $dir/*.go &> /dev/null; then
|
||||||
|
godep go test -covermode=count -coverprofile=$dir/profile.tmp $dir
|
||||||
|
if [ -f $dir/profile.tmp ]
|
||||||
|
then
|
||||||
|
cat $dir/profile.tmp | tail -n +2 >> profile.cov
|
||||||
|
rm $dir/profile.tmp
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
go tool cover -func profile.cov
|
||||||
|
|
||||||
|
goveralls -coverprofile=profile.cov -service=travis-ci -repotoken $COVERALLS_REPO_TOKEN
|
|
@ -0,0 +1,65 @@
|
||||||
|
package dc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/CenturyLinkCloud/clc-sdk/api"
|
||||||
|
)
|
||||||
|
|
||||||
|
func New(client api.HTTP) *Service {
|
||||||
|
return &Service{
|
||||||
|
client: client,
|
||||||
|
config: client.Config(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type Service struct {
|
||||||
|
client api.HTTP
|
||||||
|
config *api.Config
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) Get(id string) (*Response, error) {
|
||||||
|
url := fmt.Sprintf("%s/datacenters/%s/%s?groupLinks=true", s.config.BaseURL, s.config.Alias, id)
|
||||||
|
dc := &Response{}
|
||||||
|
err := s.client.Get(url, dc)
|
||||||
|
return dc, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) GetAll() ([]*Response, error) {
|
||||||
|
url := fmt.Sprintf("%s/datacenters/%s", s.config.BaseURL, s.config.Alias)
|
||||||
|
dcs := make([]*Response, 0)
|
||||||
|
err := s.client.Get(url, &dcs)
|
||||||
|
return dcs, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) GetCapabilities(id string) (*CapabilitiesResponse, error) {
|
||||||
|
url := fmt.Sprintf("%s/datacenters/%s/%s/deploymentCapabilities", s.config.BaseURL, s.config.Alias, id)
|
||||||
|
c := &CapabilitiesResponse{}
|
||||||
|
err := s.client.Get(url, c)
|
||||||
|
return c, err
|
||||||
|
}
|
||||||
|
|
||||||
|
type Response struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Links api.Links `json:"links"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type CapabilitiesResponse struct {
|
||||||
|
SupportsPremiumStorage bool `json:"supportsPremiumStorage"`
|
||||||
|
SupportsBareMetalServers bool `json:"supportsBareMetalServers"`
|
||||||
|
SupportsSharedLoadBalancer bool `json:"supportsSharedLoadBalancer"`
|
||||||
|
Templates []struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
StorageSizeGB string `json:"storageSizeGB"`
|
||||||
|
Capabilities []string `json:"capabilities"`
|
||||||
|
ReservedDrivePaths []string `json:"reservedDrivePaths"`
|
||||||
|
} `json:"templates"`
|
||||||
|
DeployableNetworks []struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
NetworkId string `json:"networkId"`
|
||||||
|
Type string `json:"type"`
|
||||||
|
AccountID string `json:"accountID"`
|
||||||
|
} `json:deployableNetworks`
|
||||||
|
}
|
|
@ -0,0 +1,187 @@
|
||||||
|
package group
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/CenturyLinkCloud/clc-sdk/api"
|
||||||
|
"github.com/CenturyLinkCloud/clc-sdk/status"
|
||||||
|
)
|
||||||
|
|
||||||
|
func New(client api.HTTP) *Service {
|
||||||
|
return &Service{
|
||||||
|
client: client,
|
||||||
|
config: client.Config(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type Service struct {
|
||||||
|
client api.HTTP
|
||||||
|
config *api.Config
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) Get(id string) (*Response, error) {
|
||||||
|
url := fmt.Sprintf("%s/groups/%s/%s", s.config.BaseURL, s.config.Alias, id)
|
||||||
|
resp := &Response{}
|
||||||
|
err := s.client.Get(url, resp)
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) Create(group Group) (*Response, error) {
|
||||||
|
resp := &Response{}
|
||||||
|
url := fmt.Sprintf("%s/groups/%s", s.config.BaseURL, s.config.Alias)
|
||||||
|
err := s.client.Post(url, group, resp)
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) Update(id string, updates ...api.Update) error {
|
||||||
|
url := fmt.Sprintf("%s/groups/%s/%s", s.config.BaseURL, s.config.Alias, id)
|
||||||
|
return s.client.Patch(url, updates, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) Delete(id string) (*status.Status, error) {
|
||||||
|
url := fmt.Sprintf("%s/groups/%s/%s", s.config.BaseURL, s.config.Alias, id)
|
||||||
|
resp := &status.Status{}
|
||||||
|
err := s.client.Delete(url, resp)
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) Archive(id string) (*status.Status, error) {
|
||||||
|
url := fmt.Sprintf("%s/groups/%s/%s/archive", s.config.BaseURL, s.config.Alias, id)
|
||||||
|
resp := &status.Status{}
|
||||||
|
err := s.client.Post(url, "", resp)
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) Restore(id, intoGroup string) (*status.QueuedResponse, error) {
|
||||||
|
url := fmt.Sprintf("%s/groups/%s/%s/restore", s.config.BaseURL, s.config.Alias, id)
|
||||||
|
resp := &status.QueuedResponse{}
|
||||||
|
body := fmt.Sprintf(`{"targetGroupId": "%v"}`, intoGroup)
|
||||||
|
err := s.client.Post(url, body, resp)
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) SetDefaults(id string, defaults *GroupDefaults) error {
|
||||||
|
url := fmt.Sprintf("%s/groups/%s/%s/defaults", s.config.BaseURL, s.config.Alias, id)
|
||||||
|
var resp interface{}
|
||||||
|
err := s.client.Post(url, defaults, resp)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) SetHorizontalAutoscalePolicy(id string, policy *HorizontalAutoscalePolicy) (*interface{}, error) {
|
||||||
|
url := fmt.Sprintf("%s/groups/%s/%s/horizontalAutoscalePolicy", s.config.BaseURL, s.config.Alias, id)
|
||||||
|
var resp interface{}
|
||||||
|
err := s.client.Put(url, policy, resp)
|
||||||
|
return &resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func UpdateName(name string) api.Update {
|
||||||
|
return api.Update{
|
||||||
|
Op: "set",
|
||||||
|
Member: "name",
|
||||||
|
Value: name,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func UpdateDescription(desc string) api.Update {
|
||||||
|
return api.Update{
|
||||||
|
Op: "set",
|
||||||
|
Member: "description",
|
||||||
|
Value: desc,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func UpdateParentGroupID(id string) api.Update {
|
||||||
|
return api.Update{
|
||||||
|
Op: "set",
|
||||||
|
Member: "parentGroupId",
|
||||||
|
Value: id,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func UpdateCustomfields(fields []api.Customfields) api.Update {
|
||||||
|
return api.Update{
|
||||||
|
Op: "set",
|
||||||
|
Member: "customFields",
|
||||||
|
Value: fields,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// request body for creating groups
|
||||||
|
type Group struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Description string `json:"description,omitempty"`
|
||||||
|
ParentGroupID string `json:"parentGroupId"`
|
||||||
|
CustomFields []api.Customfields `json:"customFields,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// response body for group get
|
||||||
|
type Response struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
Locationid string `json:"locationId"`
|
||||||
|
Type string `json:"type"`
|
||||||
|
Status string `json:"status"`
|
||||||
|
Links api.Links `json:"links"`
|
||||||
|
Groups []Groups `json:"groups"`
|
||||||
|
Changeinfo struct {
|
||||||
|
Createddate time.Time `json:"createdDate"`
|
||||||
|
Createdby string `json:"createdBy"`
|
||||||
|
Modifieddate time.Time `json:"modifiedDate"`
|
||||||
|
Modifiedby string `json:"modifiedBy"`
|
||||||
|
} `json:"changeInfo"`
|
||||||
|
Customfields []api.Customfields `json:"customFields"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Response) ParentGroupID() string {
|
||||||
|
if ok, link := r.Links.GetLink("parentGroup"); ok {
|
||||||
|
return link.ID
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Response) Servers() []string {
|
||||||
|
ids := make([]string, 0)
|
||||||
|
for _, l := range r.Links {
|
||||||
|
if l.Rel == "server" {
|
||||||
|
ids = append(ids, l.ID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ids
|
||||||
|
}
|
||||||
|
|
||||||
|
// nested groups under response
|
||||||
|
type Groups struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
Locationid string `json:"locationId"`
|
||||||
|
Type string `json:"type"`
|
||||||
|
Status string `json:"status"`
|
||||||
|
Serverscount int `json:"serversCount"`
|
||||||
|
Groups []Groups `json:"groups"`
|
||||||
|
Links api.Links `json:"links"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// request body for /v2/groups/ALIAS/ID/defaults
|
||||||
|
type GroupDefaults struct {
|
||||||
|
CPU string `json:"cpu,omitempty"`
|
||||||
|
MemoryGB string `json:"memoryGB,omitempty"`
|
||||||
|
NetworkID string `json:"networkId,omitempty"`
|
||||||
|
primaryDns string `json:"primaryDns,omitempty"`
|
||||||
|
secondaryDns string `json:"secondaryDns,omitempty"`
|
||||||
|
templateName string `json:"templateName,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// request body for /v2/groups/ALIAS/ID/horizontalAutoscalePolicy
|
||||||
|
type HorizontalAutoscalePolicy struct {
|
||||||
|
PolicyId string `json:"policyId,omitempty"`
|
||||||
|
LoadBalancerPool []PoolPolicy `json:"loadBalancerPool,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type PoolPolicy struct {
|
||||||
|
ID string `json:"id,omitempty"`
|
||||||
|
PrivatePort int `json:"privatePort,omitempty"`
|
||||||
|
PublicPort int `json:"publicPort,omitempty"`
|
||||||
|
}
|
|
@ -0,0 +1,135 @@
|
||||||
|
package lb
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/CenturyLinkCloud/clc-sdk/api"
|
||||||
|
)
|
||||||
|
|
||||||
|
func New(client api.HTTP) *Service {
|
||||||
|
return &Service{
|
||||||
|
client: client,
|
||||||
|
config: client.Config(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type Service struct {
|
||||||
|
client api.HTTP
|
||||||
|
config *api.Config
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) Get(dc, id string) (*LoadBalancer, error) {
|
||||||
|
url := fmt.Sprintf("%s/sharedLoadBalancers/%s/%s/%s", s.config.BaseURL, s.config.Alias, dc, id)
|
||||||
|
resp := &LoadBalancer{}
|
||||||
|
err := s.client.Get(url, resp)
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) GetAll(dc string) ([]*LoadBalancer, error) {
|
||||||
|
url := fmt.Sprintf("%s/sharedLoadBalancers/%s/%s", s.config.BaseURL, s.config.Alias, dc)
|
||||||
|
resp := make([]*LoadBalancer, 0)
|
||||||
|
err := s.client.Get(url, &resp)
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) Create(dc string, lb LoadBalancer) (*LoadBalancer, error) {
|
||||||
|
url := fmt.Sprintf("%s/sharedLoadBalancers/%s/%s", s.config.BaseURL, s.config.Alias, dc)
|
||||||
|
resp := &LoadBalancer{}
|
||||||
|
err := s.client.Post(url, lb, resp)
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) Update(dc, id string, lb LoadBalancer) error {
|
||||||
|
url := fmt.Sprintf("%s/sharedLoadBalancers/%s/%s/%s", s.config.BaseURL, s.config.Alias, dc, id)
|
||||||
|
err := s.client.Put(url, lb, nil)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) Delete(dc, id string) error {
|
||||||
|
url := fmt.Sprintf("%s/sharedLoadBalancers/%s/%s/%s", s.config.BaseURL, s.config.Alias, dc, id)
|
||||||
|
return s.client.Delete(url, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) GetPool(dc, lb, pool string) (*Pool, error) {
|
||||||
|
url := fmt.Sprintf("%s/sharedLoadBalancers/%s/%s/%s/pools/%s", s.config.BaseURL, s.config.Alias, dc, lb, pool)
|
||||||
|
resp := &Pool{}
|
||||||
|
err := s.client.Get(url, resp)
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) GetAllPools(dc, lb string) ([]*Pool, error) {
|
||||||
|
url := fmt.Sprintf("%s/sharedLoadBalancers/%s/%s/%s/pools", s.config.BaseURL, s.config.Alias, dc, lb)
|
||||||
|
resp := make([]*Pool, 0)
|
||||||
|
err := s.client.Get(url, &resp)
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) CreatePool(dc, lb string, pool Pool) (*Pool, error) {
|
||||||
|
url := fmt.Sprintf("%s/sharedLoadBalancers/%s/%s/%s/pools", s.config.BaseURL, s.config.Alias, dc, lb)
|
||||||
|
resp := &Pool{}
|
||||||
|
err := s.client.Post(url, pool, resp)
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) UpdatePool(dc, lb, id string, pool Pool) error {
|
||||||
|
url := fmt.Sprintf("%s/sharedLoadBalancers/%s/%s/%s/pools/%s", s.config.BaseURL, s.config.Alias, dc, lb, id)
|
||||||
|
err := s.client.Put(url, pool, nil)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) DeletePool(dc, lb, pool string) error {
|
||||||
|
url := fmt.Sprintf("%s/sharedLoadBalancers/%s/%s/%s/pools/%s", s.config.BaseURL, s.config.Alias, dc, lb, pool)
|
||||||
|
return s.client.Delete(url, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) GetAllNodes(dc, lb, pool string) ([]*Node, error) {
|
||||||
|
url := fmt.Sprintf("%s/sharedLoadBalancers/%s/%s/%s/pools/%s/nodes", s.config.BaseURL, s.config.Alias, dc, lb, pool)
|
||||||
|
resp := make([]*Node, 0)
|
||||||
|
err := s.client.Get(url, &resp)
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) UpdateNodes(dc, lb, pool string, nodes ...Node) error {
|
||||||
|
url := fmt.Sprintf("%s/sharedLoadBalancers/%s/%s/%s/pools/%s/nodes", s.config.BaseURL, s.config.Alias, dc, lb, pool)
|
||||||
|
err := s.client.Put(url, nodes, nil)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
type LoadBalancer struct {
|
||||||
|
ID string `json:"id,omitempty"`
|
||||||
|
Name string `json:"name,omitempty"`
|
||||||
|
Description string `json:"description,omitempty"`
|
||||||
|
IPaddress string `json:"ipAddress,omitempty"`
|
||||||
|
Status string `json:"status,omitempty"`
|
||||||
|
Pools []Pool `json:"pools,omitempty"`
|
||||||
|
Links api.Links `json:"links,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Pool struct {
|
||||||
|
ID string `json:"id,omitempty"`
|
||||||
|
Port int `json:"port,omitempty"`
|
||||||
|
Method Method `json:"method"`
|
||||||
|
Persistence Persistence `json:"persistence"`
|
||||||
|
Nodes []Node `json:"nodes,omitempty"`
|
||||||
|
Links api.Links `json:"links,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Node struct {
|
||||||
|
Status string `json:"status,omitempty"`
|
||||||
|
IPaddress string `json:"ipAddress"`
|
||||||
|
PrivatePort int `json:"privatePort"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Persistence string
|
||||||
|
|
||||||
|
const (
|
||||||
|
Standard Persistence = "standard"
|
||||||
|
Sticky Persistence = "sticky"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Method string
|
||||||
|
|
||||||
|
const (
|
||||||
|
LeastConn Method = "leastConnection"
|
||||||
|
RoundRobin Method = "roundRobin"
|
||||||
|
)
|
|
@ -0,0 +1,388 @@
|
||||||
|
package server
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"regexp"
|
||||||
|
|
||||||
|
"github.com/CenturyLinkCloud/clc-sdk/api"
|
||||||
|
"github.com/CenturyLinkCloud/clc-sdk/status"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrInvalidServer = fmt.Errorf("server: server missing required field(s). (Name, CPU, MemoryGB, GroupID, SourceServerID, Type)")
|
||||||
|
)
|
||||||
|
|
||||||
|
func New(client api.HTTP) *Service {
|
||||||
|
return &Service{
|
||||||
|
client: client,
|
||||||
|
config: client.Config(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type Service struct {
|
||||||
|
client api.HTTP
|
||||||
|
config *api.Config
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) Get(name string) (*Response, error) {
|
||||||
|
url := fmt.Sprintf("%s/servers/%s/%s", s.config.BaseURL, s.config.Alias, name)
|
||||||
|
if regexp.MustCompile("^[0-9a-f]{32}$").MatchString(name) {
|
||||||
|
url = fmt.Sprintf("%s?uuid=true", url)
|
||||||
|
}
|
||||||
|
resp := &Response{}
|
||||||
|
err := s.client.Get(url, resp)
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) Create(server Server) (*status.QueuedResponse, error) {
|
||||||
|
if !server.Valid() {
|
||||||
|
return nil, ErrInvalidServer
|
||||||
|
}
|
||||||
|
|
||||||
|
resp := &status.QueuedResponse{}
|
||||||
|
url := fmt.Sprintf("%s/servers/%s", s.config.BaseURL, s.config.Alias)
|
||||||
|
err := s.client.Post(url, server, resp)
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) Update(name string, updates ...api.Update) (*status.Status, error) {
|
||||||
|
resp := &status.Status{}
|
||||||
|
url := fmt.Sprintf("%s/servers/%s/%s", s.config.BaseURL, s.config.Alias, name)
|
||||||
|
err := s.client.Patch(url, updates, resp)
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) Edit(name string, updates ...api.Update) error {
|
||||||
|
url := fmt.Sprintf("%s/servers/%s/%s", s.config.BaseURL, s.config.Alias, name)
|
||||||
|
err := s.client.Patch(url, updates, nil)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) Delete(name string) (*status.QueuedResponse, error) {
|
||||||
|
url := fmt.Sprintf("%s/servers/%s/%s", s.config.BaseURL, s.config.Alias, name)
|
||||||
|
resp := &status.QueuedResponse{}
|
||||||
|
err := s.client.Delete(url, resp)
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) GetCredentials(name string) (Credentials, error) {
|
||||||
|
url := fmt.Sprintf("%s/servers/%s/%s/credentials", s.config.BaseURL, s.config.Alias, name)
|
||||||
|
resp := Credentials{}
|
||||||
|
err := s.client.Get(url, &resp)
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
type Credentials struct {
|
||||||
|
Username string `json:"userName"`
|
||||||
|
Password string `json:"password"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) Archive(servers ...string) ([]*status.QueuedResponse, error) {
|
||||||
|
url := fmt.Sprintf("%s/operations/%s/servers/archive", s.config.BaseURL, s.config.Alias)
|
||||||
|
var resp []*status.QueuedResponse
|
||||||
|
err := s.client.Post(url, servers, &resp)
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) Restore(name, group string) (*status.Status, error) {
|
||||||
|
restore := map[string]string{"targetGroupId": group}
|
||||||
|
url := fmt.Sprintf("%s/servers/%s/%s/restore", s.config.BaseURL, s.config.Alias, name)
|
||||||
|
resp := &status.Status{}
|
||||||
|
err := s.client.Post(url, restore, resp)
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) CreateSnapshot(expiration int, servers ...string) ([]*status.QueuedResponse, error) {
|
||||||
|
snapshot := Snapshot{Expiration: expiration, Servers: servers}
|
||||||
|
url := fmt.Sprintf("%s/operations/%s/servers/createSnapshot", s.config.BaseURL, s.config.Alias)
|
||||||
|
var resp []*status.QueuedResponse
|
||||||
|
err := s.client.Post(url, snapshot, &resp)
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) DeleteSnapshot(server, id string) (*status.Status, error) {
|
||||||
|
url := fmt.Sprintf("%s/servers/%s/%s/snapshots/%s", s.config.BaseURL, s.config.Alias, server, id)
|
||||||
|
resp := &status.Status{}
|
||||||
|
err := s.client.Delete(url, resp)
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) RevertSnapshot(server, id string) (*status.Status, error) {
|
||||||
|
url := fmt.Sprintf("%s/servers/%s/%s/snapshots/%s/restore", s.config.BaseURL, s.config.Alias, server, id)
|
||||||
|
resp := &status.Status{}
|
||||||
|
err := s.client.Post(url, nil, resp)
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
type Snapshot struct {
|
||||||
|
Expiration int `json:"snapshotExpirationDays"`
|
||||||
|
Servers []string `json:"serverIds"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) ExecutePackage(pkg Package, servers ...string) ([]*status.QueuedResponse, error) {
|
||||||
|
url := fmt.Sprintf("%s/operations/%s/servers/executePackage", s.config.BaseURL, s.config.Alias)
|
||||||
|
var resp []*status.QueuedResponse
|
||||||
|
exec := executePackage{Servers: servers, Package: pkg}
|
||||||
|
err := s.client.Post(url, exec, &resp)
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
type executePackage struct {
|
||||||
|
Servers []string `json:"servers"`
|
||||||
|
Package Package `json:"package"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Package struct {
|
||||||
|
ID string `json:"packageId"`
|
||||||
|
Params map[string]string `json:"parameters"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) PowerState(state PowerState, servers ...string) ([]*status.QueuedResponse, error) {
|
||||||
|
url := fmt.Sprintf("%s/operations/%s/servers/%s", s.config.BaseURL, s.config.Alias, state)
|
||||||
|
var resp []*status.QueuedResponse
|
||||||
|
err := s.client.Post(url, servers, &resp)
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
type PowerState int
|
||||||
|
|
||||||
|
const (
|
||||||
|
On = iota
|
||||||
|
Off
|
||||||
|
Pause
|
||||||
|
Reboot
|
||||||
|
Reset
|
||||||
|
ShutDown
|
||||||
|
StartMaintenance
|
||||||
|
StopMaintenance
|
||||||
|
)
|
||||||
|
|
||||||
|
func (p PowerState) String() string {
|
||||||
|
switch p {
|
||||||
|
case On:
|
||||||
|
return "powerOn"
|
||||||
|
case Off:
|
||||||
|
return "powerOff"
|
||||||
|
case Pause:
|
||||||
|
return "pause"
|
||||||
|
case Reboot:
|
||||||
|
return "reboot"
|
||||||
|
case Reset:
|
||||||
|
return "reset"
|
||||||
|
case ShutDown:
|
||||||
|
return "shutDown"
|
||||||
|
case StartMaintenance:
|
||||||
|
return "startMaintenance"
|
||||||
|
case StopMaintenance:
|
||||||
|
return "stopMaintenance"
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) GetPublicIP(name string, ip string) (*PublicIP, error) {
|
||||||
|
url := fmt.Sprintf("%s/servers/%s/%s/publicIPAddresses/%s", s.config.BaseURL, s.config.Alias, name, ip)
|
||||||
|
resp := &PublicIP{}
|
||||||
|
err := s.client.Get(url, resp)
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) AddPublicIP(name string, ip PublicIP) (*status.Status, error) {
|
||||||
|
url := fmt.Sprintf("%s/servers/%s/%s/publicIPAddresses", s.config.BaseURL, s.config.Alias, name)
|
||||||
|
resp := &status.Status{}
|
||||||
|
err := s.client.Post(url, ip, resp)
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) UpdatePublicIP(name string, public string, ip PublicIP) (*status.Status, error) {
|
||||||
|
url := fmt.Sprintf("%s/servers/%s/%s/publicIPAddresses/%s", s.config.BaseURL, s.config.Alias, name, public)
|
||||||
|
resp := &status.Status{}
|
||||||
|
err := s.client.Put(url, ip, resp)
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) DeletePublicIP(name, ip string) (*status.Status, error) {
|
||||||
|
url := fmt.Sprintf("%s/servers/%s/%s/publicIPAddresses/%s", s.config.BaseURL, s.config.Alias, name, ip)
|
||||||
|
resp := &status.Status{}
|
||||||
|
err := s.client.Delete(url, resp)
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
type PublicIP struct {
|
||||||
|
InternalIP string `json:"internalIPAddress,omitempty"`
|
||||||
|
Ports []Port `json:"ports,omitempty"`
|
||||||
|
SourceRestrictions []SourceRestriction `json:"sourceRestrictions,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Port struct {
|
||||||
|
Protocol string `json:"protocol"`
|
||||||
|
Port int `json:"port"`
|
||||||
|
PortTo int `json:"portTo,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type SourceRestriction struct {
|
||||||
|
CIDR string `json:"cidr"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Disk struct {
|
||||||
|
DiskID string `json:"diskId,omitempty"`
|
||||||
|
Path string `json:"path,omitempty"`
|
||||||
|
SizeGB int `json:"sizeGB,omitempty"`
|
||||||
|
Type string `json:"type,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) AddSecondaryNetwork(name, networkId, ip string) (*status.Status, error) {
|
||||||
|
url := fmt.Sprintf("%s/servers/%s/%s/networks", s.config.BaseURL, s.config.Alias, name)
|
||||||
|
req := &SecondaryNetwork{
|
||||||
|
NetworkID: networkId,
|
||||||
|
IPAddress: ip,
|
||||||
|
}
|
||||||
|
// returned a non-standard status object, repackage into a proper one
|
||||||
|
resp := &status.QueuedOperation{}
|
||||||
|
err := s.client.Post(url, req, resp)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return resp.Status(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type SecondaryNetwork struct {
|
||||||
|
NetworkID string `json:"networkId,omitempty"`
|
||||||
|
IPAddress string `json:"ipAddress,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func UpdateCPU(num int) api.Update {
|
||||||
|
return api.Update{
|
||||||
|
Op: "set",
|
||||||
|
Member: "cpu",
|
||||||
|
Value: num,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func UpdateMemory(num int) api.Update {
|
||||||
|
return api.Update{
|
||||||
|
Op: "set",
|
||||||
|
Member: "memory",
|
||||||
|
Value: num,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func UpdateCredentials(current, updated string) api.Update {
|
||||||
|
return api.Update{
|
||||||
|
Op: "set",
|
||||||
|
Member: "password",
|
||||||
|
Value: struct {
|
||||||
|
Current string `json:"current"`
|
||||||
|
Password string `json:"password"`
|
||||||
|
}{
|
||||||
|
current,
|
||||||
|
updated,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func UpdateGroup(group string) api.Update {
|
||||||
|
return api.Update{
|
||||||
|
Op: "set",
|
||||||
|
Member: "groupId",
|
||||||
|
Value: group,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func UpdateDescription(desc string) api.Update {
|
||||||
|
return api.Update{
|
||||||
|
Op: "set",
|
||||||
|
Member: "description",
|
||||||
|
Value: desc,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func UpdateAdditionaldisks(disks []Disk) api.Update {
|
||||||
|
return api.Update{
|
||||||
|
Op: "set",
|
||||||
|
Member: "disks",
|
||||||
|
Value: disks,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func UpdateCustomfields(fields []api.Customfields) api.Update {
|
||||||
|
return api.Update{
|
||||||
|
Op: "set",
|
||||||
|
Member: "customFields",
|
||||||
|
Value: fields,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type Server struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Description string `json:"description,omitempty"`
|
||||||
|
GroupID string `json:"groupId"`
|
||||||
|
SourceServerID string `json:"sourceServerId"`
|
||||||
|
IsManagedOS bool `json:"isManagedOS,omitempty"`
|
||||||
|
PrimaryDNS string `json:"primaryDns,omitempty"`
|
||||||
|
SecondaryDNS string `json:"secondaryDns,omitempty"`
|
||||||
|
NetworkID string `json:"networkId,omitempty"`
|
||||||
|
IPaddress string `json:"ipAddress,omitempty"`
|
||||||
|
Password string `json:"password,omitempty"`
|
||||||
|
CPU int `json:"cpu"`
|
||||||
|
MemoryGB int `json:"memoryGB"`
|
||||||
|
Type string `json:"type"`
|
||||||
|
Storagetype string `json:"storageType,omitempty"`
|
||||||
|
Customfields []api.Customfields `json:"customFields,omitempty"`
|
||||||
|
Additionaldisks []Disk `json:"additionalDisks,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) Valid() bool {
|
||||||
|
return s.Name != "" && s.CPU != 0 && s.MemoryGB != 0 && s.GroupID != "" && s.SourceServerID != ""
|
||||||
|
}
|
||||||
|
|
||||||
|
type Response struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
GroupID string `json:"groupId"`
|
||||||
|
IsTemplate bool `json:"isTemplate"`
|
||||||
|
LocationID string `json:"locationId"`
|
||||||
|
OStype string `json:"osType"`
|
||||||
|
Status string `json:"status"`
|
||||||
|
Details struct {
|
||||||
|
IPaddresses []struct {
|
||||||
|
Internal string `json:"internal"`
|
||||||
|
Public string `json:"public"`
|
||||||
|
} `json:"ipAddresses"`
|
||||||
|
AlertPolicies []struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Links api.Links `json:"links"`
|
||||||
|
} `json:"alertPolicies"`
|
||||||
|
CPU int `json:"cpu"`
|
||||||
|
Diskcount int `json:"diskCount"`
|
||||||
|
Hostname string `json:"hostName"`
|
||||||
|
InMaintenanceMode bool `json:"inMaintenanceMode"`
|
||||||
|
MemoryMB int `json:"memoryMB"`
|
||||||
|
Powerstate string `json:"powerState"`
|
||||||
|
Storagegb int `json:"storageGB"`
|
||||||
|
Disks []struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
SizeGB int `json:"sizeGB"`
|
||||||
|
PartitionPaths []interface{} `json:"partitionPaths"`
|
||||||
|
} `json:"disks"`
|
||||||
|
Partitions []struct {
|
||||||
|
SizeGB float64 `json:"sizeGB"`
|
||||||
|
Path string `json:"path"`
|
||||||
|
} `json:"partitions"`
|
||||||
|
Snapshots []struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Links api.Links `json:"links"`
|
||||||
|
} `json:"snapshots"`
|
||||||
|
Customfields []api.Customfields `json:"customFields,omitempty"`
|
||||||
|
} `json:"details"`
|
||||||
|
Type string `json:"type"`
|
||||||
|
Storagetype string `json:"storageType"`
|
||||||
|
ChangeInfo struct {
|
||||||
|
CreatedDate string `json:"createdDate"`
|
||||||
|
CreatedBy string `json:"createdBy"`
|
||||||
|
ModifiedDate string `json:"modifiedDate"`
|
||||||
|
ModifiedBy string `json:"modifiedBy"`
|
||||||
|
} `json:"changeInfo"`
|
||||||
|
Links api.Links `json:"links"`
|
||||||
|
}
|
|
@ -0,0 +1,130 @@
|
||||||
|
package status
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/url"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/CenturyLinkCloud/clc-sdk/api"
|
||||||
|
)
|
||||||
|
|
||||||
|
func New(client api.HTTP) *Service {
|
||||||
|
return &Service{
|
||||||
|
client: client,
|
||||||
|
config: client.Config(),
|
||||||
|
PollInterval: 30 * time.Second,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type Service struct {
|
||||||
|
client api.HTTP
|
||||||
|
config *api.Config
|
||||||
|
|
||||||
|
PollInterval time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) Get(id string) (*Response, error) {
|
||||||
|
url := fmt.Sprintf("%s/operations/%s/status/%s", s.config.BaseURL, s.config.Alias, id)
|
||||||
|
status := &Response{}
|
||||||
|
err := s.client.Get(url, status)
|
||||||
|
return status, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) Poll(id string, poll chan *Response) error {
|
||||||
|
for {
|
||||||
|
status, err := s.Get(id)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !status.Running() {
|
||||||
|
poll <- status
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
time.Sleep(s.PollInterval)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type Status struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
Rel string `json:"rel"`
|
||||||
|
Href string `json:"href"`
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Response represents a running async job
|
||||||
|
result from polling status
|
||||||
|
{"status": "succeeded"}
|
||||||
|
*/
|
||||||
|
type Response struct {
|
||||||
|
Status string `json:"status"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Response) Complete() bool {
|
||||||
|
return s.Status == Complete
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Response) Failed() bool {
|
||||||
|
return s.Status == Failed
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Response) Running() bool {
|
||||||
|
return !s.Complete() && !s.Failed() && s.Status != ""
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
Complete = "succeeded"
|
||||||
|
Failed = "failed"
|
||||||
|
)
|
||||||
|
|
||||||
|
/* QueuedResponse represents a returned response for an async platform job
|
||||||
|
eg. create server
|
||||||
|
{"server":"web", "isQueued":true, "links":[
|
||||||
|
{"rel":"status", "href":"...", "id":"wa1-12345"},
|
||||||
|
{"rel":"self", "href":"...", "id":"8134c91a66784c6dada651eba90a5123"}]}
|
||||||
|
*/
|
||||||
|
type QueuedResponse struct {
|
||||||
|
Server string `json:"server,omitempty"`
|
||||||
|
IsQueued bool `json:"isQueued,omitempty"`
|
||||||
|
Links api.Links `json:"links,omitempty"`
|
||||||
|
Error string `json:"errorMessage,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *QueuedResponse) GetStatusID() (bool, string) {
|
||||||
|
return q.Links.GetID("status")
|
||||||
|
}
|
||||||
|
|
||||||
|
/* QueuedOperation may be a one-off and/or experimental version of QueuedResponse
|
||||||
|
eg. add secondary network
|
||||||
|
{"operationId": "2b70710dba4142dcaf3ab2de68e4f40c", "uri": "..."}
|
||||||
|
*/
|
||||||
|
type QueuedOperation struct {
|
||||||
|
OperationID string `json:"operationId,omitempty"`
|
||||||
|
URI string `json:"uri,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *QueuedOperation) GetStatusID() (bool, string) {
|
||||||
|
return q.OperationID != "", q.OperationID
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *QueuedOperation) GetHref() (bool, string) {
|
||||||
|
var path = ""
|
||||||
|
if q.URI != "" {
|
||||||
|
u, err := url.Parse(q.URI)
|
||||||
|
if err == nil {
|
||||||
|
path = u.Path
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return path != "", path
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *QueuedOperation) Status() *Status {
|
||||||
|
st := &Status{}
|
||||||
|
if ok, id := q.GetStatusID(); ok {
|
||||||
|
st.ID = id
|
||||||
|
}
|
||||||
|
if ok, href := q.GetHref(); ok {
|
||||||
|
st.Href = href
|
||||||
|
}
|
||||||
|
return st
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
The MIT License
|
||||||
|
===============
|
||||||
|
|
||||||
|
Copyright (c) 2015 Mike Beyer
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
|
@ -0,0 +1,18 @@
|
||||||
|
env
|
||||||
|
===
|
||||||
|
|
||||||
|
[ ![Codeship Status for mikebeyer/env](https://codeship.io/projects/d046ac90-ae6d-0132-79aa-6a5d0765ab36/status)](https://codeship.io/projects/68901)
|
||||||
|
|
||||||
|
|
||||||
|
Go implementation for default values for environment variables.
|
||||||
|
|
||||||
|
~~~ go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import "github.com/mikebeyer/env"
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
port := env.String("PORT", "8080")
|
||||||
|
fmt.Printf("port: %s", port)
|
||||||
|
}
|
||||||
|
~~~
|
|
@ -0,0 +1,73 @@
|
||||||
|
// Package env provides convenience wrapper around getting environment variables.
|
||||||
|
package env
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
// String gets string variable from the environment and
|
||||||
|
// returns it if it exists, otherwise it returns the default.
|
||||||
|
func String(key string, def string) string {
|
||||||
|
val := os.Getenv(key)
|
||||||
|
if val == "" {
|
||||||
|
return def
|
||||||
|
}
|
||||||
|
return val
|
||||||
|
}
|
||||||
|
|
||||||
|
// MustString exits if an environment variable is not present.
|
||||||
|
func MustString(key string) string {
|
||||||
|
val := os.Getenv(key)
|
||||||
|
if val == "" {
|
||||||
|
fmt.Printf("%s must be provided.", key)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
return val
|
||||||
|
}
|
||||||
|
|
||||||
|
// Int gets int variable from the environment and
|
||||||
|
// returns it if it exists, otherwise it returns the default.
|
||||||
|
func Int(key string, def int) int {
|
||||||
|
val := os.Getenv(key)
|
||||||
|
if val == "" {
|
||||||
|
return def
|
||||||
|
}
|
||||||
|
|
||||||
|
i, err := strconv.Atoi(val)
|
||||||
|
if err != nil {
|
||||||
|
return def
|
||||||
|
}
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bool gets boolean variable from the environment and
|
||||||
|
// returns it if it exists, otherwise it returns the default.
|
||||||
|
func Bool(key string, def bool) bool {
|
||||||
|
val := os.Getenv(key)
|
||||||
|
if val == "" {
|
||||||
|
return def
|
||||||
|
}
|
||||||
|
|
||||||
|
b, err := strconv.ParseBool(val)
|
||||||
|
if err != nil {
|
||||||
|
return def
|
||||||
|
}
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
// Float gets float variable with a provided bit type from the environment and
|
||||||
|
// returns it if it exists, otherwise it returns the default.
|
||||||
|
func Float(key string, def float64, bit int) float64 {
|
||||||
|
val := os.Getenv(key)
|
||||||
|
if val == "" {
|
||||||
|
return def
|
||||||
|
}
|
||||||
|
|
||||||
|
f, err := strconv.ParseFloat(val, bit)
|
||||||
|
if err != nil {
|
||||||
|
return def
|
||||||
|
}
|
||||||
|
return f
|
||||||
|
}
|
Loading…
Reference in New Issue