dnsprovider: upgrade egoscale to v0.11.1 (#617)

This commit is contained in:
Yoan Blanc 2018-09-08 12:09:59 +02:00 committed by Ludovic Fernandez
parent de3accf531
commit f3cfe4a24a
437 changed files with 27494 additions and 8548 deletions

168
Gopkg.lock generated
View file

@ -2,26 +2,26 @@
[[projects]]
digest = "1:f8ad8a53fa865a70efbe215b0ca34735523f50ea39e0efde319ab6fc80089b44"
digest = "1:5c3894b2aa4d6bead0ceeea6831b305d62879c871780e7b76296ded1b004bc57"
name = "cloud.google.com/go"
packages = ["compute/metadata"]
pruneopts = "NUT"
revision = "0fd7230b2a7505833d5f69b75cbd6c9582401479"
version = "v0.23.0"
revision = "64a2037ec6be8a4b0c1d1f706ed35b428b989239"
version = "v0.26.0"
[[projects]]
digest = "1:9b32ab3f23ea7ea14ec197ebe250d5e8a8796b4262d7adb83cd8658d92ce3f09"
digest = "1:e589e863ce0d0500bb806c716877c2f8caffe2a2b6c2f6c148d535d8eeb92d00"
name = "github.com/Azure/azure-sdk-for-go"
packages = [
"services/dns/mgmt/2017-09-01/dns",
"version",
]
pruneopts = "NUT"
revision = "4650843026a7fdec254a8d9cf893693a254edd0b"
version = "v16.2.1"
revision = "4e8cbbfb1aeab140cd0fa97fd16b64ee18c3ca6a"
version = "v19.1.0"
[[projects]]
digest = "1:ea9819df34b0aafdc42f6693504795cf9fbb57a09da5ed3fed235f114f43e083"
digest = "1:6b05ebcb43e299f0319de63de668c804a4c3a2dbc12da392a6720de500b10358"
name = "github.com/Azure/go-autorest"
packages = [
"autorest",
@ -29,10 +29,13 @@
"autorest/azure",
"autorest/date",
"autorest/to",
"autorest/validation",
"logger",
"version",
]
pruneopts = "NUT"
revision = "aa2a4534ab680e938d933870f58f23f77e0e208e"
version = "v10.9.0"
revision = "39013ecb48eaf6ced3f4e3e1d95515140ce6b3cf"
version = "v10.15.2"
[[projects]]
digest = "1:ed3fc9992df610d07c85c24e0b792268cc1ce226dd9bf8cb2e6ad9a377b35415"
@ -64,7 +67,7 @@
version = "v0.6.2"
[[projects]]
digest = "1:bf1d2990b1ff737e7ad35b21413747016a187056683e8db2ab34b37bd79905a8"
digest = "1:1634c57333a07adf7d7e206d3329340f1c36e82bca9e61e2cff08c064ab85046"
name = "github.com/aws/aws-sdk-go"
packages = [
"aws",
@ -77,6 +80,7 @@
"aws/credentials/ec2rolecreds",
"aws/credentials/endpointcreds",
"aws/credentials/stscreds",
"aws/csm",
"aws/defaults",
"aws/ec2metadata",
"aws/endpoints",
@ -85,6 +89,7 @@
"aws/signer/v4",
"internal/sdkio",
"internal/sdkrand",
"internal/sdkuri",
"internal/shareddefaults",
"private/protocol",
"private/protocol/json/jsonutil",
@ -99,8 +104,8 @@
"service/sts",
]
pruneopts = "NUT"
revision = "398d14696895d68a3409bb3ccb1cfe8abc2d4376"
version = "v1.13.57"
revision = "8b15f938ed215522a37275106e847f6f0be85fe8"
version = "v1.15.23"
[[projects]]
branch = "master"
@ -108,15 +113,15 @@
name = "github.com/cpu/goacmedns"
packages = ["."]
pruneopts = "NUT"
revision = "f232997f461a5a58982d536108cfc382e512481e"
revision = "565ecf2a84df654865cc102705ac160a3b04fc01"
[[projects]]
digest = "1:a2c1d0e43bd3baaa071d1b9ed72c27d78169b2b269f71c105ac4ba34b1be4a39"
digest = "1:ffe9824d294da03b391f44e1ae8281281b4afc1bdaa9588c9097785e3af10cec"
name = "github.com/davecgh/go-spew"
packages = ["spew"]
pruneopts = "NUT"
revision = "346938d642f2ec3594ed81d874461961cd0faa76"
version = "v1.1.0"
revision = "8991bc29aa16c548c550c7ff78260e27b9ab7c73"
version = "v1.1.1"
[[projects]]
branch = "master"
@ -136,11 +141,11 @@
[[projects]]
branch = "master"
digest = "1:e8055cec2992f8bbf63c390aa6b36d78c2f93b13617e4569168c09219f88c6b0"
digest = "1:6b873be0e0ec65484ee086d02143f31332e363b968fdc6d6663160fa98fda505"
name = "github.com/dnsimple/dnsimple-go"
packages = ["dnsimple"]
pruneopts = "NUT"
revision = "bbe1a2c87affea187478e24d3aea3cac25f870b3"
revision = "35bcc6b47c20ec9bf3a53adcb7fa9665a75f0e7b"
[[projects]]
digest = "1:82127d77b40b617d650e64dc287cfc190f31d1030bd01cae5110780dd1e5bbb3"
@ -158,28 +163,28 @@
version = "v1.0.3"
[[projects]]
digest = "1:88edf0bd76f728da7c2f0bae296f2efdada8b24d670db83597a524757d7ca86d"
digest = "1:e096f1857eedd49e2bd0885d05105d1d4af1bfcf8b1d07fa5710718e6641fd48"
name = "github.com/exoscale/egoscale"
packages = ["."]
pruneopts = "NUT"
revision = "caa6b3727b959d704dda224fb7a63fae3c14d8fb"
version = "v0.9.26"
revision = "0863d555d5198557e0bf2b61b6c59a873ab0173a"
version = "v0.11.1"
[[projects]]
digest = "1:30b09d75e7e16119e85020374df10ea06f263c9bdff72afdc1522838b9609375"
digest = "1:74d9b0a7b4107b41e0ade759fac64502876f82d29fb23d77b3dd24b194ee3dd5"
name = "github.com/go-ini/ini"
packages = ["."]
pruneopts = "NUT"
revision = "06f5f3d67269ccec1fe5fe4134ba6e982984f7f5"
version = "v1.37.0"
revision = "5cf292cae48347c2490ac1a58fe36735fb78df7e"
version = "v1.38.2"
[[projects]]
digest = "1:15042ad3498153684d09f393bbaec6b216c8eec6d61f63dff711de7d64ed8861"
digest = "1:97df918963298c287643883209a2c3f642e6593379f97ab400c2a2e219ab647d"
name = "github.com/golang/protobuf"
packages = ["proto"]
pruneopts = "NUT"
revision = "b4deda0973fb4c70b50d226b1af49f3da59f5265"
version = "v1.1.0"
revision = "aa810b61a9c79d51363740d207bb46cf8e620ed5"
version = "v1.2.0"
[[projects]]
branch = "master"
@ -190,20 +195,12 @@
revision = "53e6ce116135b80d037921a7fdd5138cf32d7a8a"
[[projects]]
digest = "1:1bb197a3b5db4e06e00b7560f8e89836c486627f2a0338332ed37daa003d259e"
digest = "1:a1578f7323eca2b88021fdc9a79a99833d40b12c32a5ea4f284e2fad19ea2657"
name = "github.com/google/uuid"
packages = ["."]
pruneopts = "NUT"
revision = "064e2069ce9c359c118179501254f67d7d37ba24"
version = "0.2"
[[projects]]
branch = "master"
digest = "1:6c2f939d99ad2074a4113c36aa93fa792ede8ff0b8cdb97634b41b9c5da7fab6"
name = "github.com/jinzhu/copier"
packages = ["."]
pruneopts = "NUT"
revision = "7e38e58719c33e0d44d585c4ab477a30f8cb82dd"
revision = "d460ce9f8df2e77fb1ba55ca87fafed96c607494"
version = "v1.0.0"
[[projects]]
digest = "1:ac6d01547ec4f7f673311b4663909269bfb8249952de3279799289467837c3cc"
@ -213,12 +210,12 @@
revision = "0b12d6b5"
[[projects]]
digest = "1:42c47ace7ccb114261ef7e0d418d274921514ab50a3bf6bdb9e51c3dde8ce13d"
digest = "1:8e36686e8b139f8fe240c1d5cf3a145bc675c22ff8e707857cdd3ae17b00d728"
name = "github.com/json-iterator/go"
packages = ["."]
pruneopts = "NUT"
revision = "ca39e5af3ece67bbcda3d0f4f56a8e24d9f2dad4"
version = "1.1.3"
revision = "1624edc4454b8682399def8740d46db5e4362ba4"
version = "v1.1.5"
[[projects]]
digest = "1:8b3234b10eacd5edea45bf0c13a585b608749da23f94aaf29b46d9ef8a8babf4"
@ -229,20 +226,20 @@
version = "1.0.1"
[[projects]]
digest = "1:ea82624b8f3f9b68075183aeb1000bcf3adb2f0da483758646c48cf29417ea7e"
digest = "1:24b5f8d41224b90e3f4d22768926ed782a8ca481d945c0e064c8f165bf768280"
name = "github.com/miekg/dns"
packages = ["."]
pruneopts = "NUT"
revision = "e57bf427e68187a27e22adceac868350d7a7079b"
version = "v1.0.7"
revision = "5a2b9fab83ff0f8bfc99684bd5f43a37abe560f1"
version = "v1.0.8"
[[projects]]
branch = "master"
digest = "1:9db29b604bd78452d167abed82386ddd2f93973df3841896fb6ab8aff936f1d6"
digest = "1:a4df73029d2c42fabcb6b41e327d2f87e685284ec03edf76921c267d9cfc9c23"
name = "github.com/mitchellh/go-homedir"
packages = ["."]
pruneopts = "NUT"
revision = "3864e76763d94a6df2f9960b16a20a33da9f9a66"
revision = "ae18d6b8b3205b561c79e8e5f69bff09736185f4"
version = "v1.0.0"
[[projects]]
digest = "1:2f42fa12d6911c7b7659738758631bec870b7e9b4c6be5444f963cdcfccc191f"
@ -253,12 +250,12 @@
version = "1.0.3"
[[projects]]
digest = "1:314a5881fab303a80d6d2e35a77000f2224bb50f09ef63a9aa4c1f9eaef985d8"
digest = "1:c6aca19413b13dc59c220ad7430329e2ec454cc310bc6d8de2c7e2b93c18a0f6"
name = "github.com/modern-go/reflect2"
packages = ["."]
pruneopts = "NUT"
revision = "1df9eeb2bb81f327b96228865c5687bc2194af3f"
version = "1.0.0"
revision = "4b7aa43c6742a2c18fdef89dd197aaae7dac7ccd"
version = "1.0.1"
[[projects]]
branch = "master"
@ -270,11 +267,11 @@
[[projects]]
branch = "master"
digest = "1:4e9d94fd7812a4c41e403796decb682a8f18fa50fa1f48532967dddc279303f5"
digest = "1:02584222c11b07d1d61cd76cc73e78cbc72810384a7ea69c543246d60d3335f7"
name = "github.com/ovh/go-ovh"
packages = ["ovh"]
pruneopts = "NUT"
revision = "91b7eb631d2eced3e706932a0b36ee8b5ee22e92"
revision = "c3e61035ea66f5c637719c90140da4e3ac3b1bf0"
[[projects]]
digest = "1:5cf3f025cbee5951a4ee961de067c8a89fc95a5adabead774f82822efabab121"
@ -302,7 +299,7 @@
[[projects]]
branch = "master"
digest = "1:cd3baee48de2e9fc878d4099b8ea0601b6babd525d61c65a86dd3572b08ae964"
digest = "1:0f9362b2768972675cf28574249bfb5dd65556aac6ad1c36830b4bc8c2134926"
name = "github.com/sacloud/libsacloud"
packages = [
".",
@ -311,18 +308,26 @@
"sacloud/ostype",
]
pruneopts = "NUT"
revision = "178aa57136e8c6e3882b6b5ef37a48f0e7cf836f"
revision = "7afff3fbc0a3bdff2e008fe2c429d44d9f66f209"
[[projects]]
digest = "1:6989062eb7ccf25cf38bf4fe3dba097ee209f896cda42cefdca3927047bef7b6"
digest = "1:6bc0652ea6e39e22ccd522458b8bdd8665bf23bdc5a20eec90056e4dc7e273ca"
name = "github.com/satori/go.uuid"
packages = ["."]
pruneopts = "NUT"
revision = "f58768cc1a7a7e77a3bd49e98cdd21419399b6a3"
version = "v1.2.0"
[[projects]]
digest = "1:b2339e83ce9b5c4f79405f949429a7f68a9a904fed903c672aac1e7ceb7f5f02"
name = "github.com/sirupsen/logrus"
packages = ["."]
pruneopts = "NUT"
revision = "c155da19408a8799da419ed3eeb0cb5db0ad5dbc"
version = "v1.0.5"
revision = "3e01752db0189b9157070a0e1668a620f9a85da2"
version = "v1.0.6"
[[projects]]
digest = "1:7f694e534dcd061d6a308f5b9d50b389f81c3d636920b0a03176516e958cf32d"
digest = "1:b31059dac028ff111793a8345eacf0f99d0f1150ead34ebf32fdd2b7d54c2d45"
name = "github.com/stretchr/testify"
packages = [
"assert",
@ -330,8 +335,8 @@
"suite",
]
pruneopts = "NUT"
revision = "12b6f73e6084dad08a7c6e575284b177ecafbc71"
version = "v1.2.1"
revision = "f35b8ab0b5a2cef36673838d662e249dd9c94686"
version = "v1.2.2"
[[projects]]
branch = "master"
@ -354,20 +359,21 @@
[[projects]]
branch = "master"
digest = "1:55aa7c2f4c2196b10eb56a589666cf132054f2950b2272d9ff4e0c2921600888"
digest = "1:1683f109d69f4a055621eeb7f396d37121c1d2a259b3a77030c3ce5a1b8c92e9"
name = "golang.org/x/crypto"
packages = [
"ed25519",
"ed25519/internal/edwards25519",
"ocsp",
"pbkdf2",
"ssh/terminal",
]
pruneopts = "NUT"
revision = "ab813273cd59e1333f7ae7bff5d027d4aadf528c"
revision = "614d502a4dac94afa3a6ce146bd1736da82514c6"
[[projects]]
branch = "master"
digest = "1:008bb6a09679c9b8381873d8c2a11774b81f393ecb1dce18ad70cdcf8643316d"
digest = "1:bec9fee9f0fc9b6f8cbcd4a7e2d80036829079f0aaccc28776510b18ec5f0dc5"
name = "golang.org/x/net"
packages = [
"bpf",
@ -379,11 +385,11 @@
"ipv6",
]
pruneopts = "NUT"
revision = "75944861c7512f64725d687546cfbc757626151f"
revision = "8a410e7b638dca158bf9e766925842f6651ff828"
[[projects]]
branch = "master"
digest = "1:cc7e2aaaa87d7bf0e7d08baede383b27984eec7175a4bc504255352c77166a4d"
digest = "1:bc2b221d465bb28ce46e8d472ecdc424b9a9b541bd61d8c311c5f29c8dd75b1b"
name = "golang.org/x/oauth2"
packages = [
".",
@ -393,22 +399,22 @@
"jwt",
]
pruneopts = "NUT"
revision = "ec22f46f877b4505e0117eeaab541714644fdd28"
revision = "d2e6202438beef2727060aa7cabdd924d92ebfd9"
[[projects]]
branch = "master"
digest = "1:0dbe2a3199cde81dd841ea2de1386165f953d227ddb2b5c2c4b591209bf37746"
digest = "1:e1498a6884b2564efe2a4c4aefce64eb4bc6a4666acfd42db044cf581db4d9cb"
name = "golang.org/x/sys"
packages = [
"unix",
"windows",
]
pruneopts = "NUT"
revision = "c11f84a56e43e20a78cee75a7c034031ecf57d1f"
revision = "d99a578cf41bfccdeaf48b0845c823a4b8b0ad5e"
[[projects]]
branch = "master"
digest = "1:da32ebe70dd3ec97d2df26281b08b18d05c2f12491ae79f389813f6c8d3006b3"
digest = "1:90f61cd4297f7f4ce75f142744673ecdafd9023668cd24730f14033f265e394a"
name = "google.golang.org/api"
packages = [
"dns/v1",
@ -417,10 +423,10 @@
"googleapi/internal/uritemplates",
]
pruneopts = "NUT"
revision = "de943baf05a022a8f921b544b7827bacaba1aed5"
revision = "087779f1d2c96357d4c45bd04c4d10d7b5f22736"
[[projects]]
digest = "1:7206d98ec77c90c72ec2c405181a1dcf86965803b6dbc4f98ceab7a5047c37a9"
digest = "1:b5aeceb800276be69165a745328380c8a4b6f2edc7a6bac112e22c8596ed8a49"
name = "google.golang.org/appengine"
packages = [
".",
@ -435,20 +441,20 @@
"urlfetch",
]
pruneopts = "NUT"
revision = "150dc57a1b433e64154302bdc40b6bb8aefa313a"
version = "v1.0.0"
revision = "b1f26356af11148e710935ed1ac8a7f5702c7612"
version = "v1.1.0"
[[projects]]
digest = "1:30b09d75e7e16119e85020374df10ea06f263c9bdff72afdc1522838b9609375"
digest = "1:74d9b0a7b4107b41e0ade759fac64502876f82d29fb23d77b3dd24b194ee3dd5"
name = "gopkg.in/ini.v1"
packages = ["."]
pruneopts = "NUT"
revision = "06f5f3d67269ccec1fe5fe4134ba6e982984f7f5"
version = "v1.37.0"
revision = "5cf292cae48347c2490ac1a58fe36735fb78df7e"
version = "v1.38.2"
[[projects]]
branch = "v2"
digest = "1:f57d15cb0f61c985f61fd2c529245debb439128ecc8c44c06251900d281c68fc"
digest = "1:f15af196d07dbb28a0633599eba0fb872acd0b24426001520dd6cb9944c7ad1a"
name = "gopkg.in/ns1/ns1-go.v2"
packages = [
"rest",
@ -459,10 +465,10 @@
"rest/model/monitor",
]
pruneopts = "NUT"
revision = "a5bcac82d3f637d3928d30476610891935b2d691"
revision = "028658c6d9be774b6d103a923d8c4b2715135c3f"
[[projects]]
digest = "1:c25ddb3b9fc4ec258f94a4e10b5e7eab434e3e5a5a21fef1dbe1749155abf7b2"
digest = "1:3b7124c543146736e07107be13ea6288923c4a743e07c7a31d6b7209a00a9dab"
name = "gopkg.in/square/go-jose.v2"
packages = [
".",
@ -470,8 +476,8 @@
"json",
]
pruneopts = "NUT"
revision = "76dd09796242edb5b897103a75df2645c028c960"
version = "v2.1.6"
revision = "8254d6c783765f38c8675fae4427a1fe73fbd09d"
version = "v2.1.8"
[solve-meta]
analyzer-name = "dep"

View file

@ -76,3 +76,7 @@
[[constraint]]
branch = "master"
name = "github.com/sacloud/libsacloud"
[[constraint]]
version = "0.11.1"
name = "github.com/exoscale/egoscale"

View file

@ -58,6 +58,7 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error {
return err
}
if recordID == 0 {
record := egoscale.DNSRecord{
Name: recordName,
TTL: ttl,
@ -65,13 +66,19 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error {
RecordType: "TXT",
}
if recordID == 0 {
_, err := d.client.CreateRecord(zone, record)
if err != nil {
return errors.New("Error while creating DNS record: " + err.Error())
}
} else {
record.ID = recordID
record := egoscale.UpdateDNSRecord{
ID: recordID,
Name: recordName,
TTL: ttl,
Content: value,
RecordType: "TXT",
}
_, err := d.client.UpdateRecord(zone, record)
if err != nil {
return errors.New("Error while updating DNS record: " + err.Error())

View file

@ -1,4 +1,4 @@
// Copyright 2014 Google Inc. All Rights Reserved.
// Copyright 2014 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@ -64,7 +64,7 @@ var (
)
var (
metaClient = &http.Client{
defaultClient = &Client{hc: &http.Client{
Transport: &http.Transport{
Dial: (&net.Dialer{
Timeout: 2 * time.Second,
@ -72,15 +72,15 @@ var (
}).Dial,
ResponseHeaderTimeout: 2 * time.Second,
},
}
subscribeClient = &http.Client{
}}
subscribeClient = &Client{hc: &http.Client{
Transport: &http.Transport{
Dial: (&net.Dialer{
Timeout: 2 * time.Second,
KeepAlive: 30 * time.Second,
}).Dial,
},
}
}}
)
// NotDefinedError is returned when requested metadata is not defined.
@ -95,74 +95,16 @@ func (suffix NotDefinedError) Error() string {
return fmt.Sprintf("metadata: GCE metadata %q not defined", string(suffix))
}
// Get returns a value from the metadata service.
// The suffix is appended to "http://${GCE_METADATA_HOST}/computeMetadata/v1/".
//
// If the GCE_METADATA_HOST environment variable is not defined, a default of
// 169.254.169.254 will be used instead.
//
// If the requested metadata is not defined, the returned error will
// be of type NotDefinedError.
func Get(suffix string) (string, error) {
val, _, err := getETag(metaClient, suffix)
return val, err
}
// getETag returns a value from the metadata service as well as the associated
// ETag using the provided client. This func is otherwise equivalent to Get.
func getETag(client *http.Client, suffix string) (value, etag string, err error) {
// Using a fixed IP makes it very difficult to spoof the metadata service in
// a container, which is an important use-case for local testing of cloud
// deployments. To enable spoofing of the metadata service, the environment
// variable GCE_METADATA_HOST is first inspected to decide where metadata
// requests shall go.
host := os.Getenv(metadataHostEnv)
if host == "" {
// Using 169.254.169.254 instead of "metadata" here because Go
// binaries built with the "netgo" tag and without cgo won't
// know the search suffix for "metadata" is
// ".google.internal", and this IP address is documented as
// being stable anyway.
host = metadataIP
}
url := "http://" + host + "/computeMetadata/v1/" + suffix
req, _ := http.NewRequest("GET", url, nil)
req.Header.Set("Metadata-Flavor", "Google")
req.Header.Set("User-Agent", userAgent)
res, err := client.Do(req)
if err != nil {
return "", "", err
}
defer res.Body.Close()
if res.StatusCode == http.StatusNotFound {
return "", "", NotDefinedError(suffix)
}
if res.StatusCode != 200 {
return "", "", fmt.Errorf("status code %d trying to fetch %s", res.StatusCode, url)
}
all, err := ioutil.ReadAll(res.Body)
if err != nil {
return "", "", err
}
return string(all), res.Header.Get("Etag"), nil
}
func getTrimmed(suffix string) (s string, err error) {
s, err = Get(suffix)
s = strings.TrimSpace(s)
return
}
func (c *cachedValue) get() (v string, err error) {
func (c *cachedValue) get(cl *Client) (v string, err error) {
defer c.mu.Unlock()
c.mu.Lock()
if c.v != "" {
return c.v, nil
}
if c.trim {
v, err = getTrimmed(c.k)
v, err = cl.getTrimmed(c.k)
} else {
v, err = Get(c.k)
v, err = cl.Get(c.k)
}
if err == nil {
c.v = v
@ -201,7 +143,7 @@ func testOnGCE() bool {
go func() {
req, _ := http.NewRequest("GET", "http://"+metadataIP, nil)
req.Header.Set("User-Agent", userAgent)
res, err := ctxhttp.Do(ctx, metaClient, req)
res, err := ctxhttp.Do(ctx, defaultClient.hc, req)
if err != nil {
resc <- false
return
@ -266,6 +208,255 @@ func systemInfoSuggestsGCE() bool {
return name == "Google" || name == "Google Compute Engine"
}
// Subscribe calls Client.Subscribe on a client designed for subscribing (one with no
// ResponseHeaderTimeout).
func Subscribe(suffix string, fn func(v string, ok bool) error) error {
return subscribeClient.Subscribe(suffix, fn)
}
// Get calls Client.Get on the default client.
func Get(suffix string) (string, error) { return defaultClient.Get(suffix) }
// ProjectID returns the current instance's project ID string.
func ProjectID() (string, error) { return defaultClient.ProjectID() }
// NumericProjectID returns the current instance's numeric project ID.
func NumericProjectID() (string, error) { return defaultClient.NumericProjectID() }
// InternalIP returns the instance's primary internal IP address.
func InternalIP() (string, error) { return defaultClient.InternalIP() }
// ExternalIP returns the instance's primary external (public) IP address.
func ExternalIP() (string, error) { return defaultClient.ExternalIP() }
// Hostname returns the instance's hostname. This will be of the form
// "<instanceID>.c.<projID>.internal".
func Hostname() (string, error) { return defaultClient.Hostname() }
// InstanceTags returns the list of user-defined instance tags,
// assigned when initially creating a GCE instance.
func InstanceTags() ([]string, error) { return defaultClient.InstanceTags() }
// InstanceID returns the current VM's numeric instance ID.
func InstanceID() (string, error) { return defaultClient.InstanceID() }
// InstanceName returns the current VM's instance ID string.
func InstanceName() (string, error) { return defaultClient.InstanceName() }
// Zone returns the current VM's zone, such as "us-central1-b".
func Zone() (string, error) { return defaultClient.Zone() }
// InstanceAttributes calls Client.InstanceAttributes on the default client.
func InstanceAttributes() ([]string, error) { return defaultClient.InstanceAttributes() }
// ProjectAttributes calls Client.ProjectAttributes on the default client.
func ProjectAttributes() ([]string, error) { return defaultClient.ProjectAttributes() }
// InstanceAttributeValue calls Client.InstanceAttributeValue on the default client.
func InstanceAttributeValue(attr string) (string, error) {
return defaultClient.InstanceAttributeValue(attr)
}
// ProjectAttributeValue calls Client.ProjectAttributeValue on the default client.
func ProjectAttributeValue(attr string) (string, error) {
return defaultClient.ProjectAttributeValue(attr)
}
// Scopes calls Client.Scopes on the default client.
func Scopes(serviceAccount string) ([]string, error) { return defaultClient.Scopes(serviceAccount) }
func strsContains(ss []string, s string) bool {
for _, v := range ss {
if v == s {
return true
}
}
return false
}
// A Client provides metadata.
type Client struct {
hc *http.Client
}
// NewClient returns a Client that can be used to fetch metadata. All HTTP requests
// will use the given http.Client instead of the default client.
func NewClient(c *http.Client) *Client {
return &Client{hc: c}
}
// getETag returns a value from the metadata service as well as the associated ETag.
// This func is otherwise equivalent to Get.
func (c *Client) getETag(suffix string) (value, etag string, err error) {
// Using a fixed IP makes it very difficult to spoof the metadata service in
// a container, which is an important use-case for local testing of cloud
// deployments. To enable spoofing of the metadata service, the environment
// variable GCE_METADATA_HOST is first inspected to decide where metadata
// requests shall go.
host := os.Getenv(metadataHostEnv)
if host == "" {
// Using 169.254.169.254 instead of "metadata" here because Go
// binaries built with the "netgo" tag and without cgo won't
// know the search suffix for "metadata" is
// ".google.internal", and this IP address is documented as
// being stable anyway.
host = metadataIP
}
url := "http://" + host + "/computeMetadata/v1/" + suffix
req, _ := http.NewRequest("GET", url, nil)
req.Header.Set("Metadata-Flavor", "Google")
req.Header.Set("User-Agent", userAgent)
res, err := c.hc.Do(req)
if err != nil {
return "", "", err
}
defer res.Body.Close()
if res.StatusCode == http.StatusNotFound {
return "", "", NotDefinedError(suffix)
}
if res.StatusCode != 200 {
return "", "", fmt.Errorf("status code %d trying to fetch %s", res.StatusCode, url)
}
all, err := ioutil.ReadAll(res.Body)
if err != nil {
return "", "", err
}
return string(all), res.Header.Get("Etag"), nil
}
// Get returns a value from the metadata service.
// The suffix is appended to "http://${GCE_METADATA_HOST}/computeMetadata/v1/".
//
// If the GCE_METADATA_HOST environment variable is not defined, a default of
// 169.254.169.254 will be used instead.
//
// If the requested metadata is not defined, the returned error will
// be of type NotDefinedError.
func (c *Client) Get(suffix string) (string, error) {
val, _, err := c.getETag(suffix)
return val, err
}
func (c *Client) getTrimmed(suffix string) (s string, err error) {
s, err = c.Get(suffix)
s = strings.TrimSpace(s)
return
}
func (c *Client) lines(suffix string) ([]string, error) {
j, err := c.Get(suffix)
if err != nil {
return nil, err
}
s := strings.Split(strings.TrimSpace(j), "\n")
for i := range s {
s[i] = strings.TrimSpace(s[i])
}
return s, nil
}
// ProjectID returns the current instance's project ID string.
func (c *Client) ProjectID() (string, error) { return projID.get(c) }
// NumericProjectID returns the current instance's numeric project ID.
func (c *Client) NumericProjectID() (string, error) { return projNum.get(c) }
// InstanceID returns the current VM's numeric instance ID.
func (c *Client) InstanceID() (string, error) { return instID.get(c) }
// InternalIP returns the instance's primary internal IP address.
func (c *Client) InternalIP() (string, error) {
return c.getTrimmed("instance/network-interfaces/0/ip")
}
// ExternalIP returns the instance's primary external (public) IP address.
func (c *Client) ExternalIP() (string, error) {
return c.getTrimmed("instance/network-interfaces/0/access-configs/0/external-ip")
}
// Hostname returns the instance's hostname. This will be of the form
// "<instanceID>.c.<projID>.internal".
func (c *Client) Hostname() (string, error) {
return c.getTrimmed("instance/hostname")
}
// InstanceTags returns the list of user-defined instance tags,
// assigned when initially creating a GCE instance.
func (c *Client) InstanceTags() ([]string, error) {
var s []string
j, err := c.Get("instance/tags")
if err != nil {
return nil, err
}
if err := json.NewDecoder(strings.NewReader(j)).Decode(&s); err != nil {
return nil, err
}
return s, nil
}
// InstanceName returns the current VM's instance ID string.
func (c *Client) InstanceName() (string, error) {
host, err := c.Hostname()
if err != nil {
return "", err
}
return strings.Split(host, ".")[0], nil
}
// Zone returns the current VM's zone, such as "us-central1-b".
func (c *Client) Zone() (string, error) {
zone, err := c.getTrimmed("instance/zone")
// zone is of the form "projects/<projNum>/zones/<zoneName>".
if err != nil {
return "", err
}
return zone[strings.LastIndex(zone, "/")+1:], nil
}
// InstanceAttributes returns the list of user-defined attributes,
// assigned when initially creating a GCE VM instance. The value of an
// attribute can be obtained with InstanceAttributeValue.
func (c *Client) InstanceAttributes() ([]string, error) { return c.lines("instance/attributes/") }
// ProjectAttributes returns the list of user-defined attributes
// applying to the project as a whole, not just this VM. The value of
// an attribute can be obtained with ProjectAttributeValue.
func (c *Client) ProjectAttributes() ([]string, error) { return c.lines("project/attributes/") }
// InstanceAttributeValue returns the value of the provided VM
// instance attribute.
//
// If the requested attribute is not defined, the returned error will
// be of type NotDefinedError.
//
// InstanceAttributeValue may return ("", nil) if the attribute was
// defined to be the empty string.
func (c *Client) InstanceAttributeValue(attr string) (string, error) {
return c.Get("instance/attributes/" + attr)
}
// ProjectAttributeValue returns the value of the provided
// project attribute.
//
// If the requested attribute is not defined, the returned error will
// be of type NotDefinedError.
//
// ProjectAttributeValue may return ("", nil) if the attribute was
// defined to be the empty string.
func (c *Client) ProjectAttributeValue(attr string) (string, error) {
return c.Get("project/attributes/" + attr)
}
// Scopes returns the service account scopes for the given account.
// The account may be empty or the string "default" to use the instance's
// main account.
func (c *Client) Scopes(serviceAccount string) ([]string, error) {
if serviceAccount == "" {
serviceAccount = "default"
}
return c.lines("instance/service-accounts/" + serviceAccount + "/scopes")
}
// Subscribe subscribes to a value from the metadata service.
// The suffix is appended to "http://${GCE_METADATA_HOST}/computeMetadata/v1/".
// The suffix may contain query parameters.
@ -275,11 +466,11 @@ func systemInfoSuggestsGCE() bool {
// and ok false. Subscribe blocks until fn returns a non-nil error or the value
// is deleted. Subscribe returns the error value returned from the last call to
// fn, which may be nil when ok == false.
func Subscribe(suffix string, fn func(v string, ok bool) error) error {
func (c *Client) Subscribe(suffix string, fn func(v string, ok bool) error) error {
const failedSubscribeSleep = time.Second * 5
// First check to see if the metadata value exists at all.
val, lastETag, err := getETag(subscribeClient, suffix)
val, lastETag, err := c.getETag(suffix)
if err != nil {
return err
}
@ -295,7 +486,7 @@ func Subscribe(suffix string, fn func(v string, ok bool) error) error {
suffix += "?wait_for_change=true&last_etag="
}
for {
val, etag, err := getETag(subscribeClient, suffix+url.QueryEscape(lastETag))
val, etag, err := c.getETag(suffix + url.QueryEscape(lastETag))
if err != nil {
if _, deleted := err.(NotDefinedError); !deleted {
time.Sleep(failedSubscribeSleep)
@ -310,128 +501,3 @@ func Subscribe(suffix string, fn func(v string, ok bool) error) error {
}
}
}
// ProjectID returns the current instance's project ID string.
func ProjectID() (string, error) { return projID.get() }
// NumericProjectID returns the current instance's numeric project ID.
func NumericProjectID() (string, error) { return projNum.get() }
// InternalIP returns the instance's primary internal IP address.
func InternalIP() (string, error) {
return getTrimmed("instance/network-interfaces/0/ip")
}
// ExternalIP returns the instance's primary external (public) IP address.
func ExternalIP() (string, error) {
return getTrimmed("instance/network-interfaces/0/access-configs/0/external-ip")
}
// Hostname returns the instance's hostname. This will be of the form
// "<instanceID>.c.<projID>.internal".
func Hostname() (string, error) {
return getTrimmed("instance/hostname")
}
// InstanceTags returns the list of user-defined instance tags,
// assigned when initially creating a GCE instance.
func InstanceTags() ([]string, error) {
var s []string
j, err := Get("instance/tags")
if err != nil {
return nil, err
}
if err := json.NewDecoder(strings.NewReader(j)).Decode(&s); err != nil {
return nil, err
}
return s, nil
}
// InstanceID returns the current VM's numeric instance ID.
func InstanceID() (string, error) {
return instID.get()
}
// InstanceName returns the current VM's instance ID string.
func InstanceName() (string, error) {
host, err := Hostname()
if err != nil {
return "", err
}
return strings.Split(host, ".")[0], nil
}
// Zone returns the current VM's zone, such as "us-central1-b".
func Zone() (string, error) {
zone, err := getTrimmed("instance/zone")
// zone is of the form "projects/<projNum>/zones/<zoneName>".
if err != nil {
return "", err
}
return zone[strings.LastIndex(zone, "/")+1:], nil
}
// InstanceAttributes returns the list of user-defined attributes,
// assigned when initially creating a GCE VM instance. The value of an
// attribute can be obtained with InstanceAttributeValue.
func InstanceAttributes() ([]string, error) { return lines("instance/attributes/") }
// ProjectAttributes returns the list of user-defined attributes
// applying to the project as a whole, not just this VM. The value of
// an attribute can be obtained with ProjectAttributeValue.
func ProjectAttributes() ([]string, error) { return lines("project/attributes/") }
func lines(suffix string) ([]string, error) {
j, err := Get(suffix)
if err != nil {
return nil, err
}
s := strings.Split(strings.TrimSpace(j), "\n")
for i := range s {
s[i] = strings.TrimSpace(s[i])
}
return s, nil
}
// InstanceAttributeValue returns the value of the provided VM
// instance attribute.
//
// If the requested attribute is not defined, the returned error will
// be of type NotDefinedError.
//
// InstanceAttributeValue may return ("", nil) if the attribute was
// defined to be the empty string.
func InstanceAttributeValue(attr string) (string, error) {
return Get("instance/attributes/" + attr)
}
// ProjectAttributeValue returns the value of the provided
// project attribute.
//
// If the requested attribute is not defined, the returned error will
// be of type NotDefinedError.
//
// ProjectAttributeValue may return ("", nil) if the attribute was
// defined to be the empty string.
func ProjectAttributeValue(attr string) (string, error) {
return Get("project/attributes/" + attr)
}
// Scopes returns the service account scopes for the given account.
// The account may be empty or the string "default" to use the instance's
// main account.
func Scopes(serviceAccount string) ([]string, error) {
if serviceAccount == "" {
serviceAccount = "default"
}
return lines("instance/service-accounts/" + serviceAccount + "/scopes")
}
func strsContains(ss []string, s string) bool {
for _, v := range ss {
if v == s {
return true
}
}
return false
}

View file

@ -717,12 +717,11 @@ type ZoneProperties struct {
// ZonesDeleteFuture an abstraction for monitoring and retrieving the results of a long-running operation.
type ZonesDeleteFuture struct {
azure.Future
req *http.Request
}
// Result returns the result of the asynchronous operation.
// If the operation has not completed it will return an error.
func (future ZonesDeleteFuture) Result(client ZonesClient) (ar autorest.Response, err error) {
func (future *ZonesDeleteFuture) Result(client ZonesClient) (ar autorest.Response, err error) {
var done bool
done, err = future.Done(client)
if err != nil {
@ -730,34 +729,9 @@ func (future ZonesDeleteFuture) Result(client ZonesClient) (ar autorest.Response
return
}
if !done {
return ar, azure.NewAsyncOpIncompleteError("dns.ZonesDeleteFuture")
}
if future.PollingMethod() == azure.PollingLocation {
ar, err = client.DeleteResponder(future.Response())
if err != nil {
err = autorest.NewErrorWithError(err, "dns.ZonesDeleteFuture", "Result", future.Response(), "Failure responding to request")
}
err = azure.NewAsyncOpIncompleteError("dns.ZonesDeleteFuture")
return
}
var req *http.Request
var resp *http.Response
if future.PollingURL() != "" {
req, err = http.NewRequest(http.MethodGet, future.PollingURL(), nil)
if err != nil {
return
}
} else {
req = autorest.ChangeToGet(future.req)
}
resp, err = autorest.SendWithSender(client, req,
autorest.DoRetryForStatusCodes(client.RetryAttempts, client.RetryDuration, autorest.StatusCodesForRetry...))
if err != nil {
err = autorest.NewErrorWithError(err, "dns.ZonesDeleteFuture", "Result", resp, "Failure sending request")
return
}
ar, err = client.DeleteResponder(resp)
if err != nil {
err = autorest.NewErrorWithError(err, "dns.ZonesDeleteFuture", "Result", resp, "Failure responding to request")
}
ar.Response = future.Response()
return
}

View file

@ -21,6 +21,7 @@ import (
"context"
"github.com/Azure/go-autorest/autorest"
"github.com/Azure/go-autorest/autorest/azure"
"github.com/Azure/go-autorest/autorest/validation"
"net/http"
)
@ -41,7 +42,7 @@ func NewRecordSetsClientWithBaseURI(baseURI string, subscriptionID string) Recor
// CreateOrUpdate creates or updates a record set within a DNS zone.
// Parameters:
// resourceGroupName - the name of the resource group.
// resourceGroupName - the name of the resource group. The name is case insensitive.
// zoneName - the name of the DNS zone (without a terminating dot).
// relativeRecordSetName - the name of the record set, relative to the name of the zone.
// recordType - the type of DNS record in this record set. Record sets of type SOA can be updated but not
@ -52,6 +53,14 @@ func NewRecordSetsClientWithBaseURI(baseURI string, subscriptionID string) Recor
// ifNoneMatch - set to '*' to allow a new record set to be created, but to prevent updating an existing record
// set. Other values will be ignored.
func (client RecordSetsClient) CreateOrUpdate(ctx context.Context, resourceGroupName string, zoneName string, relativeRecordSetName string, recordType RecordType, parameters RecordSet, ifMatch string, ifNoneMatch string) (result RecordSet, err error) {
if err := validation.Validate([]validation.Validation{
{TargetValue: resourceGroupName,
Constraints: []validation.Constraint{{Target: "resourceGroupName", Name: validation.MaxLength, Rule: 90, Chain: nil},
{Target: "resourceGroupName", Name: validation.MinLength, Rule: 1, Chain: nil},
{Target: "resourceGroupName", Name: validation.Pattern, Rule: `^[-\w\._\(\)]+$`, Chain: nil}}}}); err != nil {
return result, validation.NewError("dns.RecordSetsClient", "CreateOrUpdate", err.Error())
}
req, err := client.CreateOrUpdatePreparer(ctx, resourceGroupName, zoneName, relativeRecordSetName, recordType, parameters, ifMatch, ifNoneMatch)
if err != nil {
err = autorest.NewErrorWithError(err, "dns.RecordSetsClient", "CreateOrUpdate", nil, "Failure preparing request")
@ -128,7 +137,7 @@ func (client RecordSetsClient) CreateOrUpdateResponder(resp *http.Response) (res
// Delete deletes a record set from a DNS zone. This operation cannot be undone.
// Parameters:
// resourceGroupName - the name of the resource group.
// resourceGroupName - the name of the resource group. The name is case insensitive.
// zoneName - the name of the DNS zone (without a terminating dot).
// relativeRecordSetName - the name of the record set, relative to the name of the zone.
// recordType - the type of DNS record in this record set. Record sets of type SOA cannot be deleted (they are
@ -136,6 +145,14 @@ func (client RecordSetsClient) CreateOrUpdateResponder(resp *http.Response) (res
// ifMatch - the etag of the record set. Omit this value to always delete the current record set. Specify the
// last-seen etag value to prevent accidentally deleting any concurrent changes.
func (client RecordSetsClient) Delete(ctx context.Context, resourceGroupName string, zoneName string, relativeRecordSetName string, recordType RecordType, ifMatch string) (result autorest.Response, err error) {
if err := validation.Validate([]validation.Validation{
{TargetValue: resourceGroupName,
Constraints: []validation.Constraint{{Target: "resourceGroupName", Name: validation.MaxLength, Rule: 90, Chain: nil},
{Target: "resourceGroupName", Name: validation.MinLength, Rule: 1, Chain: nil},
{Target: "resourceGroupName", Name: validation.Pattern, Rule: `^[-\w\._\(\)]+$`, Chain: nil}}}}); err != nil {
return result, validation.NewError("dns.RecordSetsClient", "Delete", err.Error())
}
req, err := client.DeletePreparer(ctx, resourceGroupName, zoneName, relativeRecordSetName, recordType, ifMatch)
if err != nil {
err = autorest.NewErrorWithError(err, "dns.RecordSetsClient", "Delete", nil, "Failure preparing request")
@ -205,11 +222,19 @@ func (client RecordSetsClient) DeleteResponder(resp *http.Response) (result auto
// Get gets a record set.
// Parameters:
// resourceGroupName - the name of the resource group.
// resourceGroupName - the name of the resource group. The name is case insensitive.
// zoneName - the name of the DNS zone (without a terminating dot).
// relativeRecordSetName - the name of the record set, relative to the name of the zone.
// recordType - the type of DNS record in this record set.
func (client RecordSetsClient) Get(ctx context.Context, resourceGroupName string, zoneName string, relativeRecordSetName string, recordType RecordType) (result RecordSet, err error) {
if err := validation.Validate([]validation.Validation{
{TargetValue: resourceGroupName,
Constraints: []validation.Constraint{{Target: "resourceGroupName", Name: validation.MaxLength, Rule: 90, Chain: nil},
{Target: "resourceGroupName", Name: validation.MinLength, Rule: 1, Chain: nil},
{Target: "resourceGroupName", Name: validation.Pattern, Rule: `^[-\w\._\(\)]+$`, Chain: nil}}}}); err != nil {
return result, validation.NewError("dns.RecordSetsClient", "Get", err.Error())
}
req, err := client.GetPreparer(ctx, resourceGroupName, zoneName, relativeRecordSetName, recordType)
if err != nil {
err = autorest.NewErrorWithError(err, "dns.RecordSetsClient", "Get", nil, "Failure preparing request")
@ -276,13 +301,21 @@ func (client RecordSetsClient) GetResponder(resp *http.Response) (result RecordS
// ListByDNSZone lists all record sets in a DNS zone.
// Parameters:
// resourceGroupName - the name of the resource group.
// resourceGroupName - the name of the resource group. The name is case insensitive.
// zoneName - the name of the DNS zone (without a terminating dot).
// top - the maximum number of record sets to return. If not specified, returns up to 100 record sets.
// recordsetnamesuffix - the suffix label of the record set name that has to be used to filter the record set
// enumerations. If this parameter is specified, Enumeration will return only records that end with
// .<recordSetNameSuffix>
func (client RecordSetsClient) ListByDNSZone(ctx context.Context, resourceGroupName string, zoneName string, top *int32, recordsetnamesuffix string) (result RecordSetListResultPage, err error) {
if err := validation.Validate([]validation.Validation{
{TargetValue: resourceGroupName,
Constraints: []validation.Constraint{{Target: "resourceGroupName", Name: validation.MaxLength, Rule: 90, Chain: nil},
{Target: "resourceGroupName", Name: validation.MinLength, Rule: 1, Chain: nil},
{Target: "resourceGroupName", Name: validation.Pattern, Rule: `^[-\w\._\(\)]+$`, Chain: nil}}}}); err != nil {
return result, validation.NewError("dns.RecordSetsClient", "ListByDNSZone", err.Error())
}
result.fn = client.listByDNSZoneNextResults
req, err := client.ListByDNSZonePreparer(ctx, resourceGroupName, zoneName, top, recordsetnamesuffix)
if err != nil {
@ -381,7 +414,7 @@ func (client RecordSetsClient) ListByDNSZoneComplete(ctx context.Context, resour
// ListByType lists the record sets of a specified type in a DNS zone.
// Parameters:
// resourceGroupName - the name of the resource group.
// resourceGroupName - the name of the resource group. The name is case insensitive.
// zoneName - the name of the DNS zone (without a terminating dot).
// recordType - the type of record sets to enumerate.
// top - the maximum number of record sets to return. If not specified, returns up to 100 record sets.
@ -389,6 +422,14 @@ func (client RecordSetsClient) ListByDNSZoneComplete(ctx context.Context, resour
// enumerations. If this parameter is specified, Enumeration will return only records that end with
// .<recordSetNameSuffix>
func (client RecordSetsClient) ListByType(ctx context.Context, resourceGroupName string, zoneName string, recordType RecordType, top *int32, recordsetnamesuffix string) (result RecordSetListResultPage, err error) {
if err := validation.Validate([]validation.Validation{
{TargetValue: resourceGroupName,
Constraints: []validation.Constraint{{Target: "resourceGroupName", Name: validation.MaxLength, Rule: 90, Chain: nil},
{Target: "resourceGroupName", Name: validation.MinLength, Rule: 1, Chain: nil},
{Target: "resourceGroupName", Name: validation.Pattern, Rule: `^[-\w\._\(\)]+$`, Chain: nil}}}}); err != nil {
return result, validation.NewError("dns.RecordSetsClient", "ListByType", err.Error())
}
result.fn = client.listByTypeNextResults
req, err := client.ListByTypePreparer(ctx, resourceGroupName, zoneName, recordType, top, recordsetnamesuffix)
if err != nil {
@ -488,7 +529,7 @@ func (client RecordSetsClient) ListByTypeComplete(ctx context.Context, resourceG
// Update updates a record set within a DNS zone.
// Parameters:
// resourceGroupName - the name of the resource group.
// resourceGroupName - the name of the resource group. The name is case insensitive.
// zoneName - the name of the DNS zone (without a terminating dot).
// relativeRecordSetName - the name of the record set, relative to the name of the zone.
// recordType - the type of DNS record in this record set.
@ -496,6 +537,14 @@ func (client RecordSetsClient) ListByTypeComplete(ctx context.Context, resourceG
// ifMatch - the etag of the record set. Omit this value to always overwrite the current record set. Specify
// the last-seen etag value to prevent accidentally overwritting concurrent changes.
func (client RecordSetsClient) Update(ctx context.Context, resourceGroupName string, zoneName string, relativeRecordSetName string, recordType RecordType, parameters RecordSet, ifMatch string) (result RecordSet, err error) {
if err := validation.Validate([]validation.Validation{
{TargetValue: resourceGroupName,
Constraints: []validation.Constraint{{Target: "resourceGroupName", Name: validation.MaxLength, Rule: 90, Chain: nil},
{Target: "resourceGroupName", Name: validation.MinLength, Rule: 1, Chain: nil},
{Target: "resourceGroupName", Name: validation.Pattern, Rule: `^[-\w\._\(\)]+$`, Chain: nil}}}}); err != nil {
return result, validation.NewError("dns.RecordSetsClient", "Update", err.Error())
}
req, err := client.UpdatePreparer(ctx, resourceGroupName, zoneName, relativeRecordSetName, recordType, parameters, ifMatch)
if err != nil {
err = autorest.NewErrorWithError(err, "dns.RecordSetsClient", "Update", nil, "Failure preparing request")

View file

@ -21,6 +21,7 @@ import (
"context"
"github.com/Azure/go-autorest/autorest"
"github.com/Azure/go-autorest/autorest/azure"
"github.com/Azure/go-autorest/autorest/validation"
"net/http"
)
@ -41,7 +42,7 @@ func NewZonesClientWithBaseURI(baseURI string, subscriptionID string) ZonesClien
// CreateOrUpdate creates or updates a DNS zone. Does not modify DNS records within the zone.
// Parameters:
// resourceGroupName - the name of the resource group.
// resourceGroupName - the name of the resource group. The name is case insensitive.
// zoneName - the name of the DNS zone (without a terminating dot).
// parameters - parameters supplied to the CreateOrUpdate operation.
// ifMatch - the etag of the DNS zone. Omit this value to always overwrite the current zone. Specify the
@ -49,6 +50,14 @@ func NewZonesClientWithBaseURI(baseURI string, subscriptionID string) ZonesClien
// ifNoneMatch - set to '*' to allow a new DNS zone to be created, but to prevent updating an existing zone.
// Other values will be ignored.
func (client ZonesClient) CreateOrUpdate(ctx context.Context, resourceGroupName string, zoneName string, parameters Zone, ifMatch string, ifNoneMatch string) (result Zone, err error) {
if err := validation.Validate([]validation.Validation{
{TargetValue: resourceGroupName,
Constraints: []validation.Constraint{{Target: "resourceGroupName", Name: validation.MaxLength, Rule: 90, Chain: nil},
{Target: "resourceGroupName", Name: validation.MinLength, Rule: 1, Chain: nil},
{Target: "resourceGroupName", Name: validation.Pattern, Rule: `^[-\w\._\(\)]+$`, Chain: nil}}}}); err != nil {
return result, validation.NewError("dns.ZonesClient", "CreateOrUpdate", err.Error())
}
req, err := client.CreateOrUpdatePreparer(ctx, resourceGroupName, zoneName, parameters, ifMatch, ifNoneMatch)
if err != nil {
err = autorest.NewErrorWithError(err, "dns.ZonesClient", "CreateOrUpdate", nil, "Failure preparing request")
@ -124,11 +133,19 @@ func (client ZonesClient) CreateOrUpdateResponder(resp *http.Response) (result Z
// Delete deletes a DNS zone. WARNING: All DNS records in the zone will also be deleted. This operation cannot be
// undone.
// Parameters:
// resourceGroupName - the name of the resource group.
// resourceGroupName - the name of the resource group. The name is case insensitive.
// zoneName - the name of the DNS zone (without a terminating dot).
// ifMatch - the etag of the DNS zone. Omit this value to always delete the current zone. Specify the last-seen
// etag value to prevent accidentally deleting any concurrent changes.
func (client ZonesClient) Delete(ctx context.Context, resourceGroupName string, zoneName string, ifMatch string) (result ZonesDeleteFuture, err error) {
if err := validation.Validate([]validation.Validation{
{TargetValue: resourceGroupName,
Constraints: []validation.Constraint{{Target: "resourceGroupName", Name: validation.MaxLength, Rule: 90, Chain: nil},
{Target: "resourceGroupName", Name: validation.MinLength, Rule: 1, Chain: nil},
{Target: "resourceGroupName", Name: validation.Pattern, Rule: `^[-\w\._\(\)]+$`, Chain: nil}}}}); err != nil {
return result, validation.NewError("dns.ZonesClient", "Delete", err.Error())
}
req, err := client.DeletePreparer(ctx, resourceGroupName, zoneName, ifMatch)
if err != nil {
err = autorest.NewErrorWithError(err, "dns.ZonesClient", "Delete", nil, "Failure preparing request")
@ -172,15 +189,17 @@ func (client ZonesClient) DeletePreparer(ctx context.Context, resourceGroupName
// DeleteSender sends the Delete request. The method will close the
// http.Response Body if it receives an error.
func (client ZonesClient) DeleteSender(req *http.Request) (future ZonesDeleteFuture, err error) {
sender := autorest.DecorateSender(client, azure.DoRetryWithRegistration(client.Client))
future.Future = azure.NewFuture(req)
future.req = req
_, err = future.Done(sender)
var resp *http.Response
resp, err = autorest.SendWithSender(client, req,
azure.DoRetryWithRegistration(client.Client))
if err != nil {
return
}
err = autorest.Respond(future.Response(),
azure.WithErrorUnlessStatusCode(http.StatusOK, http.StatusAccepted, http.StatusNoContent))
err = autorest.Respond(resp, azure.WithErrorUnlessStatusCode(http.StatusOK, http.StatusAccepted, http.StatusNoContent))
if err != nil {
return
}
future.Future, err = azure.NewFutureFromResponse(resp)
return
}
@ -198,9 +217,17 @@ func (client ZonesClient) DeleteResponder(resp *http.Response) (result autorest.
// Get gets a DNS zone. Retrieves the zone properties, but not the record sets within the zone.
// Parameters:
// resourceGroupName - the name of the resource group.
// resourceGroupName - the name of the resource group. The name is case insensitive.
// zoneName - the name of the DNS zone (without a terminating dot).
func (client ZonesClient) Get(ctx context.Context, resourceGroupName string, zoneName string) (result Zone, err error) {
if err := validation.Validate([]validation.Validation{
{TargetValue: resourceGroupName,
Constraints: []validation.Constraint{{Target: "resourceGroupName", Name: validation.MaxLength, Rule: 90, Chain: nil},
{Target: "resourceGroupName", Name: validation.MinLength, Rule: 1, Chain: nil},
{Target: "resourceGroupName", Name: validation.Pattern, Rule: `^[-\w\._\(\)]+$`, Chain: nil}}}}); err != nil {
return result, validation.NewError("dns.ZonesClient", "Get", err.Error())
}
req, err := client.GetPreparer(ctx, resourceGroupName, zoneName)
if err != nil {
err = autorest.NewErrorWithError(err, "dns.ZonesClient", "Get", nil, "Failure preparing request")
@ -360,9 +387,17 @@ func (client ZonesClient) ListComplete(ctx context.Context, top *int32) (result
// ListByResourceGroup lists the DNS zones within a resource group.
// Parameters:
// resourceGroupName - the name of the resource group.
// resourceGroupName - the name of the resource group. The name is case insensitive.
// top - the maximum number of record sets to return. If not specified, returns up to 100 record sets.
func (client ZonesClient) ListByResourceGroup(ctx context.Context, resourceGroupName string, top *int32) (result ZoneListResultPage, err error) {
if err := validation.Validate([]validation.Validation{
{TargetValue: resourceGroupName,
Constraints: []validation.Constraint{{Target: "resourceGroupName", Name: validation.MaxLength, Rule: 90, Chain: nil},
{Target: "resourceGroupName", Name: validation.MinLength, Rule: 1, Chain: nil},
{Target: "resourceGroupName", Name: validation.Pattern, Rule: `^[-\w\._\(\)]+$`, Chain: nil}}}}); err != nil {
return result, validation.NewError("dns.ZonesClient", "ListByResourceGroup", err.Error())
}
result.fn = client.listByResourceGroupNextResults
req, err := client.ListByResourceGroupPreparer(ctx, resourceGroupName, top)
if err != nil {

View file

@ -18,4 +18,4 @@ package version
// Changes may cause incorrect behavior and will be lost if the code is regenerated.
// Number contains the semantic version of this SDK.
const Number = "v16.2.1"
const Number = "v19.1.0"

View file

@ -26,10 +26,10 @@ const (
// OAuthConfig represents the endpoints needed
// in OAuth operations
type OAuthConfig struct {
AuthorityEndpoint url.URL
AuthorizeEndpoint url.URL
TokenEndpoint url.URL
DeviceCodeEndpoint url.URL
AuthorityEndpoint url.URL `json:"authorityEndpoint"`
AuthorizeEndpoint url.URL `json:"authorizeEndpoint"`
TokenEndpoint url.URL `json:"tokenEndpoint"`
DeviceCodeEndpoint url.URL `json:"deviceCodeEndpoint"`
}
// IsZero returns true if the OAuthConfig object is zero-initialized.

View file

@ -22,8 +22,10 @@ import (
"crypto/x509"
"encoding/base64"
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"math"
"net"
"net/http"
"net/url"
@ -33,6 +35,7 @@ import (
"time"
"github.com/Azure/go-autorest/autorest/date"
"github.com/Azure/go-autorest/version"
"github.com/dgrijalva/jwt-go"
)
@ -59,6 +62,9 @@ const (
// msiEndpoint is the well known endpoint for getting MSI authentications tokens
msiEndpoint = "http://169.254.169.254/metadata/identity/oauth2/token"
// the default number of attempts to refresh an MSI authentication token
defaultMaxMSIRefreshAttempts = 5
)
// OAuthTokenProvider is an interface which should be implemented by an access token retriever
@ -136,6 +142,12 @@ func (t *Token) OAuthToken() string {
return t.AccessToken
}
// ServicePrincipalSecret is an interface that allows various secret mechanism to fill the form
// that is submitted when acquiring an oAuth token.
type ServicePrincipalSecret interface {
SetAuthenticationValues(spt *ServicePrincipalToken, values *url.Values) error
}
// ServicePrincipalNoSecret represents a secret type that contains no secret
// meaning it is not valid for fetching a fresh token. This is used by Manual
type ServicePrincipalNoSecret struct {
@ -147,15 +159,19 @@ func (noSecret *ServicePrincipalNoSecret) SetAuthenticationValues(spt *ServicePr
return fmt.Errorf("Manually created ServicePrincipalToken does not contain secret material to retrieve a new access token")
}
// ServicePrincipalSecret is an interface that allows various secret mechanism to fill the form
// that is submitted when acquiring an oAuth token.
type ServicePrincipalSecret interface {
SetAuthenticationValues(spt *ServicePrincipalToken, values *url.Values) error
// MarshalJSON implements the json.Marshaler interface.
func (noSecret ServicePrincipalNoSecret) MarshalJSON() ([]byte, error) {
type tokenType struct {
Type string `json:"type"`
}
return json.Marshal(tokenType{
Type: "ServicePrincipalNoSecret",
})
}
// ServicePrincipalTokenSecret implements ServicePrincipalSecret for client_secret type authorization.
type ServicePrincipalTokenSecret struct {
ClientSecret string
ClientSecret string `json:"value"`
}
// SetAuthenticationValues is a method of the interface ServicePrincipalSecret.
@ -165,49 +181,24 @@ func (tokenSecret *ServicePrincipalTokenSecret) SetAuthenticationValues(spt *Ser
return nil
}
// MarshalJSON implements the json.Marshaler interface.
func (tokenSecret ServicePrincipalTokenSecret) MarshalJSON() ([]byte, error) {
type tokenType struct {
Type string `json:"type"`
Value string `json:"value"`
}
return json.Marshal(tokenType{
Type: "ServicePrincipalTokenSecret",
Value: tokenSecret.ClientSecret,
})
}
// ServicePrincipalCertificateSecret implements ServicePrincipalSecret for generic RSA cert auth with signed JWTs.
type ServicePrincipalCertificateSecret struct {
Certificate *x509.Certificate
PrivateKey *rsa.PrivateKey
}
// ServicePrincipalMSISecret implements ServicePrincipalSecret for machines running the MSI Extension.
type ServicePrincipalMSISecret struct {
}
// ServicePrincipalUsernamePasswordSecret implements ServicePrincipalSecret for username and password auth.
type ServicePrincipalUsernamePasswordSecret struct {
Username string
Password string
}
// ServicePrincipalAuthorizationCodeSecret implements ServicePrincipalSecret for authorization code auth.
type ServicePrincipalAuthorizationCodeSecret struct {
ClientSecret string
AuthorizationCode string
RedirectURI string
}
// SetAuthenticationValues is a method of the interface ServicePrincipalSecret.
func (secret *ServicePrincipalAuthorizationCodeSecret) SetAuthenticationValues(spt *ServicePrincipalToken, v *url.Values) error {
v.Set("code", secret.AuthorizationCode)
v.Set("client_secret", secret.ClientSecret)
v.Set("redirect_uri", secret.RedirectURI)
return nil
}
// SetAuthenticationValues is a method of the interface ServicePrincipalSecret.
func (secret *ServicePrincipalUsernamePasswordSecret) SetAuthenticationValues(spt *ServicePrincipalToken, v *url.Values) error {
v.Set("username", secret.Username)
v.Set("password", secret.Password)
return nil
}
// SetAuthenticationValues is a method of the interface ServicePrincipalSecret.
func (msiSecret *ServicePrincipalMSISecret) SetAuthenticationValues(spt *ServicePrincipalToken, v *url.Values) error {
return nil
}
// SignJwt returns the JWT signed with the certificate's private key.
func (secret *ServicePrincipalCertificateSecret) SignJwt(spt *ServicePrincipalToken) (string, error) {
hasher := sha1.New()
@ -228,9 +219,9 @@ func (secret *ServicePrincipalCertificateSecret) SignJwt(spt *ServicePrincipalTo
token := jwt.New(jwt.SigningMethodRS256)
token.Header["x5t"] = thumbprint
token.Claims = jwt.MapClaims{
"aud": spt.oauthConfig.TokenEndpoint.String(),
"iss": spt.clientID,
"sub": spt.clientID,
"aud": spt.inner.OauthConfig.TokenEndpoint.String(),
"iss": spt.inner.ClientID,
"sub": spt.inner.ClientID,
"jti": base64.URLEncoding.EncodeToString(jti),
"nbf": time.Now().Unix(),
"exp": time.Now().Add(time.Hour * 24).Unix(),
@ -253,19 +244,151 @@ func (secret *ServicePrincipalCertificateSecret) SetAuthenticationValues(spt *Se
return nil
}
// MarshalJSON implements the json.Marshaler interface.
func (secret ServicePrincipalCertificateSecret) MarshalJSON() ([]byte, error) {
return nil, errors.New("marshalling ServicePrincipalCertificateSecret is not supported")
}
// ServicePrincipalMSISecret implements ServicePrincipalSecret for machines running the MSI Extension.
type ServicePrincipalMSISecret struct {
}
// SetAuthenticationValues is a method of the interface ServicePrincipalSecret.
func (msiSecret *ServicePrincipalMSISecret) SetAuthenticationValues(spt *ServicePrincipalToken, v *url.Values) error {
return nil
}
// MarshalJSON implements the json.Marshaler interface.
func (msiSecret ServicePrincipalMSISecret) MarshalJSON() ([]byte, error) {
return nil, errors.New("marshalling ServicePrincipalMSISecret is not supported")
}
// ServicePrincipalUsernamePasswordSecret implements ServicePrincipalSecret for username and password auth.
type ServicePrincipalUsernamePasswordSecret struct {
Username string `json:"username"`
Password string `json:"password"`
}
// SetAuthenticationValues is a method of the interface ServicePrincipalSecret.
func (secret *ServicePrincipalUsernamePasswordSecret) SetAuthenticationValues(spt *ServicePrincipalToken, v *url.Values) error {
v.Set("username", secret.Username)
v.Set("password", secret.Password)
return nil
}
// MarshalJSON implements the json.Marshaler interface.
func (secret ServicePrincipalUsernamePasswordSecret) MarshalJSON() ([]byte, error) {
type tokenType struct {
Type string `json:"type"`
Username string `json:"username"`
Password string `json:"password"`
}
return json.Marshal(tokenType{
Type: "ServicePrincipalUsernamePasswordSecret",
Username: secret.Username,
Password: secret.Password,
})
}
// ServicePrincipalAuthorizationCodeSecret implements ServicePrincipalSecret for authorization code auth.
type ServicePrincipalAuthorizationCodeSecret struct {
ClientSecret string `json:"value"`
AuthorizationCode string `json:"authCode"`
RedirectURI string `json:"redirect"`
}
// SetAuthenticationValues is a method of the interface ServicePrincipalSecret.
func (secret *ServicePrincipalAuthorizationCodeSecret) SetAuthenticationValues(spt *ServicePrincipalToken, v *url.Values) error {
v.Set("code", secret.AuthorizationCode)
v.Set("client_secret", secret.ClientSecret)
v.Set("redirect_uri", secret.RedirectURI)
return nil
}
// MarshalJSON implements the json.Marshaler interface.
func (secret ServicePrincipalAuthorizationCodeSecret) MarshalJSON() ([]byte, error) {
type tokenType struct {
Type string `json:"type"`
Value string `json:"value"`
AuthCode string `json:"authCode"`
Redirect string `json:"redirect"`
}
return json.Marshal(tokenType{
Type: "ServicePrincipalAuthorizationCodeSecret",
Value: secret.ClientSecret,
AuthCode: secret.AuthorizationCode,
Redirect: secret.RedirectURI,
})
}
// ServicePrincipalToken encapsulates a Token created for a Service Principal.
type ServicePrincipalToken struct {
token Token
secret ServicePrincipalSecret
oauthConfig OAuthConfig
clientID string
resource string
autoRefresh bool
inner servicePrincipalToken
refreshLock *sync.RWMutex
refreshWithin time.Duration
sender Sender
refreshCallbacks []TokenRefreshCallback
// MaxMSIRefreshAttempts is the maximum number of attempts to refresh an MSI token.
MaxMSIRefreshAttempts int
}
// MarshalTokenJSON returns the marshalled inner token.
func (spt ServicePrincipalToken) MarshalTokenJSON() ([]byte, error) {
return json.Marshal(spt.inner.Token)
}
// SetRefreshCallbacks replaces any existing refresh callbacks with the specified callbacks.
func (spt *ServicePrincipalToken) SetRefreshCallbacks(callbacks []TokenRefreshCallback) {
spt.refreshCallbacks = callbacks
}
// MarshalJSON implements the json.Marshaler interface.
func (spt ServicePrincipalToken) MarshalJSON() ([]byte, error) {
return json.Marshal(spt.inner)
}
// UnmarshalJSON implements the json.Unmarshaler interface.
func (spt *ServicePrincipalToken) UnmarshalJSON(data []byte) error {
// need to determine the token type
raw := map[string]interface{}{}
err := json.Unmarshal(data, &raw)
if err != nil {
return err
}
secret := raw["secret"].(map[string]interface{})
switch secret["type"] {
case "ServicePrincipalNoSecret":
spt.inner.Secret = &ServicePrincipalNoSecret{}
case "ServicePrincipalTokenSecret":
spt.inner.Secret = &ServicePrincipalTokenSecret{}
case "ServicePrincipalCertificateSecret":
return errors.New("unmarshalling ServicePrincipalCertificateSecret is not supported")
case "ServicePrincipalMSISecret":
return errors.New("unmarshalling ServicePrincipalMSISecret is not supported")
case "ServicePrincipalUsernamePasswordSecret":
spt.inner.Secret = &ServicePrincipalUsernamePasswordSecret{}
case "ServicePrincipalAuthorizationCodeSecret":
spt.inner.Secret = &ServicePrincipalAuthorizationCodeSecret{}
default:
return fmt.Errorf("unrecognized token type '%s'", secret["type"])
}
err = json.Unmarshal(data, &spt.inner)
if err != nil {
return err
}
spt.refreshLock = &sync.RWMutex{}
spt.sender = &http.Client{}
return nil
}
// internal type used for marshalling/unmarshalling
type servicePrincipalToken struct {
Token Token `json:"token"`
Secret ServicePrincipalSecret `json:"secret"`
OauthConfig OAuthConfig `json:"oauth"`
ClientID string `json:"clientID"`
Resource string `json:"resource"`
AutoRefresh bool `json:"autoRefresh"`
RefreshWithin time.Duration `json:"refreshWithin"`
}
func validateOAuthConfig(oac OAuthConfig) error {
@ -290,13 +413,15 @@ func NewServicePrincipalTokenWithSecret(oauthConfig OAuthConfig, id string, reso
return nil, fmt.Errorf("parameter 'secret' cannot be nil")
}
spt := &ServicePrincipalToken{
oauthConfig: oauthConfig,
secret: secret,
clientID: id,
resource: resource,
autoRefresh: true,
inner: servicePrincipalToken{
OauthConfig: oauthConfig,
Secret: secret,
ClientID: id,
Resource: resource,
AutoRefresh: true,
RefreshWithin: defaultRefresh,
},
refreshLock: &sync.RWMutex{},
refreshWithin: defaultRefresh,
sender: &http.Client{},
refreshCallbacks: callbacks,
}
@ -327,7 +452,39 @@ func NewServicePrincipalTokenFromManualToken(oauthConfig OAuthConfig, clientID s
return nil, err
}
spt.token = token
spt.inner.Token = token
return spt, nil
}
// NewServicePrincipalTokenFromManualTokenSecret creates a ServicePrincipalToken using the supplied token and secret
func NewServicePrincipalTokenFromManualTokenSecret(oauthConfig OAuthConfig, clientID string, resource string, token Token, secret ServicePrincipalSecret, callbacks ...TokenRefreshCallback) (*ServicePrincipalToken, error) {
if err := validateOAuthConfig(oauthConfig); err != nil {
return nil, err
}
if err := validateStringParam(clientID, "clientID"); err != nil {
return nil, err
}
if err := validateStringParam(resource, "resource"); err != nil {
return nil, err
}
if secret == nil {
return nil, fmt.Errorf("parameter 'secret' cannot be nil")
}
if token.IsZero() {
return nil, fmt.Errorf("parameter 'token' cannot be zero-initialized")
}
spt, err := NewServicePrincipalTokenWithSecret(
oauthConfig,
clientID,
resource,
secret,
callbacks...)
if err != nil {
return nil, err
}
spt.inner.Token = token
return spt, nil
}
@ -495,20 +652,23 @@ func newServicePrincipalTokenFromMSI(msiEndpoint, resource string, userAssignedI
msiEndpointURL.RawQuery = v.Encode()
spt := &ServicePrincipalToken{
oauthConfig: OAuthConfig{
inner: servicePrincipalToken{
OauthConfig: OAuthConfig{
TokenEndpoint: *msiEndpointURL,
},
secret: &ServicePrincipalMSISecret{},
resource: resource,
autoRefresh: true,
Secret: &ServicePrincipalMSISecret{},
Resource: resource,
AutoRefresh: true,
RefreshWithin: defaultRefresh,
},
refreshLock: &sync.RWMutex{},
refreshWithin: defaultRefresh,
sender: &http.Client{},
refreshCallbacks: callbacks,
MaxMSIRefreshAttempts: defaultMaxMSIRefreshAttempts,
}
if userAssignedID != nil {
spt.clientID = *userAssignedID
spt.inner.ClientID = *userAssignedID
}
return spt, nil
@ -543,12 +703,12 @@ func (spt *ServicePrincipalToken) EnsureFresh() error {
// EnsureFreshWithContext will refresh the token if it will expire within the refresh window (as set by
// RefreshWithin) and autoRefresh flag is on. This method is safe for concurrent use.
func (spt *ServicePrincipalToken) EnsureFreshWithContext(ctx context.Context) error {
if spt.autoRefresh && spt.token.WillExpireIn(spt.refreshWithin) {
if spt.inner.AutoRefresh && spt.inner.Token.WillExpireIn(spt.inner.RefreshWithin) {
// take the write lock then check to see if the token was already refreshed
spt.refreshLock.Lock()
defer spt.refreshLock.Unlock()
if spt.token.WillExpireIn(spt.refreshWithin) {
return spt.refreshInternal(ctx, spt.resource)
if spt.inner.Token.WillExpireIn(spt.inner.RefreshWithin) {
return spt.refreshInternal(ctx, spt.inner.Resource)
}
}
return nil
@ -558,7 +718,7 @@ func (spt *ServicePrincipalToken) EnsureFreshWithContext(ctx context.Context) er
func (spt *ServicePrincipalToken) InvokeRefreshCallbacks(token Token) error {
if spt.refreshCallbacks != nil {
for _, callback := range spt.refreshCallbacks {
err := callback(spt.token)
err := callback(spt.inner.Token)
if err != nil {
return fmt.Errorf("adal: TokenRefreshCallback handler failed. Error = '%v'", err)
}
@ -578,7 +738,7 @@ func (spt *ServicePrincipalToken) Refresh() error {
func (spt *ServicePrincipalToken) RefreshWithContext(ctx context.Context) error {
spt.refreshLock.Lock()
defer spt.refreshLock.Unlock()
return spt.refreshInternal(ctx, spt.resource)
return spt.refreshInternal(ctx, spt.inner.Resource)
}
// RefreshExchange refreshes the token, but for a different resource.
@ -596,7 +756,7 @@ func (spt *ServicePrincipalToken) RefreshExchangeWithContext(ctx context.Context
}
func (spt *ServicePrincipalToken) getGrantType() string {
switch spt.secret.(type) {
switch spt.inner.Secret.(type) {
case *ServicePrincipalUsernamePasswordSecret:
return OAuthGrantTypeUserPass
case *ServicePrincipalAuthorizationCodeSecret:
@ -615,22 +775,31 @@ func isIMDS(u url.URL) bool {
}
func (spt *ServicePrincipalToken) refreshInternal(ctx context.Context, resource string) error {
req, err := http.NewRequest(http.MethodPost, spt.oauthConfig.TokenEndpoint.String(), nil)
req, err := http.NewRequest(http.MethodPost, spt.inner.OauthConfig.TokenEndpoint.String(), nil)
if err != nil {
return fmt.Errorf("adal: Failed to build the refresh request. Error = '%v'", err)
}
req.Header.Add("User-Agent", version.UserAgent())
req = req.WithContext(ctx)
if !isIMDS(spt.oauthConfig.TokenEndpoint) {
if !isIMDS(spt.inner.OauthConfig.TokenEndpoint) {
v := url.Values{}
v.Set("client_id", spt.clientID)
v.Set("client_id", spt.inner.ClientID)
v.Set("resource", resource)
if spt.token.RefreshToken != "" {
if spt.inner.Token.RefreshToken != "" {
v.Set("grant_type", OAuthGrantTypeRefreshToken)
v.Set("refresh_token", spt.token.RefreshToken)
v.Set("refresh_token", spt.inner.Token.RefreshToken)
// web apps must specify client_secret when refreshing tokens
// see https://docs.microsoft.com/en-us/azure/active-directory/develop/active-directory-protocols-oauth-code#refreshing-the-access-tokens
if spt.getGrantType() == OAuthGrantTypeAuthorizationCode {
err := spt.inner.Secret.SetAuthenticationValues(spt, &v)
if err != nil {
return err
}
}
} else {
v.Set("grant_type", spt.getGrantType())
err := spt.secret.SetAuthenticationValues(spt, &v)
err := spt.inner.Secret.SetAuthenticationValues(spt, &v)
if err != nil {
return err
}
@ -643,14 +812,14 @@ func (spt *ServicePrincipalToken) refreshInternal(ctx context.Context, resource
req.Body = body
}
if _, ok := spt.secret.(*ServicePrincipalMSISecret); ok {
if _, ok := spt.inner.Secret.(*ServicePrincipalMSISecret); ok {
req.Method = http.MethodGet
req.Header.Set(metadataHeader, "true")
}
var resp *http.Response
if isIMDS(spt.oauthConfig.TokenEndpoint) {
resp, err = retry(spt.sender, req)
if isIMDS(spt.inner.OauthConfig.TokenEndpoint) {
resp, err = retryForIMDS(spt.sender, req, spt.MaxMSIRefreshAttempts)
} else {
resp, err = spt.sender.Do(req)
}
@ -684,12 +853,14 @@ func (spt *ServicePrincipalToken) refreshInternal(ctx context.Context, resource
return fmt.Errorf("adal: Failed to unmarshal the service principal token during refresh. Error = '%v' JSON = '%s'", err, string(rb))
}
spt.token = token
spt.inner.Token = token
return spt.InvokeRefreshCallbacks(token)
}
func retry(sender Sender, req *http.Request) (resp *http.Response, err error) {
// retry logic specific to retrieving a token from the IMDS endpoint
func retryForIMDS(sender Sender, req *http.Request, maxAttempts int) (resp *http.Response, err error) {
// copied from client.go due to circular dependency
retries := []int{
http.StatusRequestTimeout, // 408
http.StatusTooManyRequests, // 429
@ -698,8 +869,10 @@ func retry(sender Sender, req *http.Request) (resp *http.Response, err error) {
http.StatusServiceUnavailable, // 503
http.StatusGatewayTimeout, // 504
}
// Extra retry status codes requered
retries = append(retries, http.StatusNotFound,
// extra retry status codes specific to IMDS
retries = append(retries,
http.StatusNotFound,
http.StatusGone,
// all remaining 5xx
http.StatusNotImplemented,
http.StatusHTTPVersionNotSupported,
@ -709,36 +882,52 @@ func retry(sender Sender, req *http.Request) (resp *http.Response, err error) {
http.StatusNotExtended,
http.StatusNetworkAuthenticationRequired)
// see https://docs.microsoft.com/en-us/azure/active-directory/managed-service-identity/how-to-use-vm-token#retry-guidance
const maxDelay time.Duration = 60 * time.Second
attempt := 0
maxAttempts := 5
delay := time.Duration(0)
for attempt < maxAttempts {
resp, err = sender.Do(req)
// retry on temporary network errors, e.g. transient network failures.
if (err != nil && !isTemporaryNetworkError(err)) || (err != nil && resp == nil) || resp.StatusCode == http.StatusOK || !containsInt(retries, resp.StatusCode) {
// if we don't receive a response then assume we can't connect to the
// endpoint so we're likely not running on an Azure VM so don't retry.
if (err != nil && !isTemporaryNetworkError(err)) || resp == nil || resp.StatusCode == http.StatusOK || !containsInt(retries, resp.StatusCode) {
return
}
if !delay(resp, req.Context().Done()) {
select {
case <-time.After(time.Second):
// perform exponential backoff with a cap.
// must increment attempt before calculating delay.
attempt++
// the base value of 2 is the "delta backoff" as specified in the guidance doc
delay += (time.Duration(math.Pow(2, float64(attempt))) * time.Second)
if delay > maxDelay {
delay = maxDelay
}
select {
case <-time.After(delay):
// intentionally left blank
case <-req.Context().Done():
err = req.Context().Err()
return
}
}
}
return
}
// returns true if the specified error is a temporary network error or false if it's not.
// if the error doesn't implement the net.Error interface the return value is true.
func isTemporaryNetworkError(err error) bool {
if netErr, ok := err.(net.Error); ok && netErr.Temporary() {
if netErr, ok := err.(net.Error); !ok || (ok && netErr.Temporary()) {
return true
}
return false
}
// returns true if slice ints contains the value n
func containsInt(ints []int, n int) bool {
for _, i := range ints {
if i == n {
@ -748,31 +937,15 @@ func containsInt(ints []int, n int) bool {
return false
}
func delay(resp *http.Response, cancel <-chan struct{}) bool {
if resp == nil {
return false
}
retryAfter, _ := strconv.Atoi(resp.Header.Get("Retry-After"))
if resp.StatusCode == http.StatusTooManyRequests && retryAfter > 0 {
select {
case <-time.After(time.Duration(retryAfter) * time.Second):
return true
case <-cancel:
return false
}
}
return false
}
// SetAutoRefresh enables or disables automatic refreshing of stale tokens.
func (spt *ServicePrincipalToken) SetAutoRefresh(autoRefresh bool) {
spt.autoRefresh = autoRefresh
spt.inner.AutoRefresh = autoRefresh
}
// SetRefreshWithin sets the interval within which if the token will expire, EnsureFresh will
// refresh the token.
func (spt *ServicePrincipalToken) SetRefreshWithin(d time.Duration) {
spt.refreshWithin = d
spt.inner.RefreshWithin = d
return
}
@ -784,12 +957,12 @@ func (spt *ServicePrincipalToken) SetSender(s Sender) { spt.sender = s }
func (spt *ServicePrincipalToken) OAuthToken() string {
spt.refreshLock.RLock()
defer spt.refreshLock.RUnlock()
return spt.token.OAuthToken()
return spt.inner.Token.OAuthToken()
}
// Token returns a copy of the current token.
func (spt *ServicePrincipalToken) Token() Token {
spt.refreshLock.RLock()
defer spt.refreshLock.RUnlock()
return spt.token
return spt.inner.Token
}

View file

@ -245,7 +245,13 @@ func (f Future) PollingURL() string {
// It makes the final GET call to retrieve the resultant payload.
func (f Future) GetResult(sender autorest.Sender) (*http.Response, error) {
if f.pt.finalGetURL() == "" {
return nil, nil
// we can end up in this situation if the async operation returns a 200
// with no polling URLs. in that case return the response which should
// contain the JSON payload (only do this for successful terminal cases).
if lr := f.pt.latestResponse(); lr != nil && f.pt.hasSucceeded() {
return lr, nil
}
return nil, autorest.NewError("Future", "GetResult", "missing URL for retrieving result")
}
req, err := http.NewRequest(http.MethodGet, f.pt.finalGetURL(), nil)
if err != nil {
@ -342,6 +348,10 @@ func (pt *pollingTrackerBase) initializeState() error {
case http.StatusOK:
if ps := pt.getProvisioningState(); ps != nil {
pt.State = *ps
if pt.hasFailed() {
pt.updateErrorFromResponse()
return pt.pollingError()
}
} else {
pt.State = operationSucceeded
}
@ -358,6 +368,7 @@ func (pt *pollingTrackerBase) initializeState() error {
default:
pt.State = operationFailed
pt.updateErrorFromResponse()
return pt.pollingError()
}
return nil
}
@ -416,6 +427,7 @@ func (pt *pollingTrackerBase) pollForStatus(sender autorest.Sender) error {
// attempts to unmarshal a ServiceError type from the response body.
// if that fails then make a best attempt at creating something meaningful.
// NOTE: this assumes that the async operation has failed.
func (pt *pollingTrackerBase) updateErrorFromResponse() {
var err error
if pt.resp.ContentLength != 0 {
@ -425,8 +437,7 @@ func (pt *pollingTrackerBase) updateErrorFromResponse() {
re := respErr{}
defer pt.resp.Body.Close()
var b []byte
b, err = ioutil.ReadAll(pt.resp.Body)
if err != nil {
if b, err = ioutil.ReadAll(pt.resp.Body); err != nil {
goto Default
}
if err = json.Unmarshal(b, &re); err != nil {
@ -439,20 +450,29 @@ func (pt *pollingTrackerBase) updateErrorFromResponse() {
goto Default
}
}
if re.ServiceError != nil {
// the unmarshaller will ensure re.ServiceError is non-nil
// even if there was no content unmarshalled so check the code.
if re.ServiceError.Code != "" {
pt.Err = re.ServiceError
return
}
}
Default:
se := &ServiceError{
Code: fmt.Sprintf("HTTP status code %v", pt.resp.StatusCode),
Message: pt.resp.Status,
Code: pt.pollingStatus(),
Message: "The async operation failed.",
}
if err != nil {
se.InnerError = make(map[string]interface{})
se.InnerError["unmarshalError"] = err.Error()
}
// stick the response body into the error object in hopes
// it contains something useful to help diagnose the failure.
if len(pt.rawBody) > 0 {
se.AdditionalInfo = []map[string]interface{}{
pt.rawBody,
}
}
pt.Err = se
}

View file

@ -49,6 +49,7 @@ type ServiceError struct {
Target *string `json:"target"`
Details []map[string]interface{} `json:"details"`
InnerError map[string]interface{} `json:"innererror"`
AdditionalInfo []map[string]interface{} `json:"additionalInfo"`
}
func (se ServiceError) Error() string {
@ -74,6 +75,14 @@ func (se ServiceError) Error() string {
result += fmt.Sprintf(" InnerError=%v", string(d))
}
if se.AdditionalInfo != nil {
d, err := json.Marshal(se.AdditionalInfo)
if err != nil {
result += fmt.Sprintf(" AdditionalInfo=%v", se.AdditionalInfo)
}
result += fmt.Sprintf(" AdditionalInfo=%v", string(d))
}
return result
}
@ -91,6 +100,7 @@ func (se *ServiceError) UnmarshalJSON(b []byte) error {
Target *string `json:"target"`
Details []map[string]interface{} `json:"details"`
InnerError map[string]interface{} `json:"innererror"`
AdditionalInfo []map[string]interface{} `json:"additionalInfo"`
}
type serviceError2 struct {
@ -99,31 +109,33 @@ func (se *ServiceError) UnmarshalJSON(b []byte) error {
Target *string `json:"target"`
Details map[string]interface{} `json:"details"`
InnerError map[string]interface{} `json:"innererror"`
AdditionalInfo []map[string]interface{} `json:"additionalInfo"`
}
se1 := serviceError1{}
err := json.Unmarshal(b, &se1)
if err == nil {
se.populate(se1.Code, se1.Message, se1.Target, se1.Details, se1.InnerError)
se.populate(se1.Code, se1.Message, se1.Target, se1.Details, se1.InnerError, se1.AdditionalInfo)
return nil
}
se2 := serviceError2{}
err = json.Unmarshal(b, &se2)
if err == nil {
se.populate(se2.Code, se2.Message, se2.Target, nil, se2.InnerError)
se.populate(se2.Code, se2.Message, se2.Target, nil, se2.InnerError, se2.AdditionalInfo)
se.Details = append(se.Details, se2.Details)
return nil
}
return err
}
func (se *ServiceError) populate(code, message string, target *string, details []map[string]interface{}, inner map[string]interface{}) {
func (se *ServiceError) populate(code, message string, target *string, details []map[string]interface{}, inner map[string]interface{}, additional []map[string]interface{}) {
se.Code = code
se.Message = message
se.Target = target
se.Details = details
se.InnerError = inner
se.AdditionalInfo = additional
}
// RequestError describes an error response returned by Azure service.
@ -279,16 +291,29 @@ func WithErrorUnlessStatusCode(codes ...int) autorest.RespondDecorator {
resp.Body = ioutil.NopCloser(&b)
if decodeErr != nil {
return fmt.Errorf("autorest/azure: error response cannot be parsed: %q error: %v", b.String(), decodeErr)
} else if e.ServiceError == nil {
}
if e.ServiceError == nil {
// Check if error is unwrapped ServiceError
if err := json.Unmarshal(b.Bytes(), &e.ServiceError); err != nil || e.ServiceError.Message == "" {
if err := json.Unmarshal(b.Bytes(), &e.ServiceError); err != nil {
return err
}
}
if e.ServiceError.Message == "" {
// if we're here it means the returned error wasn't OData v4 compliant.
// try to unmarshal the body as raw JSON in hopes of getting something.
rawBody := map[string]interface{}{}
if err := json.Unmarshal(b.Bytes(), &rawBody); err != nil {
return err
}
e.ServiceError = &ServiceError{
Code: "Unknown",
Message: "Unknown service error",
}
if len(rawBody) > 0 {
e.ServiceError.Details = []map[string]interface{}{rawBody}
}
}
e.Response = resp
e.RequestID = ExtractRequestID(resp)
if e.StatusCode == nil {
e.StatusCode = resp.StatusCode

View file

@ -64,7 +64,7 @@ func DoRetryWithRegistration(client autorest.Client) autorest.SendDecorator {
}
}
}
return resp, fmt.Errorf("failed request: %s", err)
return resp, err
})
}
}

View file

@ -22,8 +22,11 @@ import (
"log"
"net/http"
"net/http/cookiejar"
"runtime"
"strings"
"time"
"github.com/Azure/go-autorest/logger"
"github.com/Azure/go-autorest/version"
)
const (
@ -41,15 +44,6 @@ const (
)
var (
// defaultUserAgent builds a string containing the Go version, system archityecture and OS,
// and the go-autorest version.
defaultUserAgent = fmt.Sprintf("Go/%s (%s-%s) go-autorest/%s",
runtime.Version(),
runtime.GOARCH,
runtime.GOOS,
Version(),
)
// StatusCodesForRetry are a defined group of status code for which the client will retry
StatusCodesForRetry = []int{
http.StatusRequestTimeout, // 408
@ -179,7 +173,7 @@ func NewClientWithUserAgent(ua string) Client {
PollingDuration: DefaultPollingDuration,
RetryAttempts: DefaultRetryAttempts,
RetryDuration: DefaultRetryDuration,
UserAgent: defaultUserAgent,
UserAgent: version.UserAgent(),
}
c.Sender = c.sender()
c.AddToUserAgent(ua)
@ -216,8 +210,17 @@ func (c Client) Do(r *http.Request) (*http.Response, error) {
}
return resp, NewErrorWithError(err, "autorest/Client", "Do", nil, "Preparing request failed")
}
logger.Instance.WriteRequest(r, logger.Filter{
Header: func(k string, v []string) (bool, []string) {
// remove the auth token from the log
if strings.EqualFold(k, "Authorization") || strings.EqualFold(k, "Ocp-Apim-Subscription-Key") {
v = []string{"**REDACTED**"}
}
return true, v
},
})
resp, err := SendWithSender(c.sender(), r)
logger.Instance.WriteResponse(resp, logger.Filter{})
Respond(resp, c.ByInspecting())
return resp, err
}

View file

@ -218,9 +218,10 @@ func IsTokenRefreshError(err error) bool {
return false
}
// IsTemporaryNetworkError returns true if the specified error is a temporary network error.
// IsTemporaryNetworkError returns true if the specified error is a temporary network error or false
// if it's not. If the error doesn't implement the net.Error interface the return value is true.
func IsTemporaryNetworkError(err error) bool {
if netErr, ok := err.(net.Error); ok && netErr.Temporary() {
if netErr, ok := err.(net.Error); !ok || (ok && netErr.Temporary()) {
return true
}
return false

View file

@ -0,0 +1,48 @@
package validation
// Copyright 2017 Microsoft Corporation
//
// 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.
import (
"fmt"
)
// Error is the type that's returned when the validation of an APIs arguments constraints fails.
type Error struct {
// PackageType is the package type of the object emitting the error. For types, the value
// matches that produced the the '%T' format specifier of the fmt package. For other elements,
// such as functions, it is just the package name (e.g., "autorest").
PackageType string
// Method is the name of the method raising the error.
Method string
// Message is the error message.
Message string
}
// Error returns a string containing the details of the validation failure.
func (e Error) Error() string {
return fmt.Sprintf("%s#%s: Invalid input: %s", e.PackageType, e.Method, e.Message)
}
// NewError creates a new Error object with the specified parameters.
// message is treated as a format string to which the optional args apply.
func NewError(packageType string, method string, message string, args ...interface{}) Error {
return Error{
PackageType: packageType,
Method: method,
Message: fmt.Sprintf(message, args...),
}
}

View file

@ -0,0 +1,408 @@
/*
Package validation provides methods for validating parameter value using reflection.
*/
package validation
// Copyright 2017 Microsoft Corporation
//
// 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.
import (
"fmt"
"reflect"
"regexp"
"strings"
)
// Constraint stores constraint name, target field name
// Rule and chain validations.
type Constraint struct {
// Target field name for validation.
Target string
// Constraint name e.g. minLength, MaxLength, Pattern, etc.
Name string
// Rule for constraint e.g. greater than 10, less than 5 etc.
Rule interface{}
// Chain Validations for struct type
Chain []Constraint
}
// Validation stores parameter-wise validation.
type Validation struct {
TargetValue interface{}
Constraints []Constraint
}
// Constraint list
const (
Empty = "Empty"
Null = "Null"
ReadOnly = "ReadOnly"
Pattern = "Pattern"
MaxLength = "MaxLength"
MinLength = "MinLength"
MaxItems = "MaxItems"
MinItems = "MinItems"
MultipleOf = "MultipleOf"
UniqueItems = "UniqueItems"
InclusiveMaximum = "InclusiveMaximum"
ExclusiveMaximum = "ExclusiveMaximum"
ExclusiveMinimum = "ExclusiveMinimum"
InclusiveMinimum = "InclusiveMinimum"
)
// Validate method validates constraints on parameter
// passed in validation array.
func Validate(m []Validation) error {
for _, item := range m {
v := reflect.ValueOf(item.TargetValue)
for _, constraint := range item.Constraints {
var err error
switch v.Kind() {
case reflect.Ptr:
err = validatePtr(v, constraint)
case reflect.String:
err = validateString(v, constraint)
case reflect.Struct:
err = validateStruct(v, constraint)
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
err = validateInt(v, constraint)
case reflect.Float32, reflect.Float64:
err = validateFloat(v, constraint)
case reflect.Array, reflect.Slice, reflect.Map:
err = validateArrayMap(v, constraint)
default:
err = createError(v, constraint, fmt.Sprintf("unknown type %v", v.Kind()))
}
if err != nil {
return err
}
}
}
return nil
}
func validateStruct(x reflect.Value, v Constraint, name ...string) error {
//Get field name from target name which is in format a.b.c
s := strings.Split(v.Target, ".")
f := x.FieldByName(s[len(s)-1])
if isZero(f) {
return createError(x, v, fmt.Sprintf("field %q doesn't exist", v.Target))
}
return Validate([]Validation{
{
TargetValue: getInterfaceValue(f),
Constraints: []Constraint{v},
},
})
}
func validatePtr(x reflect.Value, v Constraint) error {
if v.Name == ReadOnly {
if !x.IsNil() {
return createError(x.Elem(), v, "readonly parameter; must send as nil or empty in request")
}
return nil
}
if x.IsNil() {
return checkNil(x, v)
}
if v.Chain != nil {
return Validate([]Validation{
{
TargetValue: getInterfaceValue(x.Elem()),
Constraints: v.Chain,
},
})
}
return nil
}
func validateInt(x reflect.Value, v Constraint) error {
i := x.Int()
r, ok := toInt64(v.Rule)
if !ok {
return createError(x, v, fmt.Sprintf("rule must be integer value for %v constraint; got: %v", v.Name, v.Rule))
}
switch v.Name {
case MultipleOf:
if i%r != 0 {
return createError(x, v, fmt.Sprintf("value must be a multiple of %v", r))
}
case ExclusiveMinimum:
if i <= r {
return createError(x, v, fmt.Sprintf("value must be greater than %v", r))
}
case ExclusiveMaximum:
if i >= r {
return createError(x, v, fmt.Sprintf("value must be less than %v", r))
}
case InclusiveMinimum:
if i < r {
return createError(x, v, fmt.Sprintf("value must be greater than or equal to %v", r))
}
case InclusiveMaximum:
if i > r {
return createError(x, v, fmt.Sprintf("value must be less than or equal to %v", r))
}
default:
return createError(x, v, fmt.Sprintf("constraint %v is not applicable for type integer", v.Name))
}
return nil
}
func validateFloat(x reflect.Value, v Constraint) error {
f := x.Float()
r, ok := v.Rule.(float64)
if !ok {
return createError(x, v, fmt.Sprintf("rule must be float value for %v constraint; got: %v", v.Name, v.Rule))
}
switch v.Name {
case ExclusiveMinimum:
if f <= r {
return createError(x, v, fmt.Sprintf("value must be greater than %v", r))
}
case ExclusiveMaximum:
if f >= r {
return createError(x, v, fmt.Sprintf("value must be less than %v", r))
}
case InclusiveMinimum:
if f < r {
return createError(x, v, fmt.Sprintf("value must be greater than or equal to %v", r))
}
case InclusiveMaximum:
if f > r {
return createError(x, v, fmt.Sprintf("value must be less than or equal to %v", r))
}
default:
return createError(x, v, fmt.Sprintf("constraint %s is not applicable for type float", v.Name))
}
return nil
}
func validateString(x reflect.Value, v Constraint) error {
s := x.String()
switch v.Name {
case Empty:
if len(s) == 0 {
return checkEmpty(x, v)
}
case Pattern:
reg, err := regexp.Compile(v.Rule.(string))
if err != nil {
return createError(x, v, err.Error())
}
if !reg.MatchString(s) {
return createError(x, v, fmt.Sprintf("value doesn't match pattern %v", v.Rule))
}
case MaxLength:
if _, ok := v.Rule.(int); !ok {
return createError(x, v, fmt.Sprintf("rule must be integer value for %v constraint; got: %v", v.Name, v.Rule))
}
if len(s) > v.Rule.(int) {
return createError(x, v, fmt.Sprintf("value length must be less than or equal to %v", v.Rule))
}
case MinLength:
if _, ok := v.Rule.(int); !ok {
return createError(x, v, fmt.Sprintf("rule must be integer value for %v constraint; got: %v", v.Name, v.Rule))
}
if len(s) < v.Rule.(int) {
return createError(x, v, fmt.Sprintf("value length must be greater than or equal to %v", v.Rule))
}
case ReadOnly:
if len(s) > 0 {
return createError(reflect.ValueOf(s), v, "readonly parameter; must send as nil or empty in request")
}
default:
return createError(x, v, fmt.Sprintf("constraint %s is not applicable to string type", v.Name))
}
if v.Chain != nil {
return Validate([]Validation{
{
TargetValue: getInterfaceValue(x),
Constraints: v.Chain,
},
})
}
return nil
}
func validateArrayMap(x reflect.Value, v Constraint) error {
switch v.Name {
case Null:
if x.IsNil() {
return checkNil(x, v)
}
case Empty:
if x.IsNil() || x.Len() == 0 {
return checkEmpty(x, v)
}
case MaxItems:
if _, ok := v.Rule.(int); !ok {
return createError(x, v, fmt.Sprintf("rule must be integer for %v constraint; got: %v", v.Name, v.Rule))
}
if x.Len() > v.Rule.(int) {
return createError(x, v, fmt.Sprintf("maximum item limit is %v; got: %v", v.Rule, x.Len()))
}
case MinItems:
if _, ok := v.Rule.(int); !ok {
return createError(x, v, fmt.Sprintf("rule must be integer for %v constraint; got: %v", v.Name, v.Rule))
}
if x.Len() < v.Rule.(int) {
return createError(x, v, fmt.Sprintf("minimum item limit is %v; got: %v", v.Rule, x.Len()))
}
case UniqueItems:
if x.Kind() == reflect.Array || x.Kind() == reflect.Slice {
if !checkForUniqueInArray(x) {
return createError(x, v, fmt.Sprintf("all items in parameter %q must be unique; got:%v", v.Target, x))
}
} else if x.Kind() == reflect.Map {
if !checkForUniqueInMap(x) {
return createError(x, v, fmt.Sprintf("all items in parameter %q must be unique; got:%v", v.Target, x))
}
} else {
return createError(x, v, fmt.Sprintf("type must be array, slice or map for constraint %v; got: %v", v.Name, x.Kind()))
}
case ReadOnly:
if x.Len() != 0 {
return createError(x, v, "readonly parameter; must send as nil or empty in request")
}
case Pattern:
reg, err := regexp.Compile(v.Rule.(string))
if err != nil {
return createError(x, v, err.Error())
}
keys := x.MapKeys()
for _, k := range keys {
if !reg.MatchString(k.String()) {
return createError(k, v, fmt.Sprintf("map key doesn't match pattern %v", v.Rule))
}
}
default:
return createError(x, v, fmt.Sprintf("constraint %v is not applicable to array, slice and map type", v.Name))
}
if v.Chain != nil {
return Validate([]Validation{
{
TargetValue: getInterfaceValue(x),
Constraints: v.Chain,
},
})
}
return nil
}
func checkNil(x reflect.Value, v Constraint) error {
if _, ok := v.Rule.(bool); !ok {
return createError(x, v, fmt.Sprintf("rule must be bool value for %v constraint; got: %v", v.Name, v.Rule))
}
if v.Rule.(bool) {
return createError(x, v, "value can not be null; required parameter")
}
return nil
}
func checkEmpty(x reflect.Value, v Constraint) error {
if _, ok := v.Rule.(bool); !ok {
return createError(x, v, fmt.Sprintf("rule must be bool value for %v constraint; got: %v", v.Name, v.Rule))
}
if v.Rule.(bool) {
return createError(x, v, "value can not be null or empty; required parameter")
}
return nil
}
func checkForUniqueInArray(x reflect.Value) bool {
if x == reflect.Zero(reflect.TypeOf(x)) || x.Len() == 0 {
return false
}
arrOfInterface := make([]interface{}, x.Len())
for i := 0; i < x.Len(); i++ {
arrOfInterface[i] = x.Index(i).Interface()
}
m := make(map[interface{}]bool)
for _, val := range arrOfInterface {
if m[val] {
return false
}
m[val] = true
}
return true
}
func checkForUniqueInMap(x reflect.Value) bool {
if x == reflect.Zero(reflect.TypeOf(x)) || x.Len() == 0 {
return false
}
mapOfInterface := make(map[interface{}]interface{}, x.Len())
keys := x.MapKeys()
for _, k := range keys {
mapOfInterface[k.Interface()] = x.MapIndex(k).Interface()
}
m := make(map[interface{}]bool)
for _, val := range mapOfInterface {
if m[val] {
return false
}
m[val] = true
}
return true
}
func getInterfaceValue(x reflect.Value) interface{} {
if x.Kind() == reflect.Invalid {
return nil
}
return x.Interface()
}
func isZero(x interface{}) bool {
return x == reflect.Zero(reflect.TypeOf(x)).Interface()
}
func createError(x reflect.Value, v Constraint, err string) error {
return fmt.Errorf("autorest/validation: validation failed: parameter=%s constraint=%s value=%#v details: %s",
v.Target, v.Name, getInterfaceValue(x), err)
}
func toInt64(v interface{}) (int64, bool) {
if i64, ok := v.(int64); ok {
return i64, true
}
// older generators emit max constants as int, so if int64 fails fall back to int
if i32, ok := v.(int); ok {
return int64(i32), true
}
return 0, false
}
// NewErrorWithValidationError appends package type and method name in
// validation error.
//
// Deprecated: Please use validation.NewError() instead.
func NewErrorWithValidationError(err error, packageType, method string) error {
return NewError(packageType, method, err.Error())
}

View file

@ -1,5 +1,7 @@
package autorest
import "github.com/Azure/go-autorest/version"
// Copyright 2017 Microsoft Corporation
//
// Licensed under the Apache License, Version 2.0 (the "License");
@ -16,5 +18,5 @@ package autorest
// Version returns the semantic version (see http://semver.org).
func Version() string {
return "v10.9.0"
return version.Number
}

328
vendor/github.com/Azure/go-autorest/logger/logger.go generated vendored Normal file
View file

@ -0,0 +1,328 @@
package logger
// Copyright 2017 Microsoft Corporation
//
// 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.
import (
"bytes"
"fmt"
"io"
"io/ioutil"
"net/http"
"net/url"
"os"
"strings"
"sync"
"time"
)
// LevelType tells a logger the minimum level to log. When code reports a log entry,
// the LogLevel indicates the level of the log entry. The logger only records entries
// whose level is at least the level it was told to log. See the Log* constants.
// For example, if a logger is configured with LogError, then LogError, LogPanic,
// and LogFatal entries will be logged; lower level entries are ignored.
type LevelType uint32
const (
// LogNone tells a logger not to log any entries passed to it.
LogNone LevelType = iota
// LogFatal tells a logger to log all LogFatal entries passed to it.
LogFatal
// LogPanic tells a logger to log all LogPanic and LogFatal entries passed to it.
LogPanic
// LogError tells a logger to log all LogError, LogPanic and LogFatal entries passed to it.
LogError
// LogWarning tells a logger to log all LogWarning, LogError, LogPanic and LogFatal entries passed to it.
LogWarning
// LogInfo tells a logger to log all LogInfo, LogWarning, LogError, LogPanic and LogFatal entries passed to it.
LogInfo
// LogDebug tells a logger to log all LogDebug, LogInfo, LogWarning, LogError, LogPanic and LogFatal entries passed to it.
LogDebug
)
const (
logNone = "NONE"
logFatal = "FATAL"
logPanic = "PANIC"
logError = "ERROR"
logWarning = "WARNING"
logInfo = "INFO"
logDebug = "DEBUG"
logUnknown = "UNKNOWN"
)
// ParseLevel converts the specified string into the corresponding LevelType.
func ParseLevel(s string) (lt LevelType, err error) {
switch strings.ToUpper(s) {
case logFatal:
lt = LogFatal
case logPanic:
lt = LogPanic
case logError:
lt = LogError
case logWarning:
lt = LogWarning
case logInfo:
lt = LogInfo
case logDebug:
lt = LogDebug
default:
err = fmt.Errorf("bad log level '%s'", s)
}
return
}
// String implements the stringer interface for LevelType.
func (lt LevelType) String() string {
switch lt {
case LogNone:
return logNone
case LogFatal:
return logFatal
case LogPanic:
return logPanic
case LogError:
return logError
case LogWarning:
return logWarning
case LogInfo:
return logInfo
case LogDebug:
return logDebug
default:
return logUnknown
}
}
// Filter defines functions for filtering HTTP request/response content.
type Filter struct {
// URL returns a potentially modified string representation of a request URL.
URL func(u *url.URL) string
// Header returns a potentially modified set of values for the specified key.
// To completely exclude the header key/values return false.
Header func(key string, val []string) (bool, []string)
// Body returns a potentially modified request/response body.
Body func(b []byte) []byte
}
func (f Filter) processURL(u *url.URL) string {
if f.URL == nil {
return u.String()
}
return f.URL(u)
}
func (f Filter) processHeader(k string, val []string) (bool, []string) {
if f.Header == nil {
return true, val
}
return f.Header(k, val)
}
func (f Filter) processBody(b []byte) []byte {
if f.Body == nil {
return b
}
return f.Body(b)
}
// Writer defines methods for writing to a logging facility.
type Writer interface {
// Writeln writes the specified message with the standard log entry header and new-line character.
Writeln(level LevelType, message string)
// Writef writes the specified format specifier with the standard log entry header and no new-line character.
Writef(level LevelType, format string, a ...interface{})
// WriteRequest writes the specified HTTP request to the logger if the log level is greater than
// or equal to LogInfo. The request body, if set, is logged at level LogDebug or higher.
// Custom filters can be specified to exclude URL, header, and/or body content from the log.
// By default no request content is excluded.
WriteRequest(req *http.Request, filter Filter)
// WriteResponse writes the specified HTTP response to the logger if the log level is greater than
// or equal to LogInfo. The response body, if set, is logged at level LogDebug or higher.
// Custom filters can be specified to exclude URL, header, and/or body content from the log.
// By default no respone content is excluded.
WriteResponse(resp *http.Response, filter Filter)
}
// Instance is the default log writer initialized during package init.
// This can be replaced with a custom implementation as required.
var Instance Writer
// default log level
var logLevel = LogNone
// Level returns the value specified in AZURE_GO_AUTOREST_LOG_LEVEL.
// If no value was specified the default value is LogNone.
// Custom loggers can call this to retrieve the configured log level.
func Level() LevelType {
return logLevel
}
func init() {
// separated for testing purposes
initDefaultLogger()
}
func initDefaultLogger() {
// init with nilLogger so callers don't have to do a nil check on Default
Instance = nilLogger{}
llStr := strings.ToLower(os.Getenv("AZURE_GO_SDK_LOG_LEVEL"))
if llStr == "" {
return
}
var err error
logLevel, err = ParseLevel(llStr)
if err != nil {
fmt.Fprintf(os.Stderr, "go-autorest: failed to parse log level: %s\n", err.Error())
return
}
if logLevel == LogNone {
return
}
// default to stderr
dest := os.Stderr
lfStr := os.Getenv("AZURE_GO_SDK_LOG_FILE")
if strings.EqualFold(lfStr, "stdout") {
dest = os.Stdout
} else if lfStr != "" {
lf, err := os.Create(lfStr)
if err == nil {
dest = lf
} else {
fmt.Fprintf(os.Stderr, "go-autorest: failed to create log file, using stderr: %s\n", err.Error())
}
}
Instance = fileLogger{
logLevel: logLevel,
mu: &sync.Mutex{},
logFile: dest,
}
}
// the nil logger does nothing
type nilLogger struct{}
func (nilLogger) Writeln(LevelType, string) {}
func (nilLogger) Writef(LevelType, string, ...interface{}) {}
func (nilLogger) WriteRequest(*http.Request, Filter) {}
func (nilLogger) WriteResponse(*http.Response, Filter) {}
// A File is used instead of a Logger so the stream can be flushed after every write.
type fileLogger struct {
logLevel LevelType
mu *sync.Mutex // for synchronizing writes to logFile
logFile *os.File
}
func (fl fileLogger) Writeln(level LevelType, message string) {
fl.Writef(level, "%s\n", message)
}
func (fl fileLogger) Writef(level LevelType, format string, a ...interface{}) {
if fl.logLevel >= level {
fl.mu.Lock()
defer fl.mu.Unlock()
fmt.Fprintf(fl.logFile, "%s %s", entryHeader(level), fmt.Sprintf(format, a...))
fl.logFile.Sync()
}
}
func (fl fileLogger) WriteRequest(req *http.Request, filter Filter) {
if req == nil || fl.logLevel < LogInfo {
return
}
b := &bytes.Buffer{}
fmt.Fprintf(b, "%s REQUEST: %s %s\n", entryHeader(LogInfo), req.Method, filter.processURL(req.URL))
// dump headers
for k, v := range req.Header {
if ok, mv := filter.processHeader(k, v); ok {
fmt.Fprintf(b, "%s: %s\n", k, strings.Join(mv, ","))
}
}
if fl.shouldLogBody(req.Header, req.Body) {
// dump body
body, err := ioutil.ReadAll(req.Body)
if err == nil {
fmt.Fprintln(b, string(filter.processBody(body)))
if nc, ok := req.Body.(io.Seeker); ok {
// rewind to the beginning
nc.Seek(0, io.SeekStart)
} else {
// recreate the body
req.Body = ioutil.NopCloser(bytes.NewReader(body))
}
} else {
fmt.Fprintf(b, "failed to read body: %v\n", err)
}
}
fl.mu.Lock()
defer fl.mu.Unlock()
fmt.Fprint(fl.logFile, b.String())
fl.logFile.Sync()
}
func (fl fileLogger) WriteResponse(resp *http.Response, filter Filter) {
if resp == nil || fl.logLevel < LogInfo {
return
}
b := &bytes.Buffer{}
fmt.Fprintf(b, "%s RESPONSE: %d %s\n", entryHeader(LogInfo), resp.StatusCode, filter.processURL(resp.Request.URL))
// dump headers
for k, v := range resp.Header {
if ok, mv := filter.processHeader(k, v); ok {
fmt.Fprintf(b, "%s: %s\n", k, strings.Join(mv, ","))
}
}
if fl.shouldLogBody(resp.Header, resp.Body) {
// dump body
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err == nil {
fmt.Fprintln(b, string(filter.processBody(body)))
resp.Body = ioutil.NopCloser(bytes.NewReader(body))
} else {
fmt.Fprintf(b, "failed to read body: %v\n", err)
}
}
fl.mu.Lock()
defer fl.mu.Unlock()
fmt.Fprint(fl.logFile, b.String())
fl.logFile.Sync()
}
// returns true if the provided body should be included in the log
func (fl fileLogger) shouldLogBody(header http.Header, body io.ReadCloser) bool {
ct := header.Get("Content-Type")
return fl.logLevel >= LogDebug && body != nil && strings.Index(ct, "application/octet-stream") == -1
}
// creates standard header for log entries, it contains a timestamp and the log level
func entryHeader(level LevelType) string {
// this format provides a fixed number of digits so the size of the timestamp is constant
return fmt.Sprintf("(%s) %s:", time.Now().Format("2006-01-02T15:04:05.0000000Z07:00"), level.String())
}

37
vendor/github.com/Azure/go-autorest/version/version.go generated vendored Normal file
View file

@ -0,0 +1,37 @@
package version
// Copyright 2017 Microsoft Corporation
//
// 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.
import (
"fmt"
"runtime"
)
// Number contains the semantic version of this SDK.
const Number = "v10.15.2"
var (
userAgent = fmt.Sprintf("Go/%s (%s-%s) go-autorest/%s",
runtime.Version(),
runtime.GOARCH,
runtime.GOOS,
Number,
)
)
// UserAgent returns a string containing the Go version, system archityecture and OS, and the go-autorest version.
func UserAgent() string {
return userAgent
}

View file

@ -91,6 +91,6 @@ func (c *Client) AddDebugHandlers() {
return
}
c.Handlers.Send.PushFrontNamed(request.NamedHandler{Name: "awssdk.client.LogRequest", Fn: logRequest})
c.Handlers.Send.PushBackNamed(request.NamedHandler{Name: "awssdk.client.LogResponse", Fn: logResponse})
c.Handlers.Send.PushFrontNamed(LogHTTPRequestHandler)
c.Handlers.Send.PushBackNamed(LogHTTPResponseHandler)
}

View file

@ -44,12 +44,22 @@ func (reader *teeReaderCloser) Close() error {
return reader.Source.Close()
}
// LogHTTPRequestHandler is a SDK request handler to log the HTTP request sent
// to a service. Will include the HTTP request body if the LogLevel of the
// request matches LogDebugWithHTTPBody.
var LogHTTPRequestHandler = request.NamedHandler{
Name: "awssdk.client.LogRequest",
Fn: logRequest,
}
func logRequest(r *request.Request) {
logBody := r.Config.LogLevel.Matches(aws.LogDebugWithHTTPBody)
bodySeekable := aws.IsReaderSeekable(r.Body)
dumpedBody, err := httputil.DumpRequestOut(r.HTTPRequest, logBody)
b, err := httputil.DumpRequestOut(r.HTTPRequest, logBody)
if err != nil {
r.Config.Logger.Log(fmt.Sprintf(logReqErrMsg, r.ClientInfo.ServiceName, r.Operation.Name, err))
r.Config.Logger.Log(fmt.Sprintf(logReqErrMsg,
r.ClientInfo.ServiceName, r.Operation.Name, err))
return
}
@ -63,7 +73,28 @@ func logRequest(r *request.Request) {
r.ResetBody()
}
r.Config.Logger.Log(fmt.Sprintf(logReqMsg, r.ClientInfo.ServiceName, r.Operation.Name, string(dumpedBody)))
r.Config.Logger.Log(fmt.Sprintf(logReqMsg,
r.ClientInfo.ServiceName, r.Operation.Name, string(b)))
}
// LogHTTPRequestHeaderHandler is a SDK request handler to log the HTTP request sent
// to a service. Will only log the HTTP request's headers. The request payload
// will not be read.
var LogHTTPRequestHeaderHandler = request.NamedHandler{
Name: "awssdk.client.LogRequestHeader",
Fn: logRequestHeader,
}
func logRequestHeader(r *request.Request) {
b, err := httputil.DumpRequestOut(r.HTTPRequest, false)
if err != nil {
r.Config.Logger.Log(fmt.Sprintf(logReqErrMsg,
r.ClientInfo.ServiceName, r.Operation.Name, err))
return
}
r.Config.Logger.Log(fmt.Sprintf(logReqMsg,
r.ClientInfo.ServiceName, r.Operation.Name, string(b)))
}
const logRespMsg = `DEBUG: Response %s/%s Details:
@ -76,27 +107,44 @@ const logRespErrMsg = `DEBUG ERROR: Response %s/%s:
%s
-----------------------------------------------------`
// LogHTTPResponseHandler is a SDK request handler to log the HTTP response
// received from a service. Will include the HTTP response body if the LogLevel
// of the request matches LogDebugWithHTTPBody.
var LogHTTPResponseHandler = request.NamedHandler{
Name: "awssdk.client.LogResponse",
Fn: logResponse,
}
func logResponse(r *request.Request) {
lw := &logWriter{r.Config.Logger, bytes.NewBuffer(nil)}
logBody := r.Config.LogLevel.Matches(aws.LogDebugWithHTTPBody)
if logBody {
r.HTTPResponse.Body = &teeReaderCloser{
Reader: io.TeeReader(r.HTTPResponse.Body, lw),
Source: r.HTTPResponse.Body,
}
}
handlerFn := func(req *request.Request) {
body, err := httputil.DumpResponse(req.HTTPResponse, false)
b, err := httputil.DumpResponse(req.HTTPResponse, false)
if err != nil {
lw.Logger.Log(fmt.Sprintf(logRespErrMsg, req.ClientInfo.ServiceName, req.Operation.Name, err))
lw.Logger.Log(fmt.Sprintf(logRespErrMsg,
req.ClientInfo.ServiceName, req.Operation.Name, err))
return
}
lw.Logger.Log(fmt.Sprintf(logRespMsg,
req.ClientInfo.ServiceName, req.Operation.Name, string(b)))
if logBody {
b, err := ioutil.ReadAll(lw.buf)
if err != nil {
lw.Logger.Log(fmt.Sprintf(logRespErrMsg, req.ClientInfo.ServiceName, req.Operation.Name, err))
lw.Logger.Log(fmt.Sprintf(logRespErrMsg,
req.ClientInfo.ServiceName, req.Operation.Name, err))
return
}
lw.Logger.Log(fmt.Sprintf(logRespMsg, req.ClientInfo.ServiceName, req.Operation.Name, string(body)))
if req.Config.LogLevel.Matches(aws.LogDebugWithHTTPBody) {
lw.Logger.Log(string(b))
}
}
@ -110,3 +158,27 @@ func logResponse(r *request.Request) {
Name: handlerName, Fn: handlerFn,
})
}
// LogHTTPResponseHeaderHandler is a SDK request handler to log the HTTP
// response received from a service. Will only log the HTTP response's headers.
// The response payload will not be read.
var LogHTTPResponseHeaderHandler = request.NamedHandler{
Name: "awssdk.client.LogResponseHeader",
Fn: logResponseHeader,
}
func logResponseHeader(r *request.Request) {
if r.Config.Logger == nil {
return
}
b, err := httputil.DumpResponse(r.HTTPResponse, false)
if err != nil {
r.Config.Logger.Log(fmt.Sprintf(logRespErrMsg,
r.ClientInfo.ServiceName, r.Operation.Name, err))
return
}
r.Config.Logger.Log(fmt.Sprintf(logRespMsg,
r.ClientInfo.ServiceName, r.Operation.Name, string(b)))
}

View file

@ -3,6 +3,7 @@ package metadata
// ClientInfo wraps immutable data from the client.Client structure.
type ClientInfo struct {
ServiceName string
ServiceID string
APIVersion string
Endpoint string
SigningName string

View file

@ -158,13 +158,14 @@ func (e *Expiry) SetExpiration(expiration time.Time, window time.Duration) {
// IsExpired returns if the credentials are expired.
func (e *Expiry) IsExpired() bool {
if e.CurrentTime == nil {
e.CurrentTime = time.Now
curTime := e.CurrentTime
if curTime == nil {
curTime = time.Now
}
return e.expiration.Before(e.CurrentTime())
return e.expiration.Before(curTime())
}
// A Credentials provides synchronous safe retrieval of AWS credentials Value.
// A Credentials provides concurrency safe retrieval of AWS credentials Value.
// Credentials will cache the credentials value until they expire. Once the value
// expires the next Get will attempt to retrieve valid credentials.
//
@ -178,7 +179,8 @@ func (e *Expiry) IsExpired() bool {
type Credentials struct {
creds Value
forceRefresh bool
m sync.Mutex
m sync.RWMutex
provider Provider
}
@ -201,6 +203,17 @@ func NewCredentials(provider Provider) *Credentials {
// If Credentials.Expire() was called the credentials Value will be force
// expired, and the next call to Get() will cause them to be refreshed.
func (c *Credentials) Get() (Value, error) {
// Check the cached credentials first with just the read lock.
c.m.RLock()
if !c.isExpired() {
creds := c.creds
c.m.RUnlock()
return creds, nil
}
c.m.RUnlock()
// Credentials are expired need to retrieve the credentials taking the full
// lock.
c.m.Lock()
defer c.m.Unlock()
@ -234,8 +247,8 @@ func (c *Credentials) Expire() {
// If the Credentials were forced to be expired with Expire() this will
// reflect that override.
func (c *Credentials) IsExpired() bool {
c.m.Lock()
defer c.m.Unlock()
c.m.RLock()
defer c.m.RUnlock()
return c.isExpired()
}

View file

@ -4,7 +4,6 @@ import (
"bufio"
"encoding/json"
"fmt"
"path"
"strings"
"time"
@ -12,6 +11,7 @@ import (
"github.com/aws/aws-sdk-go/aws/client"
"github.com/aws/aws-sdk-go/aws/credentials"
"github.com/aws/aws-sdk-go/aws/ec2metadata"
"github.com/aws/aws-sdk-go/internal/sdkuri"
)
// ProviderName provides a name of EC2Role provider
@ -125,7 +125,7 @@ type ec2RoleCredRespBody struct {
Message string
}
const iamSecurityCredsPath = "/iam/security-credentials"
const iamSecurityCredsPath = "iam/security-credentials/"
// requestCredList requests a list of credentials from the EC2 service.
// If there are no credentials, or there is an error making or receiving the request
@ -153,7 +153,7 @@ func requestCredList(client *ec2metadata.EC2Metadata) ([]string, error) {
// If the credentials cannot be found, or there is an error reading the response
// and error will be returned.
func requestCred(client *ec2metadata.EC2Metadata, credsName string) (ec2RoleCredRespBody, error) {
resp, err := client.GetMetadata(path.Join(iamSecurityCredsPath, credsName))
resp, err := client.GetMetadata(sdkuri.PathJoin(iamSecurityCredsPath, credsName))
if err != nil {
return ec2RoleCredRespBody{},
awserr.New("EC2RoleRequestError",

46
vendor/github.com/aws/aws-sdk-go/aws/csm/doc.go generated vendored Normal file
View file

@ -0,0 +1,46 @@
// Package csm provides Client Side Monitoring (CSM) which enables sending metrics
// via UDP connection. Using the Start function will enable the reporting of
// metrics on a given port. If Start is called, with different parameters, again,
// a panic will occur.
//
// Pause can be called to pause any metrics publishing on a given port. Sessions
// that have had their handlers modified via InjectHandlers may still be used.
// However, the handlers will act as a no-op meaning no metrics will be published.
//
// Example:
// r, err := csm.Start("clientID", ":31000")
// if err != nil {
// panic(fmt.Errorf("failed starting CSM: %v", err))
// }
//
// sess, err := session.NewSession(&aws.Config{})
// if err != nil {
// panic(fmt.Errorf("failed loading session: %v", err))
// }
//
// r.InjectHandlers(&sess.Handlers)
//
// client := s3.New(sess)
// resp, err := client.GetObject(&s3.GetObjectInput{
// Bucket: aws.String("bucket"),
// Key: aws.String("key"),
// })
//
// // Will pause monitoring
// r.Pause()
// resp, err = client.GetObject(&s3.GetObjectInput{
// Bucket: aws.String("bucket"),
// Key: aws.String("key"),
// })
//
// // Resume monitoring
// r.Continue()
//
// Start returns a Reporter that is used to enable or disable monitoring. If
// access to the Reporter is required later, calling Get will return the Reporter
// singleton.
//
// Example:
// r := csm.Get()
// r.Continue()
package csm

67
vendor/github.com/aws/aws-sdk-go/aws/csm/enable.go generated vendored Normal file
View file

@ -0,0 +1,67 @@
package csm
import (
"fmt"
"sync"
)
var (
lock sync.Mutex
)
// Client side metric handler names
const (
APICallMetricHandlerName = "awscsm.SendAPICallMetric"
APICallAttemptMetricHandlerName = "awscsm.SendAPICallAttemptMetric"
)
// Start will start the a long running go routine to capture
// client side metrics. Calling start multiple time will only
// start the metric listener once and will panic if a different
// client ID or port is passed in.
//
// Example:
// r, err := csm.Start("clientID", "127.0.0.1:8094")
// if err != nil {
// panic(fmt.Errorf("expected no error, but received %v", err))
// }
// sess := session.NewSession()
// r.InjectHandlers(sess.Handlers)
//
// svc := s3.New(sess)
// out, err := svc.GetObject(&s3.GetObjectInput{
// Bucket: aws.String("bucket"),
// Key: aws.String("key"),
// })
func Start(clientID string, url string) (*Reporter, error) {
lock.Lock()
defer lock.Unlock()
if sender == nil {
sender = newReporter(clientID, url)
} else {
if sender.clientID != clientID {
panic(fmt.Errorf("inconsistent client IDs. %q was expected, but received %q", sender.clientID, clientID))
}
if sender.url != url {
panic(fmt.Errorf("inconsistent URLs. %q was expected, but received %q", sender.url, url))
}
}
if err := connect(url); err != nil {
sender = nil
return nil, err
}
return sender, nil
}
// Get will return a reporter if one exists, if one does not exist, nil will
// be returned.
func Get() *Reporter {
lock.Lock()
defer lock.Unlock()
return sender
}

51
vendor/github.com/aws/aws-sdk-go/aws/csm/metric.go generated vendored Normal file
View file

@ -0,0 +1,51 @@
package csm
import (
"strconv"
"time"
)
type metricTime time.Time
func (t metricTime) MarshalJSON() ([]byte, error) {
ns := time.Duration(time.Time(t).UnixNano())
return []byte(strconv.FormatInt(int64(ns/time.Millisecond), 10)), nil
}
type metric struct {
ClientID *string `json:"ClientId,omitempty"`
API *string `json:"Api,omitempty"`
Service *string `json:"Service,omitempty"`
Timestamp *metricTime `json:"Timestamp,omitempty"`
Type *string `json:"Type,omitempty"`
Version *int `json:"Version,omitempty"`
AttemptCount *int `json:"AttemptCount,omitempty"`
Latency *int `json:"Latency,omitempty"`
Fqdn *string `json:"Fqdn,omitempty"`
UserAgent *string `json:"UserAgent,omitempty"`
AttemptLatency *int `json:"AttemptLatency,omitempty"`
SessionToken *string `json:"SessionToken,omitempty"`
Region *string `json:"Region,omitempty"`
AccessKey *string `json:"AccessKey,omitempty"`
HTTPStatusCode *int `json:"HttpStatusCode,omitempty"`
XAmzID2 *string `json:"XAmzId2,omitempty"`
XAmzRequestID *string `json:"XAmznRequestId,omitempty"`
AWSException *string `json:"AwsException,omitempty"`
AWSExceptionMessage *string `json:"AwsExceptionMessage,omitempty"`
SDKException *string `json:"SdkException,omitempty"`
SDKExceptionMessage *string `json:"SdkExceptionMessage,omitempty"`
DestinationIP *string `json:"DestinationIp,omitempty"`
ConnectionReused *int `json:"ConnectionReused,omitempty"`
AcquireConnectionLatency *int `json:"AcquireConnectionLatency,omitempty"`
ConnectLatency *int `json:"ConnectLatency,omitempty"`
RequestLatency *int `json:"RequestLatency,omitempty"`
DNSLatency *int `json:"DnsLatency,omitempty"`
TCPLatency *int `json:"TcpLatency,omitempty"`
SSLLatency *int `json:"SslLatency,omitempty"`
}

View file

@ -0,0 +1,54 @@
package csm
import (
"sync/atomic"
)
const (
runningEnum = iota
pausedEnum
)
var (
// MetricsChannelSize of metrics to hold in the channel
MetricsChannelSize = 100
)
type metricChan struct {
ch chan metric
paused int64
}
func newMetricChan(size int) metricChan {
return metricChan{
ch: make(chan metric, size),
}
}
func (ch *metricChan) Pause() {
atomic.StoreInt64(&ch.paused, pausedEnum)
}
func (ch *metricChan) Continue() {
atomic.StoreInt64(&ch.paused, runningEnum)
}
func (ch *metricChan) IsPaused() bool {
v := atomic.LoadInt64(&ch.paused)
return v == pausedEnum
}
// Push will push metrics to the metric channel if the channel
// is not paused
func (ch *metricChan) Push(m metric) bool {
if ch.IsPaused() {
return false
}
select {
case ch.ch <- m:
return true
default:
return false
}
}

231
vendor/github.com/aws/aws-sdk-go/aws/csm/reporter.go generated vendored Normal file
View file

@ -0,0 +1,231 @@
package csm
import (
"encoding/json"
"net"
"time"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/awserr"
"github.com/aws/aws-sdk-go/aws/request"
)
const (
// DefaultPort is used when no port is specified
DefaultPort = "31000"
)
// Reporter will gather metrics of API requests made and
// send those metrics to the CSM endpoint.
type Reporter struct {
clientID string
url string
conn net.Conn
metricsCh metricChan
done chan struct{}
}
var (
sender *Reporter
)
func connect(url string) error {
const network = "udp"
if err := sender.connect(network, url); err != nil {
return err
}
if sender.done == nil {
sender.done = make(chan struct{})
go sender.start()
}
return nil
}
func newReporter(clientID, url string) *Reporter {
return &Reporter{
clientID: clientID,
url: url,
metricsCh: newMetricChan(MetricsChannelSize),
}
}
func (rep *Reporter) sendAPICallAttemptMetric(r *request.Request) {
if rep == nil {
return
}
now := time.Now()
creds, _ := r.Config.Credentials.Get()
m := metric{
ClientID: aws.String(rep.clientID),
API: aws.String(r.Operation.Name),
Service: aws.String(r.ClientInfo.ServiceID),
Timestamp: (*metricTime)(&now),
UserAgent: aws.String(r.HTTPRequest.Header.Get("User-Agent")),
Region: r.Config.Region,
Type: aws.String("ApiCallAttempt"),
Version: aws.Int(1),
XAmzRequestID: aws.String(r.RequestID),
AttemptCount: aws.Int(r.RetryCount + 1),
AttemptLatency: aws.Int(int(now.Sub(r.AttemptTime).Nanoseconds() / int64(time.Millisecond))),
AccessKey: aws.String(creds.AccessKeyID),
}
if r.HTTPResponse != nil {
m.HTTPStatusCode = aws.Int(r.HTTPResponse.StatusCode)
}
if r.Error != nil {
if awserr, ok := r.Error.(awserr.Error); ok {
setError(&m, awserr)
}
}
rep.metricsCh.Push(m)
}
func setError(m *metric, err awserr.Error) {
msg := err.Error()
code := err.Code()
switch code {
case "RequestError",
"SerializationError",
request.CanceledErrorCode:
m.SDKException = &code
m.SDKExceptionMessage = &msg
default:
m.AWSException = &code
m.AWSExceptionMessage = &msg
}
}
func (rep *Reporter) sendAPICallMetric(r *request.Request) {
if rep == nil {
return
}
now := time.Now()
m := metric{
ClientID: aws.String(rep.clientID),
API: aws.String(r.Operation.Name),
Service: aws.String(r.ClientInfo.ServiceID),
Timestamp: (*metricTime)(&now),
Type: aws.String("ApiCall"),
AttemptCount: aws.Int(r.RetryCount + 1),
Latency: aws.Int(int(time.Now().Sub(r.Time) / time.Millisecond)),
XAmzRequestID: aws.String(r.RequestID),
}
// TODO: Probably want to figure something out for logging dropped
// metrics
rep.metricsCh.Push(m)
}
func (rep *Reporter) connect(network, url string) error {
if rep.conn != nil {
rep.conn.Close()
}
conn, err := net.Dial(network, url)
if err != nil {
return awserr.New("UDPError", "Could not connect", err)
}
rep.conn = conn
return nil
}
func (rep *Reporter) close() {
if rep.done != nil {
close(rep.done)
}
rep.metricsCh.Pause()
}
func (rep *Reporter) start() {
defer func() {
rep.metricsCh.Pause()
}()
for {
select {
case <-rep.done:
rep.done = nil
return
case m := <-rep.metricsCh.ch:
// TODO: What to do with this error? Probably should just log
b, err := json.Marshal(m)
if err != nil {
continue
}
rep.conn.Write(b)
}
}
}
// Pause will pause the metric channel preventing any new metrics from
// being added.
func (rep *Reporter) Pause() {
lock.Lock()
defer lock.Unlock()
if rep == nil {
return
}
rep.close()
}
// Continue will reopen the metric channel and allow for monitoring
// to be resumed.
func (rep *Reporter) Continue() {
lock.Lock()
defer lock.Unlock()
if rep == nil {
return
}
if !rep.metricsCh.IsPaused() {
return
}
rep.metricsCh.Continue()
}
// InjectHandlers will will enable client side metrics and inject the proper
// handlers to handle how metrics are sent.
//
// Example:
// // Start must be called in order to inject the correct handlers
// r, err := csm.Start("clientID", "127.0.0.1:8094")
// if err != nil {
// panic(fmt.Errorf("expected no error, but received %v", err))
// }
//
// sess := session.NewSession()
// r.InjectHandlers(&sess.Handlers)
//
// // create a new service client with our client side metric session
// svc := s3.New(sess)
func (rep *Reporter) InjectHandlers(handlers *request.Handlers) {
if rep == nil {
return
}
apiCallHandler := request.NamedHandler{Name: APICallMetricHandlerName, Fn: rep.sendAPICallMetric}
apiCallAttemptHandler := request.NamedHandler{Name: APICallAttemptMetricHandlerName, Fn: rep.sendAPICallAttemptMetric}
handlers.Complete.PushFrontNamed(apiCallHandler)
handlers.Complete.PushFrontNamed(apiCallAttemptHandler)
handlers.AfterRetry.PushFrontNamed(apiCallAttemptHandler)
}

View file

@ -92,12 +92,23 @@ func Handlers() request.Handlers {
func CredChain(cfg *aws.Config, handlers request.Handlers) *credentials.Credentials {
return credentials.NewCredentials(&credentials.ChainProvider{
VerboseErrors: aws.BoolValue(cfg.CredentialsChainVerboseErrors),
Providers: []credentials.Provider{
Providers: CredProviders(cfg, handlers),
})
}
// CredProviders returns the slice of providers used in
// the default credential chain.
//
// For applications that need to use some other provider (for example use
// different environment variables for legacy reasons) but still fall back
// on the default chain of providers. This allows that default chaint to be
// automatically updated
func CredProviders(cfg *aws.Config, handlers request.Handlers) []credentials.Provider {
return []credentials.Provider{
&credentials.EnvProvider{},
&credentials.SharedCredentialsProvider{Filename: "", Profile: ""},
RemoteCredProvider(*cfg, handlers),
},
})
}
}
const (

View file

@ -4,12 +4,12 @@ import (
"encoding/json"
"fmt"
"net/http"
"path"
"strings"
"time"
"github.com/aws/aws-sdk-go/aws/awserr"
"github.com/aws/aws-sdk-go/aws/request"
"github.com/aws/aws-sdk-go/internal/sdkuri"
)
// GetMetadata uses the path provided to request information from the EC2
@ -19,7 +19,7 @@ func (c *EC2Metadata) GetMetadata(p string) (string, error) {
op := &request.Operation{
Name: "GetMetadata",
HTTPMethod: "GET",
HTTPPath: path.Join("/", "meta-data", p),
HTTPPath: sdkuri.PathJoin("/meta-data", p),
}
output := &metadataOutput{}
@ -35,7 +35,7 @@ func (c *EC2Metadata) GetUserData() (string, error) {
op := &request.Operation{
Name: "GetUserData",
HTTPMethod: "GET",
HTTPPath: path.Join("/", "user-data"),
HTTPPath: "/user-data",
}
output := &metadataOutput{}
@ -56,7 +56,7 @@ func (c *EC2Metadata) GetDynamicData(p string) (string, error) {
op := &request.Operation{
Name: "GetDynamicData",
HTTPMethod: "GET",
HTTPPath: path.Join("/", "dynamic", p),
HTTPPath: sdkuri.PathJoin("/dynamic", p),
}
output := &metadataOutput{}

View file

@ -84,6 +84,7 @@ func decodeV3Endpoints(modelDef modelDefinition, opts DecodeModelOptions) (Resol
custAddEC2Metadata(p)
custAddS3DualStack(p)
custRmIotDataService(p)
custFixAppAutoscalingChina(p)
}
return ps, nil
@ -122,6 +123,27 @@ func custRmIotDataService(p *partition) {
delete(p.Services, "data.iot")
}
func custFixAppAutoscalingChina(p *partition) {
if p.ID != "aws-cn" {
return
}
const serviceName = "application-autoscaling"
s, ok := p.Services[serviceName]
if !ok {
return
}
const expectHostname = `autoscaling.{region}.amazonaws.com`
if e, a := s.Defaults.Hostname, expectHostname; e != a {
fmt.Printf("custFixAppAutoscalingChina: ignoring customization, expected %s, got %s\n", e, a)
return
}
s.Defaults.Hostname = expectHostname + ".cn"
p.Services[serviceName] = s
}
type decodeModelError struct {
awsError
}

View file

@ -48,7 +48,9 @@ const (
A4bServiceID = "a4b" // A4b.
AcmServiceID = "acm" // Acm.
AcmPcaServiceID = "acm-pca" // AcmPca.
ApiMediatailorServiceID = "api.mediatailor" // ApiMediatailor.
ApiPricingServiceID = "api.pricing" // ApiPricing.
ApiSagemakerServiceID = "api.sagemaker" // ApiSagemaker.
ApigatewayServiceID = "apigateway" // Apigateway.
ApplicationAutoscalingServiceID = "application-autoscaling" // ApplicationAutoscaling.
Appstream2ServiceID = "appstream2" // Appstream2.
@ -111,6 +113,7 @@ const (
ImportexportServiceID = "importexport" // Importexport.
InspectorServiceID = "inspector" // Inspector.
IotServiceID = "iot" // Iot.
IotanalyticsServiceID = "iotanalytics" // Iotanalytics.
KinesisServiceID = "kinesis" // Kinesis.
KinesisanalyticsServiceID = "kinesisanalytics" // Kinesisanalytics.
KinesisvideoServiceID = "kinesisvideo" // Kinesisvideo.
@ -130,6 +133,7 @@ const (
ModelsLexServiceID = "models.lex" // ModelsLex.
MonitoringServiceID = "monitoring" // Monitoring.
MturkRequesterServiceID = "mturk-requester" // MturkRequester.
NeptuneServiceID = "neptune" // Neptune.
OpsworksServiceID = "opsworks" // Opsworks.
OpsworksCmServiceID = "opsworks-cm" // OpsworksCm.
OrganizationsServiceID = "organizations" // Organizations.
@ -144,7 +148,6 @@ const (
RuntimeLexServiceID = "runtime.lex" // RuntimeLex.
RuntimeSagemakerServiceID = "runtime.sagemaker" // RuntimeSagemaker.
S3ServiceID = "s3" // S3.
SagemakerServiceID = "sagemaker" // Sagemaker.
SdbServiceID = "sdb" // Sdb.
SecretsmanagerServiceID = "secretsmanager" // Secretsmanager.
ServerlessrepoServiceID = "serverlessrepo" // Serverlessrepo.
@ -302,11 +305,22 @@ var awsPartition = partition{
"ca-central-1": endpoint{},
"eu-central-1": endpoint{},
"eu-west-1": endpoint{},
"eu-west-2": endpoint{},
"us-east-1": endpoint{},
"us-east-2": endpoint{},
"us-west-2": endpoint{},
},
},
"api.mediatailor": service{
Endpoints: endpoints{
"ap-northeast-1": endpoint{},
"ap-southeast-1": endpoint{},
"ap-southeast-2": endpoint{},
"eu-west-1": endpoint{},
"us-east-1": endpoint{},
},
},
"api.pricing": service{
Defaults: endpoint{
CredentialScope: credentialScope{
@ -318,6 +332,19 @@ var awsPartition = partition{
"us-east-1": endpoint{},
},
},
"api.sagemaker": service{
Endpoints: endpoints{
"ap-northeast-1": endpoint{},
"ap-northeast-2": endpoint{},
"ap-southeast-2": endpoint{},
"eu-central-1": endpoint{},
"eu-west-1": endpoint{},
"us-east-1": endpoint{},
"us-east-2": endpoint{},
"us-west-2": endpoint{},
},
},
"apigateway": service{
Endpoints: endpoints{
@ -382,10 +409,13 @@ var awsPartition = partition{
Endpoints: endpoints{
"ap-northeast-1": endpoint{},
"ap-northeast-2": endpoint{},
"ap-south-1": endpoint{},
"ap-southeast-1": endpoint{},
"ap-southeast-2": endpoint{},
"eu-central-1": endpoint{},
"eu-west-1": endpoint{},
"eu-west-2": endpoint{},
"us-east-1": endpoint{},
"us-east-2": endpoint{},
"us-west-2": endpoint{},
@ -441,6 +471,7 @@ var awsPartition = partition{
"eu-central-1": endpoint{},
"eu-west-1": endpoint{},
"eu-west-2": endpoint{},
"sa-east-1": endpoint{},
"us-east-1": endpoint{},
"us-east-2": endpoint{},
"us-west-1": endpoint{},
@ -559,6 +590,7 @@ var awsPartition = partition{
"ca-central-1": endpoint{},
"eu-central-1": endpoint{},
"eu-west-1": endpoint{},
"eu-west-2": endpoint{},
"us-east-1": endpoint{},
"us-east-2": endpoint{},
"us-west-1": endpoint{},
@ -774,6 +806,7 @@ var awsPartition = partition{
Protocols: []string{"https"},
},
Endpoints: endpoints{
"ap-southeast-2": endpoint{},
"eu-west-1": endpoint{},
"us-east-1": endpoint{},
"us-east-2": endpoint{},
@ -1017,6 +1050,12 @@ var awsPartition = partition{
"eu-west-1": endpoint{},
"eu-west-2": endpoint{},
"eu-west-3": endpoint{},
"fips": endpoint{
Hostname: "elasticache-fips.us-west-1.amazonaws.com",
CredentialScope: credentialScope{
Region: "us-west-1",
},
},
"sa-east-1": endpoint{},
"us-east-1": endpoint{},
"us-east-2": endpoint{},
@ -1047,6 +1086,8 @@ var awsPartition = partition{
"elasticfilesystem": service{
Endpoints: endpoints{
"ap-northeast-1": endpoint{},
"ap-northeast-2": endpoint{},
"ap-southeast-2": endpoint{},
"eu-central-1": endpoint{},
"eu-west-1": endpoint{},
@ -1081,7 +1122,7 @@ var awsPartition = partition{
"elasticmapreduce": service{
Defaults: endpoint{
SSLCommonName: "{region}.{service}.{dnsSuffix}",
Protocols: []string{"http", "https"},
Protocols: []string{"https"},
},
Endpoints: endpoints{
"ap-northeast-1": endpoint{},
@ -1180,10 +1221,16 @@ var awsPartition = partition{
Endpoints: endpoints{
"ap-northeast-1": endpoint{},
"ap-northeast-2": endpoint{},
"ap-south-1": endpoint{},
"ap-southeast-1": endpoint{},
"ap-southeast-2": endpoint{},
"ca-central-1": endpoint{},
"eu-central-1": endpoint{},
"eu-west-1": endpoint{},
"eu-west-2": endpoint{},
"eu-west-3": endpoint{},
"sa-east-1": endpoint{},
"us-east-1": endpoint{},
"us-east-2": endpoint{},
"us-west-1": endpoint{},
@ -1195,6 +1242,7 @@ var awsPartition = partition{
Protocols: []string{"https"},
},
Endpoints: endpoints{
"eu-west-1": endpoint{},
"us-east-1": endpoint{},
"us-west-2": endpoint{},
},
@ -1247,6 +1295,7 @@ var awsPartition = partition{
"ap-south-1": endpoint{},
"ap-southeast-1": endpoint{},
"ap-southeast-2": endpoint{},
"ca-central-1": endpoint{},
"eu-central-1": endpoint{},
"eu-west-1": endpoint{},
"eu-west-2": endpoint{},
@ -1264,6 +1313,7 @@ var awsPartition = partition{
"ap-northeast-1": endpoint{},
"ap-southeast-2": endpoint{},
"eu-central-1": endpoint{},
"eu-west-1": endpoint{},
"us-east-1": endpoint{},
"us-west-2": endpoint{},
},
@ -1360,6 +1410,15 @@ var awsPartition = partition{
"us-west-2": endpoint{},
},
},
"iotanalytics": service{
Endpoints: endpoints{
"eu-west-1": endpoint{},
"us-east-1": endpoint{},
"us-east-2": endpoint{},
"us-west-2": endpoint{},
},
},
"kinesis": service{
Endpoints: endpoints{
@ -1383,6 +1442,7 @@ var awsPartition = partition{
"kinesisanalytics": service{
Endpoints: endpoints{
"eu-central-1": endpoint{},
"eu-west-1": endpoint{},
"us-east-1": endpoint{},
"us-west-2": endpoint{},
@ -1513,9 +1573,12 @@ var awsPartition = partition{
Endpoints: endpoints{
"ap-northeast-1": endpoint{},
"ap-northeast-2": endpoint{},
"ap-south-1": endpoint{},
"ap-southeast-1": endpoint{},
"ap-southeast-2": endpoint{},
"eu-central-1": endpoint{},
"eu-west-1": endpoint{},
"sa-east-1": endpoint{},
"us-east-1": endpoint{},
"us-west-2": endpoint{},
},
@ -1539,6 +1602,7 @@ var awsPartition = partition{
Endpoints: endpoints{
"ap-northeast-1": endpoint{},
"ap-northeast-2": endpoint{},
"ap-southeast-2": endpoint{},
"eu-central-1": endpoint{},
"eu-west-1": endpoint{},
@ -1626,6 +1690,35 @@ var awsPartition = partition{
"us-east-1": endpoint{},
},
},
"neptune": service{
Endpoints: endpoints{
"eu-west-1": endpoint{
Hostname: "rds.eu-west-1.amazonaws.com",
CredentialScope: credentialScope{
Region: "eu-west-1",
},
},
"us-east-1": endpoint{
Hostname: "rds.us-east-1.amazonaws.com",
CredentialScope: credentialScope{
Region: "us-east-1",
},
},
"us-east-2": endpoint{
Hostname: "rds.us-east-2.amazonaws.com",
CredentialScope: credentialScope{
Region: "us-east-2",
},
},
"us-west-2": endpoint{
Hostname: "rds.us-west-2.amazonaws.com",
CredentialScope: credentialScope{
Region: "us-west-2",
},
},
},
},
"opsworks": service{
Endpoints: endpoints{
@ -1768,6 +1861,7 @@ var awsPartition = partition{
"eu-central-1": endpoint{},
"eu-west-1": endpoint{},
"eu-west-2": endpoint{},
"eu-west-3": endpoint{},
"sa-east-1": endpoint{},
"us-east-1": endpoint{},
"us-east-2": endpoint{},
@ -1809,6 +1903,10 @@ var awsPartition = partition{
"runtime.sagemaker": service{
Endpoints: endpoints{
"ap-northeast-1": endpoint{},
"ap-northeast-2": endpoint{},
"ap-southeast-2": endpoint{},
"eu-central-1": endpoint{},
"eu-west-1": endpoint{},
"us-east-1": endpoint{},
"us-east-2": endpoint{},
@ -1874,15 +1972,6 @@ var awsPartition = partition{
},
},
},
"sagemaker": service{
Endpoints: endpoints{
"eu-west-1": endpoint{},
"us-east-1": endpoint{},
"us-east-2": endpoint{},
"us-west-2": endpoint{},
},
},
"sdb": service{
Defaults: endpoint{
Protocols: []string{"http", "https"},
@ -1995,6 +2084,7 @@ var awsPartition = partition{
"eu-west-1": endpoint{},
"us-east-1": endpoint{},
"us-east-2": endpoint{},
"us-west-1": endpoint{},
"us-west-2": endpoint{},
},
},
@ -2085,10 +2175,30 @@ var awsPartition = partition{
"eu-west-1": endpoint{},
"eu-west-2": endpoint{},
"eu-west-3": endpoint{},
"fips-us-east-1": endpoint{},
"fips-us-east-2": endpoint{},
"fips-us-west-1": endpoint{},
"fips-us-west-2": endpoint{},
"fips-us-east-1": endpoint{
Hostname: "sqs-fips.us-east-1.amazonaws.com",
CredentialScope: credentialScope{
Region: "us-east-1",
},
},
"fips-us-east-2": endpoint{
Hostname: "sqs-fips.us-east-2.amazonaws.com",
CredentialScope: credentialScope{
Region: "us-east-2",
},
},
"fips-us-west-1": endpoint{
Hostname: "sqs-fips.us-west-1.amazonaws.com",
CredentialScope: credentialScope{
Region: "us-west-1",
},
},
"fips-us-west-2": endpoint{
Hostname: "sqs-fips.us-west-2.amazonaws.com",
CredentialScope: credentialScope{
Region: "us-west-2",
},
},
"sa-east-1": endpoint{},
"us-east-1": endpoint{
SSLCommonName: "queue.{dnsSuffix}",
@ -2123,6 +2233,7 @@ var awsPartition = partition{
Endpoints: endpoints{
"ap-northeast-1": endpoint{},
"ap-northeast-2": endpoint{},
"ap-south-1": endpoint{},
"ap-southeast-1": endpoint{},
"ap-southeast-2": endpoint{},
"ca-central-1": endpoint{},
@ -2418,11 +2529,12 @@ var awscnPartition = partition{
Endpoints: endpoints{
"cn-north-1": endpoint{},
"cn-northwest-1": endpoint{},
},
},
"application-autoscaling": service{
Defaults: endpoint{
Hostname: "autoscaling.{region}.amazonaws.com",
Hostname: "autoscaling.{region}.amazonaws.com.cn",
Protocols: []string{"http", "https"},
CredentialScope: credentialScope{
Service: "application-autoscaling",
@ -2483,6 +2595,13 @@ var awscnPartition = partition{
"cn-northwest-1": endpoint{},
},
},
"ds": service{
Endpoints: endpoints{
"cn-north-1": endpoint{},
"cn-northwest-1": endpoint{},
},
},
"dynamodb": service{
Defaults: endpoint{
Protocols: []string{"http", "https"},
@ -2516,12 +2635,14 @@ var awscnPartition = partition{
Endpoints: endpoints{
"cn-north-1": endpoint{},
"cn-northwest-1": endpoint{},
},
},
"ecs": service{
Endpoints: endpoints{
"cn-north-1": endpoint{},
"cn-northwest-1": endpoint{},
},
},
"elasticache": service{
@ -2549,7 +2670,7 @@ var awscnPartition = partition{
},
"elasticmapreduce": service{
Defaults: endpoint{
Protocols: []string{"http", "https"},
Protocols: []string{"https"},
},
Endpoints: endpoints{
"cn-north-1": endpoint{},
@ -2612,6 +2733,7 @@ var awscnPartition = partition{
Endpoints: endpoints{
"cn-north-1": endpoint{},
"cn-northwest-1": endpoint{},
},
},
"logs": service{
@ -2877,6 +2999,12 @@ var awsusgovPartition = partition{
"elasticache": service{
Endpoints: endpoints{
"fips": endpoint{
Hostname: "elasticache-fips.us-gov-west-1.amazonaws.com",
CredentialScope: credentialScope{
Region: "us-gov-west-1",
},
},
"us-gov-west-1": endpoint{},
},
},
@ -2898,7 +3026,7 @@ var awsusgovPartition = partition{
Endpoints: endpoints{
"us-gov-west-1": endpoint{
Protocols: []string{"http", "https"},
Protocols: []string{"https"},
},
},
},
@ -2935,6 +3063,22 @@ var awsusgovPartition = partition{
},
},
},
"inspector": service{
Endpoints: endpoints{
"us-gov-west-1": endpoint{},
},
},
"iot": service{
Defaults: endpoint{
CredentialScope: credentialScope{
Service: "execute-api",
},
},
Endpoints: endpoints{
"us-gov-west-1": endpoint{},
},
},
"kinesis": service{
Endpoints: endpoints{
@ -3051,6 +3195,12 @@ var awsusgovPartition = partition{
"us-gov-west-1": endpoint{},
},
},
"states": service{
Endpoints: endpoints{
"us-gov-west-1": endpoint{},
},
},
"storagegateway": service{
Endpoints: endpoints{
@ -3091,5 +3241,13 @@ var awsusgovPartition = partition{
"us-gov-west-1": endpoint{},
},
},
"translate": service{
Defaults: endpoint{
Protocols: []string{"https"},
},
Endpoints: endpoints{
"us-gov-west-1": endpoint{},
},
},
},
}

View file

@ -71,6 +71,12 @@ const (
// LogDebugWithRequestErrors states the SDK should log when service requests fail
// to build, send, validate, or unmarshal.
LogDebugWithRequestErrors
// LogDebugWithEventStreamBody states the SDK should log EventStream
// request and response bodys. This should be used to log the EventStream
// wire unmarshaled message content of requests and responses made while
// using the SDK Will also enable LogDebug.
LogDebugWithEventStreamBody
)
// A Logger is a minimalistic interface for the SDK to log messages to. Should

View file

@ -14,6 +14,7 @@ type Handlers struct {
Send HandlerList
ValidateResponse HandlerList
Unmarshal HandlerList
UnmarshalStream HandlerList
UnmarshalMeta HandlerList
UnmarshalError HandlerList
Retry HandlerList
@ -30,6 +31,7 @@ func (h *Handlers) Copy() Handlers {
Send: h.Send.copy(),
ValidateResponse: h.ValidateResponse.copy(),
Unmarshal: h.Unmarshal.copy(),
UnmarshalStream: h.UnmarshalStream.copy(),
UnmarshalError: h.UnmarshalError.copy(),
UnmarshalMeta: h.UnmarshalMeta.copy(),
Retry: h.Retry.copy(),
@ -45,6 +47,7 @@ func (h *Handlers) Clear() {
h.Send.Clear()
h.Sign.Clear()
h.Unmarshal.Clear()
h.UnmarshalStream.Clear()
h.UnmarshalMeta.Clear()
h.UnmarshalError.Clear()
h.ValidateResponse.Clear()
@ -172,6 +175,21 @@ func (l *HandlerList) SwapNamed(n NamedHandler) (swapped bool) {
return swapped
}
// Swap will swap out all handlers matching the name passed in. The matched
// handlers will be swapped in. True is returned if the handlers were swapped.
func (l *HandlerList) Swap(name string, replace NamedHandler) bool {
var swapped bool
for i := 0; i < len(l.list); i++ {
if l.list[i].Name == name {
l.list[i] = replace
swapped = true
}
}
return swapped
}
// SetBackNamed will replace the named handler if it exists in the handler list.
// If the handler does not exist the handler will be added to the end of the list.
func (l *HandlerList) SetBackNamed(n NamedHandler) {

View file

@ -46,6 +46,7 @@ type Request struct {
Handlers Handlers
Retryer
AttemptTime time.Time
Time time.Time
Operation *Operation
HTTPRequest *http.Request
@ -121,6 +122,7 @@ func New(cfg aws.Config, clientInfo metadata.ClientInfo, handlers Handlers,
Handlers: handlers.Copy(),
Retryer: retryer,
AttemptTime: time.Now(),
Time: time.Now(),
ExpireTime: 0,
Operation: operation,
@ -368,9 +370,9 @@ func (r *Request) Build() error {
return r.Error
}
// Sign will sign the request returning error if errors are encountered.
// Sign will sign the request, returning error if errors are encountered.
//
// Send will build the request prior to signing. All Sign Handlers will
// Sign will build the request prior to signing. All Sign Handlers will
// be executed in the order they were set.
func (r *Request) Sign() error {
r.Build()
@ -440,7 +442,7 @@ func (r *Request) GetBody() io.ReadSeeker {
return r.safeBody
}
// Send will send the request returning error if errors are encountered.
// Send will send the request, returning error if errors are encountered.
//
// Send will sign the request prior to sending. All Send Handlers will
// be executed in the order they were set.
@ -461,6 +463,7 @@ func (r *Request) Send() error {
}()
for {
r.AttemptTime = time.Now()
if aws.BoolValue(r.Retryable) {
if r.Config.LogLevel.Matches(aws.LogDebugWithRequestRetries) {
r.Config.Logger.Log(fmt.Sprintf("DEBUG: Retrying Request %s/%s, attempt %d",

View file

@ -21,7 +21,7 @@ func (noBody) WriteTo(io.Writer) (int64, error) { return 0, nil }
var NoBody = noBody{}
// ResetBody rewinds the request body back to its starting position, and
// set's the HTTP Request body reference. When the body is read prior
// sets the HTTP Request body reference. When the body is read prior
// to being sent in the HTTP request it will need to be rewound.
//
// ResetBody will automatically be called by the SDK's build handler, but if

View file

@ -11,7 +11,7 @@ import (
var NoBody = http.NoBody
// ResetBody rewinds the request body back to its starting position, and
// set's the HTTP Request body reference. When the body is read prior
// sets the HTTP Request body reference. When the body is read prior
// to being sent in the HTTP request it will need to be rewound.
//
// ResetBody will automatically be called by the SDK's build handler, but if

View file

@ -97,7 +97,7 @@ func isNestedErrorRetryable(parentErr awserr.Error) bool {
}
if t, ok := err.(temporaryError); ok {
return t.Temporary()
return t.Temporary() || isErrConnectionReset(err)
}
return isErrConnectionReset(err)

View file

@ -128,7 +128,7 @@ read. The Session will be created from configuration values from the shared
credentials file (~/.aws/credentials) over those in the shared config file (~/.aws/config).
Credentials are the values the SDK should use for authenticating requests with
AWS Services. They arfrom a configuration file will need to include both
AWS Services. They are from a configuration file will need to include both
aws_access_key_id and aws_secret_access_key must be provided together in the
same file to be considered valid. The values will be ignored if not a complete
group. aws_session_token is an optional field that can be provided if both of

View file

@ -96,9 +96,23 @@ type envConfig struct {
//
// AWS_CA_BUNDLE=$HOME/my_custom_ca_bundle
CustomCABundle string
csmEnabled string
CSMEnabled bool
CSMPort string
CSMClientID string
}
var (
csmEnabledEnvKey = []string{
"AWS_CSM_ENABLED",
}
csmPortEnvKey = []string{
"AWS_CSM_PORT",
}
csmClientIDEnvKey = []string{
"AWS_CSM_CLIENT_ID",
}
credAccessEnvKey = []string{
"AWS_ACCESS_KEY_ID",
"AWS_ACCESS_KEY",
@ -157,6 +171,12 @@ func envConfigLoad(enableSharedConfig bool) envConfig {
setFromEnvVal(&cfg.Creds.SecretAccessKey, credSecretEnvKey)
setFromEnvVal(&cfg.Creds.SessionToken, credSessionEnvKey)
// CSM environment variables
setFromEnvVal(&cfg.csmEnabled, csmEnabledEnvKey)
setFromEnvVal(&cfg.CSMPort, csmPortEnvKey)
setFromEnvVal(&cfg.CSMClientID, csmClientIDEnvKey)
cfg.CSMEnabled = len(cfg.csmEnabled) > 0
// Require logical grouping of credentials
if len(cfg.Creds.AccessKeyID) == 0 || len(cfg.Creds.SecretAccessKey) == 0 {
cfg.Creds = credentials.Value{}

View file

@ -15,6 +15,7 @@ import (
"github.com/aws/aws-sdk-go/aws/corehandlers"
"github.com/aws/aws-sdk-go/aws/credentials"
"github.com/aws/aws-sdk-go/aws/credentials/stscreds"
"github.com/aws/aws-sdk-go/aws/csm"
"github.com/aws/aws-sdk-go/aws/defaults"
"github.com/aws/aws-sdk-go/aws/endpoints"
"github.com/aws/aws-sdk-go/aws/request"
@ -81,10 +82,16 @@ func New(cfgs ...*aws.Config) *Session {
r.Error = err
})
}
return s
}
return deprecatedNewSession(cfgs...)
s := deprecatedNewSession(cfgs...)
if envCfg.CSMEnabled {
enableCSM(&s.Handlers, envCfg.CSMClientID, envCfg.CSMPort, s.Config.Logger)
}
return s
}
// NewSession returns a new Session created from SDK defaults, config files,
@ -300,10 +307,22 @@ func deprecatedNewSession(cfgs ...*aws.Config) *Session {
}
initHandlers(s)
return s
}
func enableCSM(handlers *request.Handlers, clientID string, port string, logger aws.Logger) {
logger.Log("Enabling CSM")
if len(port) == 0 {
port = csm.DefaultPort
}
r, err := csm.Start(clientID, "127.0.0.1:"+port)
if err != nil {
return
}
r.InjectHandlers(handlers)
}
func newSession(opts Options, envCfg envConfig, cfgs ...*aws.Config) (*Session, error) {
cfg := defaults.Config()
handlers := defaults.Handlers()
@ -343,6 +362,9 @@ func newSession(opts Options, envCfg envConfig, cfgs ...*aws.Config) (*Session,
}
initHandlers(s)
if envCfg.CSMEnabled {
enableCSM(&s.Handlers, envCfg.CSMClientID, envCfg.CSMPort, s.Config.Logger)
}
// Setup HTTP client with custom cert bundle if enabled
if opts.CustomCABundle != nil {

View file

@ -135,6 +135,7 @@ var requiredSignedHeaders = rules{
"X-Amz-Server-Side-Encryption-Customer-Key-Md5": struct{}{},
"X-Amz-Storage-Class": struct{}{},
"X-Amz-Website-Redirect-Location": struct{}{},
"X-Amz-Content-Sha256": struct{}{},
},
},
patterns{"X-Amz-Meta-"},
@ -671,8 +672,15 @@ func (ctx *signingCtx) buildSignature() {
func (ctx *signingCtx) buildBodyDigest() error {
hash := ctx.Request.Header.Get("X-Amz-Content-Sha256")
if hash == "" {
if ctx.unsignedPayload || (ctx.isPresign && ctx.ServiceName == "s3") {
includeSHA256Header := ctx.unsignedPayload ||
ctx.ServiceName == "s3" ||
ctx.ServiceName == "glacier"
s3Presign := ctx.isPresign && ctx.ServiceName == "s3"
if ctx.unsignedPayload || s3Presign {
hash = "UNSIGNED-PAYLOAD"
includeSHA256Header = !s3Presign
} else if ctx.Body == nil {
hash = emptyStringSHA256
} else {
@ -681,7 +689,8 @@ func (ctx *signingCtx) buildBodyDigest() error {
}
hash = hex.EncodeToString(makeSha256Reader(ctx.Body))
}
if ctx.unsignedPayload || ctx.ServiceName == "s3" || ctx.ServiceName == "glacier" {
if includeSHA256Header {
ctx.Request.Header.Set("X-Amz-Content-Sha256", hash)
}
}

View file

@ -5,4 +5,4 @@ package aws
const SDKName = "aws-sdk-go"
// SDKVersion is the version of this SDK
const SDKVersion = "1.13.57"
const SDKVersion = "1.15.23"

View file

@ -0,0 +1,23 @@
package sdkuri
import (
"path"
"strings"
)
// PathJoin will join the elements of the path delimited by the "/"
// character. Similar to path.Join with the exception the trailing "/"
// character is preserved if present.
func PathJoin(elems ...string) string {
if len(elems) == 0 {
return ""
}
hasTrailing := strings.HasSuffix(elems[len(elems)-1], "/")
str := path.Join(elems...)
if hasTrailing && str != "/" {
str += "/"
}
return str
}

View file

@ -216,7 +216,17 @@ func buildScalar(v reflect.Value, buf *bytes.Buffer, tag reflect.StructTag) erro
default:
switch converted := value.Interface().(type) {
case time.Time:
buf.Write(strconv.AppendInt(scratch[:0], converted.UTC().Unix(), 10))
format := tag.Get("timestampFormat")
if len(format) == 0 {
format = protocol.UnixTimeFormatName
}
ts := protocol.FormatTime(format, converted)
if format != protocol.UnixTimeFormatName {
ts = `"` + ts + `"`
}
buf.WriteString(ts)
case []byte:
if !value.IsNil() {
buf.WriteByte('"')

View file

@ -172,9 +172,6 @@ func unmarshalMap(value reflect.Value, data interface{}, tag reflect.StructTag)
}
func unmarshalScalar(value reflect.Value, data interface{}, tag reflect.StructTag) error {
errf := func() error {
return fmt.Errorf("unsupported value: %v (%s)", value.Interface(), value.Type())
}
switch d := data.(type) {
case nil:
@ -189,6 +186,17 @@ func unmarshalScalar(value reflect.Value, data interface{}, tag reflect.StructTa
return err
}
value.Set(reflect.ValueOf(b))
case *time.Time:
format := tag.Get("timestampFormat")
if len(format) == 0 {
format = protocol.ISO8601TimeFormatName
}
t, err := protocol.ParseTime(format, d)
if err != nil {
return err
}
value.Set(reflect.ValueOf(&t))
case aws.JSONValue:
// No need to use escaping as the value is a non-quoted string.
v, err := protocol.DecodeJSONValue(d, protocol.NoEscape)
@ -197,7 +205,7 @@ func unmarshalScalar(value reflect.Value, data interface{}, tag reflect.StructTa
}
value.Set(reflect.ValueOf(v))
default:
return errf()
return fmt.Errorf("unsupported value: %v (%s)", value.Interface(), value.Type())
}
case float64:
switch value.Interface().(type) {
@ -207,17 +215,18 @@ func unmarshalScalar(value reflect.Value, data interface{}, tag reflect.StructTa
case *float64:
value.Set(reflect.ValueOf(&d))
case *time.Time:
// Time unmarshaled from a float64 can only be epoch seconds
t := time.Unix(int64(d), 0).UTC()
value.Set(reflect.ValueOf(&t))
default:
return errf()
return fmt.Errorf("unsupported value: %v (%s)", value.Interface(), value.Type())
}
case bool:
switch value.Interface().(type) {
case *bool:
value.Set(reflect.ValueOf(&d))
default:
return errf()
return fmt.Errorf("unsupported value: %v (%s)", value.Interface(), value.Type())
}
default:
return fmt.Errorf("unsupported JSON value (%v)", data)

View file

@ -0,0 +1,81 @@
package protocol
import (
"io"
"io/ioutil"
"net/http"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/client/metadata"
"github.com/aws/aws-sdk-go/aws/request"
)
// PayloadUnmarshaler provides the interface for unmarshaling a payload's
// reader into a SDK shape.
type PayloadUnmarshaler interface {
UnmarshalPayload(io.Reader, interface{}) error
}
// HandlerPayloadUnmarshal implements the PayloadUnmarshaler from a
// HandlerList. This provides the support for unmarshaling a payload reader to
// a shape without needing a SDK request first.
type HandlerPayloadUnmarshal struct {
Unmarshalers request.HandlerList
}
// UnmarshalPayload unmarshals the io.Reader payload into the SDK shape using
// the Unmarshalers HandlerList provided. Returns an error if unable
// unmarshaling fails.
func (h HandlerPayloadUnmarshal) UnmarshalPayload(r io.Reader, v interface{}) error {
req := &request.Request{
HTTPRequest: &http.Request{},
HTTPResponse: &http.Response{
StatusCode: 200,
Header: http.Header{},
Body: ioutil.NopCloser(r),
},
Data: v,
}
h.Unmarshalers.Run(req)
return req.Error
}
// PayloadMarshaler provides the interface for marshaling a SDK shape into and
// io.Writer.
type PayloadMarshaler interface {
MarshalPayload(io.Writer, interface{}) error
}
// HandlerPayloadMarshal implements the PayloadMarshaler from a HandlerList.
// This provides support for marshaling a SDK shape into an io.Writer without
// needing a SDK request first.
type HandlerPayloadMarshal struct {
Marshalers request.HandlerList
}
// MarshalPayload marshals the SDK shape into the io.Writer using the
// Marshalers HandlerList provided. Returns an error if unable if marshal
// fails.
func (h HandlerPayloadMarshal) MarshalPayload(w io.Writer, v interface{}) error {
req := request.New(
aws.Config{},
metadata.ClientInfo{},
request.Handlers{},
nil,
&request.Operation{HTTPMethod: "GET"},
v,
nil,
)
h.Marshalers.Run(req)
if req.Error != nil {
return req.Error
}
io.Copy(w, req.GetBody())
return nil
}

View file

@ -233,7 +233,12 @@ func (q *queryParser) parseScalar(v url.Values, r reflect.Value, name string, ta
v.Set(name, strconv.FormatFloat(float64(value), 'f', -1, 32))
case time.Time:
const ISO8601UTC = "2006-01-02T15:04:05Z"
v.Set(name, value.UTC().Format(ISO8601UTC))
format := tag.Get("timestampFormat")
if len(format) == 0 {
format = protocol.ISO8601TimeFormatName
}
v.Set(name, protocol.FormatTime(format, value))
default:
return fmt.Errorf("unsupported value for param %s: %v (%s)", name, r.Interface(), r.Type().Name())
}

View file

@ -20,11 +20,6 @@ import (
"github.com/aws/aws-sdk-go/private/protocol"
)
// RFC1123GMT is a RFC1123 (RFC822) formated timestame. This format is not
// using the standard library's time.RFC1123 due to the desire to always use
// GMT as the timezone.
const RFC1123GMT = "Mon, 2 Jan 2006 15:04:05 GMT"
// Whether the byte value can be sent without escaping in AWS URLs
var noEscape [256]bool
@ -272,7 +267,14 @@ func convertType(v reflect.Value, tag reflect.StructTag) (str string, err error)
case float64:
str = strconv.FormatFloat(value, 'f', -1, 64)
case time.Time:
str = value.UTC().Format(RFC1123GMT)
format := tag.Get("timestampFormat")
if len(format) == 0 {
format = protocol.RFC822TimeFormatName
if tag.Get("location") == "querystring" {
format = protocol.ISO8601TimeFormatName
}
}
str = protocol.FormatTime(format, value)
case aws.JSONValue:
if len(value) == 0 {
return "", errValueNotSet

View file

@ -198,7 +198,11 @@ func unmarshalHeader(v reflect.Value, header string, tag reflect.StructTag) erro
}
v.Set(reflect.ValueOf(&f))
case *time.Time:
t, err := time.Parse(time.RFC1123, header)
format := tag.Get("timestampFormat")
if len(format) == 0 {
format = protocol.RFC822TimeFormatName
}
t, err := protocol.ParseTime(format, header)
if err != nil {
return err
}

View file

@ -0,0 +1,72 @@
package protocol
import (
"strconv"
"time"
)
// Names of time formats supported by the SDK
const (
RFC822TimeFormatName = "rfc822"
ISO8601TimeFormatName = "iso8601"
UnixTimeFormatName = "unixTimestamp"
)
// Time formats supported by the SDK
const (
// RFC 7231#section-7.1.1.1 timetamp format. e.g Tue, 29 Apr 2014 18:30:38 GMT
RFC822TimeFormat = "Mon, 2 Jan 2006 15:04:05 GMT"
// RFC3339 a subset of the ISO8601 timestamp format. e.g 2014-04-29T18:30:38Z
ISO8601TimeFormat = "2006-01-02T15:04:05Z"
)
// IsKnownTimestampFormat returns if the timestamp format name
// is know to the SDK's protocols.
func IsKnownTimestampFormat(name string) bool {
switch name {
case RFC822TimeFormatName:
fallthrough
case ISO8601TimeFormatName:
fallthrough
case UnixTimeFormatName:
return true
default:
return false
}
}
// FormatTime returns a string value of the time.
func FormatTime(name string, t time.Time) string {
t = t.UTC()
switch name {
case RFC822TimeFormatName:
return t.Format(RFC822TimeFormat)
case ISO8601TimeFormatName:
return t.Format(ISO8601TimeFormat)
case UnixTimeFormatName:
return strconv.FormatInt(t.Unix(), 10)
default:
panic("unknown timestamp format name, " + name)
}
}
// ParseTime attempts to parse the time given the format. Returns
// the time if it was able to be parsed, and fails otherwise.
func ParseTime(formatName, value string) (time.Time, error) {
switch formatName {
case RFC822TimeFormatName:
return time.Parse(RFC822TimeFormat, value)
case ISO8601TimeFormatName:
return time.Parse(ISO8601TimeFormat, value)
case UnixTimeFormatName:
v, err := strconv.ParseFloat(value, 64)
if err != nil {
return time.Time{}, err
}
return time.Unix(int64(v), 0), nil
default:
panic("unknown timestamp format name, " + formatName)
}
}

View file

@ -13,9 +13,13 @@ import (
"github.com/aws/aws-sdk-go/private/protocol"
)
// BuildXML will serialize params into an xml.Encoder.
// Error will be returned if the serialization of any of the params or nested values fails.
// BuildXML will serialize params into an xml.Encoder. Error will be returned
// if the serialization of any of the params or nested values fails.
func BuildXML(params interface{}, e *xml.Encoder) error {
return buildXML(params, e, false)
}
func buildXML(params interface{}, e *xml.Encoder, sorted bool) error {
b := xmlBuilder{encoder: e, namespaces: map[string]string{}}
root := NewXMLElement(xml.Name{})
if err := b.buildValue(reflect.ValueOf(params), root, ""); err != nil {
@ -23,7 +27,7 @@ func BuildXML(params interface{}, e *xml.Encoder) error {
}
for _, c := range root.Children {
for _, v := range c {
return StructToXML(e, v, false)
return StructToXML(e, v, sorted)
}
}
return nil
@ -90,8 +94,6 @@ func (b *xmlBuilder) buildStruct(value reflect.Value, current *XMLNode, tag refl
return nil
}
fieldAdded := false
// unwrap payloads
if payload := tag.Get("payload"); payload != "" {
field, _ := value.Type().FieldByName(payload)
@ -119,6 +121,8 @@ func (b *xmlBuilder) buildStruct(value reflect.Value, current *XMLNode, tag refl
child.Attr = append(child.Attr, ns)
}
var payloadFields, nonPayloadFields int
t := value.Type()
for i := 0; i < value.NumField(); i++ {
member := elemOf(value.Field(i))
@ -133,8 +137,10 @@ func (b *xmlBuilder) buildStruct(value reflect.Value, current *XMLNode, tag refl
mTag := field.Tag
if mTag.Get("location") != "" { // skip non-body members
nonPayloadFields++
continue
}
payloadFields++
if protocol.CanSetIdempotencyToken(value.Field(i), field) {
token := protocol.GetIdempotencyToken()
@ -149,11 +155,11 @@ func (b *xmlBuilder) buildStruct(value reflect.Value, current *XMLNode, tag refl
if err := b.buildValue(member, child, mTag); err != nil {
return err
}
fieldAdded = true
}
if fieldAdded { // only append this child if we have one ore more valid members
// Only case where the child shape is not added is if the shape only contains
// non-payload fields, e.g headers/query.
if !(payloadFields == 0 && nonPayloadFields > 0) {
current.AddChild(child)
}
@ -278,8 +284,12 @@ func (b *xmlBuilder) buildScalar(value reflect.Value, current *XMLNode, tag refl
case float32:
str = strconv.FormatFloat(float64(converted), 'f', -1, 32)
case time.Time:
const ISO8601UTC = "2006-01-02T15:04:05Z"
str = converted.UTC().Format(ISO8601UTC)
format := tag.Get("timestampFormat")
if len(format) == 0 {
format = protocol.ISO8601TimeFormatName
}
str = protocol.FormatTime(format, converted)
default:
return fmt.Errorf("unsupported value for param %s: %v (%s)",
tag.Get("locationName"), value.Interface(), value.Type().Name())

View file

@ -9,6 +9,8 @@ import (
"strconv"
"strings"
"time"
"github.com/aws/aws-sdk-go/private/protocol"
)
// UnmarshalXML deserializes an xml.Decoder into the container v. V
@ -253,8 +255,12 @@ func parseScalar(r reflect.Value, node *XMLNode, tag reflect.StructTag) error {
}
r.Set(reflect.ValueOf(&v))
case *time.Time:
const ISO8601UTC = "2006-01-02T15:04:05Z"
t, err := time.Parse(ISO8601UTC, node.Text)
format := tag.Get("timestampFormat")
if len(format) == 0 {
format = protocol.ISO8601TimeFormatName
}
t, err := protocol.ParseTime(format, node.Text)
if err != nil {
return err
}

View file

@ -29,6 +29,7 @@ func NewXMLElement(name xml.Name) *XMLNode {
// AddChild adds child to the XMLNode.
func (n *XMLNode) AddChild(child *XMLNode) {
child.parent = n
if _, ok := n.Children[child.Name.Local]; !ok {
n.Children[child.Name.Local] = []*XMLNode{}
}

View file

@ -10166,7 +10166,7 @@ type Disk struct {
AttachmentState *string `locationName:"attachmentState" deprecated:"true" type:"string"`
// The date when the disk was created.
CreatedAt *time.Time `locationName:"createdAt" type:"timestamp" timestampFormat:"unix"`
CreatedAt *time.Time `locationName:"createdAt" type:"timestamp"`
// (Deprecated) The number of GB in use by the disk.
//
@ -10349,7 +10349,7 @@ type DiskSnapshot struct {
Arn *string `locationName:"arn" type:"string"`
// The date when the disk snapshot was created.
CreatedAt *time.Time `locationName:"createdAt" type:"timestamp" timestampFormat:"unix"`
CreatedAt *time.Time `locationName:"createdAt" type:"timestamp"`
// The Amazon Resource Name (ARN) of the source disk from which you are creating
// the disk snapshot.
@ -10466,7 +10466,7 @@ type Domain struct {
Arn *string `locationName:"arn" type:"string"`
// The date when the domain recordset was created.
CreatedAt *time.Time `locationName:"createdAt" type:"timestamp" timestampFormat:"unix"`
CreatedAt *time.Time `locationName:"createdAt" type:"timestamp"`
// An array of key-value pairs containing information about the domain entries.
DomainEntries []*DomainEntry `locationName:"domainEntries" type:"list"`
@ -11325,7 +11325,7 @@ type GetInstanceMetricDataInput struct {
// The end time of the time period.
//
// EndTime is a required field
EndTime *time.Time `locationName:"endTime" type:"timestamp" timestampFormat:"unix" required:"true"`
EndTime *time.Time `locationName:"endTime" type:"timestamp" required:"true"`
// The name of the instance for which you want to get metrics data.
//
@ -11345,7 +11345,7 @@ type GetInstanceMetricDataInput struct {
// The start time of the time period.
//
// StartTime is a required field
StartTime *time.Time `locationName:"startTime" type:"timestamp" timestampFormat:"unix" required:"true"`
StartTime *time.Time `locationName:"startTime" type:"timestamp" required:"true"`
// The instance statistics.
//
@ -11961,7 +11961,7 @@ type GetLoadBalancerMetricDataInput struct {
// The end time of the period.
//
// EndTime is a required field
EndTime *time.Time `locationName:"endTime" type:"timestamp" timestampFormat:"unix" required:"true"`
EndTime *time.Time `locationName:"endTime" type:"timestamp" required:"true"`
// The name of the load balancer.
//
@ -12061,7 +12061,7 @@ type GetLoadBalancerMetricDataInput struct {
// The start time of the period.
//
// StartTime is a required field
StartTime *time.Time `locationName:"startTime" type:"timestamp" timestampFormat:"unix" required:"true"`
StartTime *time.Time `locationName:"startTime" type:"timestamp" required:"true"`
// An array of statistics that you want to request metrics for. Valid values
// are listed below.
@ -12906,7 +12906,7 @@ type Instance struct {
BundleId *string `locationName:"bundleId" type:"string"`
// The timestamp when the instance was created (e.g., 1479734909.17).
CreatedAt *time.Time `locationName:"createdAt" type:"timestamp" timestampFormat:"unix"`
CreatedAt *time.Time `locationName:"createdAt" type:"timestamp"`
// The size of the vCPU and the amount of RAM for the instance.
Hardware *InstanceHardware `locationName:"hardware" type:"structure"`
@ -13080,7 +13080,7 @@ type InstanceAccessDetails struct {
CertKey *string `locationName:"certKey" type:"string"`
// For SSH access, the date on which the temporary keys expire.
ExpiresAt *time.Time `locationName:"expiresAt" type:"timestamp" timestampFormat:"unix"`
ExpiresAt *time.Time `locationName:"expiresAt" type:"timestamp"`
// The name of this Amazon Lightsail instance.
InstanceName *string `locationName:"instanceName" type:"string"`
@ -13519,7 +13519,7 @@ type InstanceSnapshot struct {
Arn *string `locationName:"arn" type:"string"`
// The timestamp when the snapshot was created (e.g., 1479907467.024).
CreatedAt *time.Time `locationName:"createdAt" type:"timestamp" timestampFormat:"unix"`
CreatedAt *time.Time `locationName:"createdAt" type:"timestamp"`
// An array of disk objects containing information about all block storage disks.
FromAttachedDisks []*Disk `locationName:"fromAttachedDisks" type:"list"`
@ -13735,7 +13735,7 @@ type KeyPair struct {
Arn *string `locationName:"arn" type:"string"`
// The timestamp when the key pair was created (e.g., 1479816991.349).
CreatedAt *time.Time `locationName:"createdAt" type:"timestamp" timestampFormat:"unix"`
CreatedAt *time.Time `locationName:"createdAt" type:"timestamp"`
// The RSA fingerprint of the key pair.
Fingerprint *string `locationName:"fingerprint" type:"string"`
@ -13819,7 +13819,7 @@ type LoadBalancer struct {
ConfigurationOptions map[string]*string `locationName:"configurationOptions" type:"map"`
// The date when your load balancer was created.
CreatedAt *time.Time `locationName:"createdAt" type:"timestamp" timestampFormat:"unix"`
CreatedAt *time.Time `locationName:"createdAt" type:"timestamp"`
// The DNS name of your Lightsail load balancer.
DnsName *string `locationName:"dnsName" type:"string"`
@ -13979,7 +13979,7 @@ type LoadBalancerTlsCertificate struct {
Arn *string `locationName:"arn" type:"string"`
// The time when you created your SSL/TLS certificate.
CreatedAt *time.Time `locationName:"createdAt" type:"timestamp" timestampFormat:"unix"`
CreatedAt *time.Time `locationName:"createdAt" type:"timestamp"`
// The domain name for your SSL/TLS certificate.
DomainName *string `locationName:"domainName" type:"string"`
@ -13995,7 +13995,7 @@ type LoadBalancerTlsCertificate struct {
IsAttached *bool `locationName:"isAttached" type:"boolean"`
// The time when the SSL/TLS certificate was issued.
IssuedAt *time.Time `locationName:"issuedAt" type:"timestamp" timestampFormat:"unix"`
IssuedAt *time.Time `locationName:"issuedAt" type:"timestamp"`
// The issuer of the certificate.
Issuer *string `locationName:"issuer" type:"string"`
@ -14014,10 +14014,10 @@ type LoadBalancerTlsCertificate struct {
Name *string `locationName:"name" type:"string"`
// The timestamp when the SSL/TLS certificate expires.
NotAfter *time.Time `locationName:"notAfter" type:"timestamp" timestampFormat:"unix"`
NotAfter *time.Time `locationName:"notAfter" type:"timestamp"`
// The timestamp when the SSL/TLS certificate is first valid.
NotBefore *time.Time `locationName:"notBefore" type:"timestamp" timestampFormat:"unix"`
NotBefore *time.Time `locationName:"notBefore" type:"timestamp"`
// An object containing information about the status of Lightsail's managed
// renewal for the certificate.
@ -14051,7 +14051,7 @@ type LoadBalancerTlsCertificate struct {
RevocationReason *string `locationName:"revocationReason" type:"string" enum:"LoadBalancerTlsCertificateRevocationReason"`
// The timestamp when the SSL/TLS certificate was revoked.
RevokedAt *time.Time `locationName:"revokedAt" type:"timestamp" timestampFormat:"unix"`
RevokedAt *time.Time `locationName:"revokedAt" type:"timestamp"`
// The serial number of the certificate.
Serial *string `locationName:"serial" type:"string"`
@ -14418,7 +14418,7 @@ type MetricDatapoint struct {
Sum *float64 `locationName:"sum" type:"double"`
// The timestamp (e.g., 1479816991.349).
Timestamp *time.Time `locationName:"timestamp" type:"timestamp" timestampFormat:"unix"`
Timestamp *time.Time `locationName:"timestamp" type:"timestamp"`
// The unit.
Unit *string `locationName:"unit" type:"string" enum:"MetricUnit"`
@ -14581,7 +14581,7 @@ type Operation struct {
_ struct{} `type:"structure"`
// The timestamp when the operation was initialized (e.g., 1479816991.349).
CreatedAt *time.Time `locationName:"createdAt" type:"timestamp" timestampFormat:"unix"`
CreatedAt *time.Time `locationName:"createdAt" type:"timestamp"`
// The error code.
ErrorCode *string `locationName:"errorCode" type:"string"`
@ -14614,7 +14614,7 @@ type Operation struct {
Status *string `locationName:"status" type:"string" enum:"OperationStatus"`
// The timestamp when the status was changed (e.g., 1479816991.349).
StatusChangedAt *time.Time `locationName:"statusChangedAt" type:"timestamp" timestampFormat:"unix"`
StatusChangedAt *time.Time `locationName:"statusChangedAt" type:"timestamp"`
}
// String returns the string representation
@ -15194,7 +15194,7 @@ type StaticIp struct {
AttachedTo *string `locationName:"attachedTo" type:"string"`
// The timestamp when the static IP was created (e.g., 1479735304.222).
CreatedAt *time.Time `locationName:"createdAt" type:"timestamp" timestampFormat:"unix"`
CreatedAt *time.Time `locationName:"createdAt" type:"timestamp"`
// The static IP address.
IpAddress *string `locationName:"ipAddress" type:"string"`

View file

@ -29,8 +29,9 @@ var initRequest func(*request.Request)
// Service information constants
const (
ServiceName = "lightsail" // Service endpoint prefix API calls made to.
EndpointsID = ServiceName // Service ID for Regions and Endpoints metadata.
ServiceName = "lightsail" // Name of service.
EndpointsID = ServiceName // ID to lookup a service endpoint with.
ServiceID = "Lightsail" // ServiceID is a unique identifer of a specific service.
)
// New creates a new instance of the Lightsail client with a session.
@ -55,6 +56,7 @@ func newClient(cfg aws.Config, handlers request.Handlers, endpoint, signingRegio
cfg,
metadata.ClientInfo{
ServiceName: ServiceName,
ServiceID: ServiceID,
SigningName: signingName,
SigningRegion: signingRegion,
Endpoint: endpoint,

View file

@ -6401,7 +6401,7 @@ type ChangeInfo struct {
// at 17:48:16.751 UTC.
//
// SubmittedAt is a required field
SubmittedAt *time.Time `type:"timestamp" timestampFormat:"iso8601" required:"true"`
SubmittedAt *time.Time `type:"timestamp" required:"true"`
}
// String returns the string representation
@ -13275,7 +13275,7 @@ type StatusReport struct {
// 8601 format (https://en.wikipedia.org/wiki/ISO_8601) and Coordinated Universal
// Time (UTC). For example, the value 2017-03-27T17:48:16.751Z represents March
// 27, 2017 at 17:48:16.751 UTC.
CheckedTime *time.Time `type:"timestamp" timestampFormat:"iso8601"`
CheckedTime *time.Time `type:"timestamp"`
// A description of the status of the health check endpoint as reported by one
// of the Amazon Route 53 health checkers.

View file

@ -29,8 +29,9 @@ var initRequest func(*request.Request)
// Service information constants
const (
ServiceName = "route53" // Service endpoint prefix API calls made to.
EndpointsID = ServiceName // Service ID for Regions and Endpoints metadata.
ServiceName = "route53" // Name of service.
EndpointsID = ServiceName // ID to lookup a service endpoint with.
ServiceID = "Route 53" // ServiceID is a unique identifer of a specific service.
)
// New creates a new instance of the Route53 client with a session.
@ -55,6 +56,7 @@ func newClient(cfg aws.Config, handlers request.Handlers, endpoint, signingRegio
cfg,
metadata.ClientInfo{
ServiceName: ServiceName,
ServiceID: ServiceID,
SigningName: signingName,
SigningRegion: signingRegion,
Endpoint: endpoint,

View file

@ -1908,7 +1908,7 @@ type Credentials struct {
// The date on which the current credentials expire.
//
// Expiration is a required field
Expiration *time.Time `type:"timestamp" timestampFormat:"iso8601" required:"true"`
Expiration *time.Time `type:"timestamp" required:"true"`
// The secret access key that can be used to sign requests.
//

View file

@ -29,8 +29,9 @@ var initRequest func(*request.Request)
// Service information constants
const (
ServiceName = "sts" // Service endpoint prefix API calls made to.
EndpointsID = ServiceName // Service ID for Regions and Endpoints metadata.
ServiceName = "sts" // Name of service.
EndpointsID = ServiceName // ID to lookup a service endpoint with.
ServiceID = "STS" // ServiceID is a unique identifer of a specific service.
)
// New creates a new instance of the STS client with a session.
@ -55,6 +56,7 @@ func newClient(cfg aws.Config, handlers request.Handlers, endpoint, signingRegio
cfg,
metadata.ClientInfo{
ServiceName: ServiceName,
ServiceID: ServiceID,
SigningName: signingName,
SigningRegion: signingRegion,
Endpoint: endpoint,

View file

@ -2,7 +2,7 @@ ISC License
Copyright (c) 2012-2016 Dave Collins <dave@davec.name>
Permission to use, copy, modify, and distribute this software for any
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.

View file

@ -16,7 +16,9 @@
// when the code is not running on Google App Engine, compiled by GopherJS, and
// "-tags safe" is not added to the go build command line. The "disableunsafe"
// tag is deprecated and thus should not be used.
// +build !js,!appengine,!safe,!disableunsafe
// Go versions prior to 1.4 are disabled because they use a different layout
// for interfaces which make the implementation of unsafeReflectValue more complex.
// +build !js,!appengine,!safe,!disableunsafe,go1.4
package spew
@ -34,80 +36,49 @@ const (
ptrSize = unsafe.Sizeof((*byte)(nil))
)
var (
// offsetPtr, offsetScalar, and offsetFlag are the offsets for the
// internal reflect.Value fields. These values are valid before golang
// commit ecccf07e7f9d which changed the format. The are also valid
// after commit 82f48826c6c7 which changed the format again to mirror
// the original format. Code in the init function updates these offsets
// as necessary.
offsetPtr = uintptr(ptrSize)
offsetScalar = uintptr(0)
offsetFlag = uintptr(ptrSize * 2)
type flag uintptr
// flagKindWidth and flagKindShift indicate various bits that the
// reflect package uses internally to track kind information.
//
// flagRO indicates whether or not the value field of a reflect.Value is
// read-only.
//
// flagIndir indicates whether the value field of a reflect.Value is
// the actual data or a pointer to the data.
//
// These values are valid before golang commit 90a7c3c86944 which
// changed their positions. Code in the init function updates these
// flags as necessary.
flagKindWidth = uintptr(5)
flagKindShift = uintptr(flagKindWidth - 1)
flagRO = uintptr(1 << 0)
flagIndir = uintptr(1 << 1)
var (
// flagRO indicates whether the value field of a reflect.Value
// is read-only.
flagRO flag
// flagAddr indicates whether the address of the reflect.Value's
// value may be taken.
flagAddr flag
)
func init() {
// Older versions of reflect.Value stored small integers directly in the
// ptr field (which is named val in the older versions). Versions
// between commits ecccf07e7f9d and 82f48826c6c7 added a new field named
// scalar for this purpose which unfortunately came before the flag
// field, so the offset of the flag field is different for those
// versions.
//
// This code constructs a new reflect.Value from a known small integer
// and checks if the size of the reflect.Value struct indicates it has
// the scalar field. When it does, the offsets are updated accordingly.
vv := reflect.ValueOf(0xf00)
if unsafe.Sizeof(vv) == (ptrSize * 4) {
offsetScalar = ptrSize * 2
offsetFlag = ptrSize * 3
}
// flagKindMask holds the bits that make up the kind
// part of the flags field. In all the supported versions,
// it is in the lower 5 bits.
const flagKindMask = flag(0x1f)
// Commit 90a7c3c86944 changed the flag positions such that the low
// order bits are the kind. This code extracts the kind from the flags
// field and ensures it's the correct type. When it's not, the flag
// order has been changed to the newer format, so the flags are updated
// accordingly.
upf := unsafe.Pointer(uintptr(unsafe.Pointer(&vv)) + offsetFlag)
upfv := *(*uintptr)(upf)
flagKindMask := uintptr((1<<flagKindWidth - 1) << flagKindShift)
if (upfv&flagKindMask)>>flagKindShift != uintptr(reflect.Int) {
flagKindShift = 0
flagRO = 1 << 5
flagIndir = 1 << 6
// Different versions of Go have used different
// bit layouts for the flags type. This table
// records the known combinations.
var okFlags = []struct {
ro, addr flag
}{{
// From Go 1.4 to 1.5
ro: 1 << 5,
addr: 1 << 7,
}, {
// Up to Go tip.
ro: 1<<5 | 1<<6,
addr: 1 << 8,
}}
// Commit adf9b30e5594 modified the flags to separate the
// flagRO flag into two bits which specifies whether or not the
// field is embedded. This causes flagIndir to move over a bit
// and means that flagRO is the combination of either of the
// original flagRO bit and the new bit.
//
// This code detects the change by extracting what used to be
// the indirect bit to ensure it's set. When it's not, the flag
// order has been changed to the newer format, so the flags are
// updated accordingly.
if upfv&flagIndir == 0 {
flagRO = 3 << 5
flagIndir = 1 << 7
}
var flagValOffset = func() uintptr {
field, ok := reflect.TypeOf(reflect.Value{}).FieldByName("flag")
if !ok {
panic("reflect.Value has no flag field")
}
return field.Offset
}()
// flagField returns a pointer to the flag field of a reflect.Value.
func flagField(v *reflect.Value) *flag {
return (*flag)(unsafe.Pointer(uintptr(unsafe.Pointer(v)) + flagValOffset))
}
// unsafeReflectValue converts the passed reflect.Value into a one that bypasses
@ -119,34 +90,56 @@ func init() {
// This allows us to check for implementations of the Stringer and error
// interfaces to be used for pretty printing ordinarily unaddressable and
// inaccessible values such as unexported struct fields.
func unsafeReflectValue(v reflect.Value) (rv reflect.Value) {
indirects := 1
vt := v.Type()
upv := unsafe.Pointer(uintptr(unsafe.Pointer(&v)) + offsetPtr)
rvf := *(*uintptr)(unsafe.Pointer(uintptr(unsafe.Pointer(&v)) + offsetFlag))
if rvf&flagIndir != 0 {
vt = reflect.PtrTo(v.Type())
indirects++
} else if offsetScalar != 0 {
// The value is in the scalar field when it's not one of the
// reference types.
switch vt.Kind() {
case reflect.Uintptr:
case reflect.Chan:
case reflect.Func:
case reflect.Map:
case reflect.Ptr:
case reflect.UnsafePointer:
default:
upv = unsafe.Pointer(uintptr(unsafe.Pointer(&v)) +
offsetScalar)
func unsafeReflectValue(v reflect.Value) reflect.Value {
if !v.IsValid() || (v.CanInterface() && v.CanAddr()) {
return v
}
}
pv := reflect.NewAt(vt, upv)
rv = pv
for i := 0; i < indirects; i++ {
rv = rv.Elem()
}
return rv
flagFieldPtr := flagField(&v)
*flagFieldPtr &^= flagRO
*flagFieldPtr |= flagAddr
return v
}
// Sanity checks against future reflect package changes
// to the type or semantics of the Value.flag field.
func init() {
field, ok := reflect.TypeOf(reflect.Value{}).FieldByName("flag")
if !ok {
panic("reflect.Value has no flag field")
}
if field.Type.Kind() != reflect.TypeOf(flag(0)).Kind() {
panic("reflect.Value flag field has changed kind")
}
type t0 int
var t struct {
A t0
// t0 will have flagEmbedRO set.
t0
// a will have flagStickyRO set
a t0
}
vA := reflect.ValueOf(t).FieldByName("A")
va := reflect.ValueOf(t).FieldByName("a")
vt0 := reflect.ValueOf(t).FieldByName("t0")
// Infer flagRO from the difference between the flags
// for the (otherwise identical) fields in t.
flagPublic := *flagField(&vA)
flagWithRO := *flagField(&va) | *flagField(&vt0)
flagRO = flagPublic ^ flagWithRO
// Infer flagAddr from the difference between a value
// taken from a pointer and not.
vPtrA := reflect.ValueOf(&t).Elem().FieldByName("A")
flagNoPtr := *flagField(&vA)
flagPtr := *flagField(&vPtrA)
flagAddr = flagNoPtr ^ flagPtr
// Check that the inferred flags tally with one of the known versions.
for _, f := range okFlags {
if flagRO == f.ro && flagAddr == f.addr {
return
}
}
panic("reflect.Value read-only flag has changed semantics")
}

View file

@ -16,7 +16,7 @@
// when the code is running on Google App Engine, compiled by GopherJS, or
// "-tags safe" is added to the go build command line. The "disableunsafe"
// tag is deprecated and thus should not be used.
// +build js appengine safe disableunsafe
// +build js appengine safe disableunsafe !go1.4
package spew

View file

@ -180,7 +180,7 @@ func printComplex(w io.Writer, c complex128, floatPrecision int) {
w.Write(closeParenBytes)
}
// printHexPtr outputs a uintptr formatted as hexidecimal with a leading '0x'
// printHexPtr outputs a uintptr formatted as hexadecimal with a leading '0x'
// prefix to Writer w.
func printHexPtr(w io.Writer, p uintptr) {
// Null pointer.

View file

@ -35,16 +35,16 @@ var (
// cCharRE is a regular expression that matches a cgo char.
// It is used to detect character arrays to hexdump them.
cCharRE = regexp.MustCompile("^.*\\._Ctype_char$")
cCharRE = regexp.MustCompile(`^.*\._Ctype_char$`)
// cUnsignedCharRE is a regular expression that matches a cgo unsigned
// char. It is used to detect unsigned character arrays to hexdump
// them.
cUnsignedCharRE = regexp.MustCompile("^.*\\._Ctype_unsignedchar$")
cUnsignedCharRE = regexp.MustCompile(`^.*\._Ctype_unsignedchar$`)
// cUint8tCharRE is a regular expression that matches a cgo uint8_t.
// It is used to detect uint8_t arrays to hexdump them.
cUint8tCharRE = regexp.MustCompile("^.*\\._Ctype_uint8_t$")
cUint8tCharRE = regexp.MustCompile(`^.*\._Ctype_uint8_t$`)
)
// dumpState contains information about the state of a dump operation.
@ -143,10 +143,10 @@ func (d *dumpState) dumpPtr(v reflect.Value) {
// Display dereferenced value.
d.w.Write(openParenBytes)
switch {
case nilFound == true:
case nilFound:
d.w.Write(nilAngleBytes)
case cycleFound == true:
case cycleFound:
d.w.Write(circularBytes)
default:

View file

@ -182,10 +182,10 @@ func (f *formatState) formatPtr(v reflect.Value) {
// Display dereferenced value.
switch {
case nilFound == true:
case nilFound:
f.fs.Write(nilAngleBytes)
case cycleFound == true:
case cycleFound:
f.fs.Write(circularShortBytes)
default:

View file

@ -231,7 +231,7 @@ func (c *Client) Do(req *http.Request, obj interface{}) (*http.Response, error)
// the response body is decoded into v.
if obj != nil {
if w, ok := obj.(io.Writer); ok {
io.Copy(w, resp.Body)
_, err = io.Copy(w, resp.Body)
} else {
err = json.NewDecoder(resp.Body).Decode(obj)
}

View file

@ -6,3 +6,4 @@ Sebastien Goasguen
Yoan Blanc
Stefano Marengo
Pierre-Emmanuel Jacquier
Fabrizio Steiner

View file

@ -1,25 +1,161 @@
package egoscale
func (*ListAccounts) name() string {
return "listAccounts"
import "fmt"
// AccountType represents the type of an Account
//
// http://docs.cloudstack.apache.org/projects/cloudstack-administration/en/4.8/accounts.html#accounts-users-and-domains
type AccountType int16
//go:generate stringer -type AccountType
const (
// UserAccount represents a User
UserAccount AccountType = 0
// AdminAccount represents an Admin
AdminAccount AccountType = 1
// DomainAdminAccount represents a Domain Admin
DomainAdminAccount AccountType = 2
)
// Account provides the detailed account information
type Account struct {
AccountDetails map[string]string `json:"accountdetails,omitempty" doc:"details for the account"`
AccountType AccountType `json:"accounttype,omitempty" doc:"account type (admin, domain-admin, user)"`
CPUAvailable string `json:"cpuavailable,omitempty" doc:"the total number of cpu cores available to be created for this account"`
CPULimit string `json:"cpulimit,omitempty" doc:"the total number of cpu cores the account can own"`
CPUTotal int64 `json:"cputotal,omitempty" doc:"the total number of cpu cores owned by account"`
DefaultZoneID *UUID `json:"defaultzoneid,omitempty" doc:"the default zone of the account"`
Domain string `json:"domain,omitempty" doc:"name of the Domain the account belongs too"`
DomainID *UUID `json:"domainid,omitempty" doc:"id of the Domain the account belongs too"`
EipLimit string `json:"eiplimit,omitempty" doc:"the total number of public elastic ip addresses this account can acquire"`
Groups []string `json:"groups,omitempty" doc:"the list of acl groups that account belongs to"`
ID *UUID `json:"id,omitempty" doc:"the id of the account"`
IPAvailable string `json:"ipavailable,omitempty" doc:"the total number of public ip addresses available for this account to acquire"`
IPLimit string `json:"iplimit,omitempty" doc:"the total number of public ip addresses this account can acquire"`
IPTotal int64 `json:"iptotal,omitempty" doc:"the total number of public ip addresses allocated for this account"`
IsCleanupRequired bool `json:"iscleanuprequired,omitempty" doc:"true if the account requires cleanup"`
IsDefault bool `json:"isdefault,omitempty" doc:"true if account is default, false otherwise"`
MemoryAvailable string `json:"memoryavailable,omitempty" doc:"the total memory (in MB) available to be created for this account"`
MemoryLimit string `json:"memorylimit,omitempty" doc:"the total memory (in MB) the account can own"`
MemoryTotal int64 `json:"memorytotal,omitempty" doc:"the total memory (in MB) owned by account"`
Name string `json:"name,omitempty" doc:"the name of the account"`
NetworkAvailable string `json:"networkavailable,omitempty" doc:"the total number of networks available to be created for this account"`
NetworkDomain string `json:"networkdomain,omitempty" doc:"the network domain"`
NetworkLimit string `json:"networklimit,omitempty" doc:"the total number of networks the account can own"`
NetworkTotal int64 `json:"networktotal,omitempty" doc:"the total number of networks owned by account"`
PrimaryStorageAvailable string `json:"primarystorageavailable,omitempty" doc:"the total primary storage space (in GiB) available to be used for this account"`
PrimaryStorageLimit string `json:"primarystoragelimit,omitempty" doc:"the total primary storage space (in GiB) the account can own"`
PrimaryStorageTotal int64 `json:"primarystoragetotal,omitempty" doc:"the total primary storage space (in GiB) owned by account"`
ProjectAvailable string `json:"projectavailable,omitempty" doc:"the total number of projects available for administration by this account"`
ProjectLimit string `json:"projectlimit,omitempty" doc:"the total number of projects the account can own"`
ProjectTotal int64 `json:"projecttotal,omitempty" doc:"the total number of projects being administrated by this account"`
SecondaryStorageAvailable string `json:"secondarystorageavailable,omitempty" doc:"the total secondary storage space (in GiB) available to be used for this account"`
SecondaryStorageLimit string `json:"secondarystoragelimit,omitempty" doc:"the total secondary storage space (in GiB) the account can own"`
SecondaryStorageTotal int64 `json:"secondarystoragetotal,omitempty" doc:"the total secondary storage space (in GiB) owned by account"`
SMTP bool `json:"smtp,omitempty" doc:"if SMTP outbound is allowed"`
SnapshotAvailable string `json:"snapshotavailable,omitempty" doc:"the total number of snapshots available for this account"`
SnapshotLimit string `json:"snapshotlimit,omitempty" doc:"the total number of snapshots which can be stored by this account"`
SnapshotTotal int64 `json:"snapshottotal,omitempty" doc:"the total number of snapshots stored by this account"`
State string `json:"state,omitempty" doc:"the state of the account"`
TemplateAvailable string `json:"templateavailable,omitempty" doc:"the total number of templates available to be created by this account"`
TemplateLimit string `json:"templatelimit,omitempty" doc:"the total number of templates which can be created by this account"`
TemplateTotal int64 `json:"templatetotal,omitempty" doc:"the total number of templates which have been created by this account"`
User []User `json:"user,omitempty" doc:"the list of users associated with account"`
VMAvailable string `json:"vmavailable,omitempty" doc:"the total number of virtual machines available for this account to acquire"`
VMLimit string `json:"vmlimit,omitempty" doc:"the total number of virtual machines that can be deployed by this account"`
VMRunning int `json:"vmrunning,omitempty" doc:"the total number of virtual machines running for this account"`
VMStopped int `json:"vmstopped,omitempty" doc:"the total number of virtual machines stopped for this account"`
VMTotal int64 `json:"vmtotal,omitempty" doc:"the total number of virtual machines deployed by this account"`
VolumeAvailable string `json:"volumeavailable,omitempty" doc:"the total volume available for this account"`
VolumeLimit string `json:"volumelimit,omitempty" doc:"the total volume which can be used by this account"`
VolumeTotal int64 `json:"volumetotal,omitempty" doc:"the total volume being used by this account"`
}
func (*ListAccounts) response() interface{} {
// ListRequest builds the ListAccountsGroups request
func (a Account) ListRequest() (ListCommand, error) {
return &ListAccounts{
ID: a.ID,
DomainID: a.DomainID,
AccountType: a.AccountType,
State: a.State,
}, nil
}
// ListAccounts represents a query to display the accounts
type ListAccounts struct {
AccountType AccountType `json:"accounttype,omitempty" doc:"list accounts by account type. Valid account types are 1 (admin), 2 (domain-admin), and 0 (user)."`
DomainID *UUID `json:"domainid,omitempty" doc:"list only resources belonging to the domain specified"`
ID *UUID `json:"id,omitempty" doc:"list account by account ID"`
IsCleanUpRequired *bool `json:"iscleanuprequired,omitempty" doc:"list accounts by cleanuprequired attribute (values are true or false)"`
IsRecursive *bool `json:"isrecursive,omitempty" doc:"defaults to false, but if true, lists all resources from the parent specified by the domainId till leaves."`
Keyword string `json:"keyword,omitempty" doc:"List by keyword"`
ListAll *bool `json:"listall,omitempty" doc:"If set to false, list only resources belonging to the command's caller; if set to true - list resources that the caller is authorized to see. Default value is false"`
Name string `json:"name,omitempty" doc:"list account by account name"`
Page int `json:"page,omitempty"`
PageSize int `json:"pagesize,omitempty"`
State string `json:"state,omitempty" doc:"list accounts by state. Valid states are enabled, disabled, and locked."`
_ bool `name:"listAccounts" description:"Lists accounts and provides detailed account information for listed accounts"`
}
func (ListAccounts) response() interface{} {
return new(ListAccountsResponse)
}
func (*EnableAccount) name() string {
return "enableAccount"
// SetPage sets the current page
func (ls *ListAccounts) SetPage(page int) {
ls.Page = page
}
func (*EnableAccount) response() interface{} {
return new(EnableAccountResponse)
// SetPageSize sets the page size
func (ls *ListAccounts) SetPageSize(pageSize int) {
ls.PageSize = pageSize
}
func (*DisableAccount) name() string {
return "disableAccount"
func (ListAccounts) each(resp interface{}, callback IterateItemFunc) {
vms, ok := resp.(*ListAccountsResponse)
if !ok {
callback(nil, fmt.Errorf("wrong type. ListAccountsResponse expected, got %T", resp))
return
}
for i := range vms.Account {
if !callback(&vms.Account[i], nil) {
break
}
}
}
func (*DisableAccount) asyncResponse() interface{} {
return new(DisableAccountResponse)
// ListAccountsResponse represents a list of accounts
type ListAccountsResponse struct {
Count int `json:"count"`
Account []Account `json:"account"`
}
// EnableAccount represents the activation of an account
type EnableAccount struct {
Account string `json:"account,omitempty" doc:"Enables specified account."`
DomainID *UUID `json:"domainid,omitempty" doc:"Enables specified account in this domain."`
ID *UUID `json:"id,omitempty" doc:"Account id"`
_ bool `name:"enableAccount" description:"Enables an account"`
}
func (EnableAccount) response() interface{} {
return new(Account)
}
// DisableAccount (Async) represents the deactivation of an account
type DisableAccount struct {
Lock *bool `json:"lock" doc:"If true, only lock the account; else disable the account"`
Account string `json:"account,omitempty" doc:"Disables specified account."`
DomainID *UUID `json:"domainid,omitempty" doc:"Disables specified account in this domain."`
ID *UUID `json:"id,omitempty" doc:"Account id"`
_ bool `name:"disableAccount" description:"Disables an account"`
}
func (DisableAccount) response() interface{} {
return new(AsyncJobResult)
}
func (DisableAccount) asyncResponse() interface{} {
return new(Account)
}

View file

@ -1,120 +0,0 @@
package egoscale
// AccountType represents the type of an Account
//
// http://docs.cloudstack.apache.org/projects/cloudstack-administration/en/4.8/accounts.html#accounts-users-and-domains
type AccountType int16
//go:generate stringer -type AccountType
const (
// UserAccount represents a User
UserAccount AccountType = iota
// AdminAccount represents an Admin
AdminAccount
// DomainAdminAccount represents a Domain Admin
DomainAdminAccount
)
// Account provides the detailed account information
type Account struct {
AccountDetails map[string]string `json:"accountdetails,omitempty" doc:"details for the account"`
AccountType AccountType `json:"accounttype,omitempty" doc:"account type (admin, domain-admin, user)"`
CPUAvailable string `json:"cpuavailable,omitempty" doc:"the total number of cpu cores available to be created for this account"`
CPULimit string `json:"cpulimit,omitempty" doc:"the total number of cpu cores the account can own"`
CPUTotal int64 `json:"cputotal,omitempty" doc:"the total number of cpu cores owned by account"`
DefaultZoneID string `json:"defaultzoneid,omitempty" doc:"the default zone of the account"`
Domain string `json:"domain,omitempty" doc:"name of the Domain the account belongs too"`
DomainID string `json:"domainid,omitempty" doc:"id of the Domain the account belongs too"`
EipLimit string `json:"eiplimit,omitempty" doc:"the total number of public elastic ip addresses this account can acquire"`
Groups []string `json:"groups,omitempty" doc:"the list of acl groups that account belongs to"`
ID string `json:"id,omitempty" doc:"the id of the account"`
IPAvailable string `json:"ipavailable,omitempty" doc:"the total number of public ip addresses available for this account to acquire"`
IPLimit string `json:"iplimit,omitempty" doc:"the total number of public ip addresses this account can acquire"`
IPTotal int64 `json:"iptotal,omitempty" doc:"the total number of public ip addresses allocated for this account"`
IsCleanupRequired bool `json:"iscleanuprequired,omitempty" doc:"true if the account requires cleanup"`
IsDefault bool `json:"isdefault,omitempty" doc:"true if account is default, false otherwise"`
MemoryAvailable string `json:"memoryavailable,omitempty" doc:"the total memory (in MB) available to be created for this account"`
MemoryLimit string `json:"memorylimit,omitempty" doc:"the total memory (in MB) the account can own"`
MemoryTotal int64 `json:"memorytotal,omitempty" doc:"the total memory (in MB) owned by account"`
Name string `json:"name,omitempty" doc:"the name of the account"`
NetworkAvailable string `json:"networkavailable,omitempty" doc:"the total number of networks available to be created for this account"`
NetworkDomain string `json:"networkdomain,omitempty" doc:"the network domain"`
NetworkLimit string `json:"networklimit,omitempty" doc:"the total number of networks the account can own"`
NetworkTotal int64 `json:"networktotal,omitempty" doc:"the total number of networks owned by account"`
PrimaryStorageAvailable string `json:"primarystorageavailable,omitempty" doc:"the total primary storage space (in GiB) available to be used for this account"`
PrimaryStorageLimit string `json:"primarystoragelimit,omitempty" doc:"the total primary storage space (in GiB) the account can own"`
PrimaryStorageTotal int64 `json:"primarystoragetotal,omitempty" doc:"the total primary storage space (in GiB) owned by account"`
ProjectAvailable string `json:"projectavailable,omitempty" doc:"the total number of projects available for administration by this account"`
ProjectLimit string `json:"projectlimit,omitempty" doc:"the total number of projects the account can own"`
ProjectTotal int64 `json:"projecttotal,omitempty" doc:"the total number of projects being administrated by this account"`
SecondaryStorageAvailable string `json:"secondarystorageavailable,omitempty" doc:"the total secondary storage space (in GiB) available to be used for this account"`
SecondaryStorageLimit string `json:"secondarystoragelimit,omitempty" doc:"the total secondary storage space (in GiB) the account can own"`
SecondaryStorageTotal int64 `json:"secondarystoragetotal,omitempty" doc:"the total secondary storage space (in GiB) owned by account"`
SMTP bool `json:"smtp,omitempty" doc:"if SMTP outbound is allowed"`
SnapshotAvailable string `json:"snapshotavailable,omitempty" doc:"the total number of snapshots available for this account"`
SnapshotLimit string `json:"snapshotlimit,omitempty" doc:"the total number of snapshots which can be stored by this account"`
SnapshotTotal int64 `json:"snapshottotal,omitempty" doc:"the total number of snapshots stored by this account"`
State string `json:"state,omitempty" doc:"the state of the account"`
TemplateAvailable string `json:"templateavailable,omitempty" doc:"the total number of templates available to be created by this account"`
TemplateLimit string `json:"templatelimit,omitempty" doc:"the total number of templates which can be created by this account"`
TemplateTotal int64 `json:"templatetotal,omitempty" doc:"the total number of templates which have been created by this account"`
User []User `json:"user,omitempty" doc:"the list of users associated with account"`
VMAvailable string `json:"vmavailable,omitempty" doc:"the total number of virtual machines available for this account to acquire"`
VMLimit string `json:"vmlimit,omitempty" doc:"the total number of virtual machines that can be deployed by this account"`
VMRunning int `json:"vmrunning,omitempty" doc:"the total number of virtual machines running for this account"`
VMStopped int `json:"vmstopped,omitempty" doc:"the total number of virtual machines stopped for this account"`
VMTotal int64 `json:"vmtotal,omitempty" doc:"the total number of virtual machines deployed by this account"`
VolumeAvailable string `json:"volumeavailable,omitempty" doc:"the total volume available for this account"`
VolumeLimit string `json:"volumelimit,omitempty" doc:"the total volume which can be used by this account"`
VolumeTotal int64 `json:"volumetotal,omitempty" doc:"the total volume being used by this account"`
}
// ListAccounts represents a query to display the accounts
//
// CloudStack API: http://cloudstack.apache.org/api/apidocs-4.10/apis/listAccounts.html
type ListAccounts struct {
AccountType AccountType `json:"accounttype,omitempty" doc:"list accounts by account type. Valid account types are 1 (admin), 2 (domain-admin), and 0 (user)."`
DomainID string `json:"domainid,omitempty" doc:"list only resources belonging to the domain specified"`
ID string `json:"id,omitempty" doc:"list account by account ID"`
IsCleanUpRequired *bool `json:"iscleanuprequired,omitempty" doc:"list accounts by cleanuprequred attribute (values are true or false)"`
IsRecursive *bool `json:"isrecursive,omitempty" doc:"defaults to false, but if true, lists all resources from the parent specified by the domainId till leaves."`
Keyword string `json:"keyword,omitempty" doc:"List by keyword"`
ListAll *bool `json:"listall,omitempty" doc:"If set to false, list only resources belonging to the command's caller; if set to true - list resources that the caller is authorized to see. Default value is false"`
Name string `json:"name,omitempty" doc:"list account by account name"`
Page int `json:"page,omitempty"`
PageSize int `json:"pagesize,omitempty"`
State string `json:"state,omitempty" doc:"list accounts by state. Valid states are enabled, disabled, and locked."`
}
// ListAccountsResponse represents a list of accounts
type ListAccountsResponse struct {
Count int `json:"count"`
Account []Account `json:"account"`
}
// EnableAccount represents the activation of an account
//
// CloudStack API: http://cloudstack.apache.org/api/apidocs-4.10/apis/enableAccount.html
type EnableAccount struct {
Account string `json:"account,omitempty" doc:"Enables specified account."`
DomainID string `json:"domainid,omitempty" doc:"Enables specified account in this domain."`
ID string `json:"id,omitempty" doc:"Account id"`
}
// EnableAccountResponse represents the modified account
type EnableAccountResponse struct {
Account Account `json:"account"`
}
// DisableAccount (Async) represents the deactivation of an account
//
// CloudStack API: http://cloudstack.apache.org/api/apidocs-4.10/apis/disableAccount.html
type DisableAccount struct {
Lock *bool `json:"lock" doc:"If true, only lock the account; else disable the account"`
Account string `json:"account,omitempty" doc:"Disables specified account."`
DomainID string `json:"domainid,omitempty" doc:"Disables specified account in this domain."`
ID string `json:"id,omitempty" doc:"Account id"`
}
// DisableAccountResponse represents the modified account
type DisableAccountResponse EnableAccountResponse

View file

@ -3,51 +3,80 @@ package egoscale
import (
"context"
"fmt"
"github.com/jinzhu/copier"
"net"
)
// Get fetches the resource
func (ipaddress *IPAddress) Get(ctx context.Context, client *Client) error {
if ipaddress.ID == "" && ipaddress.IPAddress == nil {
return fmt.Errorf("An IPAddress may only be searched using ID or IPAddress")
}
// IPAddress represents an IP Address
type IPAddress struct {
Account string `json:"account,omitempty" doc:"the account the public IP address is associated with"`
Allocated string `json:"allocated,omitempty" doc:"date the public IP address was acquired"`
Associated string `json:"associated,omitempty" doc:"date the public IP address was associated"`
AssociatedNetworkID *UUID `json:"associatednetworkid,omitempty" doc:"the ID of the Network associated with the IP address"`
AssociatedNetworkName string `json:"associatednetworkname,omitempty" doc:"the name of the Network associated with the IP address"`
Domain string `json:"domain,omitempty" doc:"the domain the public IP address is associated with"`
DomainID *UUID `json:"domainid,omitempty" doc:"the domain ID the public IP address is associated with"`
ForDisplay bool `json:"fordisplay,omitempty" doc:"is public ip for display to the regular user"`
ForVirtualNetwork bool `json:"forvirtualnetwork,omitempty" doc:"the virtual network for the IP address"`
ID *UUID `json:"id,omitempty" doc:"public IP address id"`
IPAddress net.IP `json:"ipaddress,omitempty" doc:"public IP address"`
IsElastic bool `json:"iselastic,omitempty" doc:"is an elastic ip"`
IsPortable bool `json:"isportable,omitempty" doc:"is public IP portable across the zones"`
IsSourceNat bool `json:"issourcenat,omitempty" doc:"true if the IP address is a source nat address, false otherwise"`
IsStaticNat *bool `json:"isstaticnat,omitempty" doc:"true if this ip is for static nat, false otherwise"`
IsSystem bool `json:"issystem,omitempty" doc:"true if this ip is system ip (was allocated as a part of deployVm or createLbRule)"`
NetworkID *UUID `json:"networkid,omitempty" doc:"the ID of the Network where ip belongs to"`
PhysicalNetworkID *UUID `json:"physicalnetworkid,omitempty" doc:"the physical network this belongs to"`
Purpose string `json:"purpose,omitempty" doc:"purpose of the IP address. In Acton this value is not null for Ips with isSystem=true, and can have either StaticNat or LB value"`
ReverseDNS []ReverseDNS `json:"reversedns,omitempty" doc:"the list of PTR record(s) associated with the ip address"`
State string `json:"state,omitempty" doc:"State of the ip address. Can be: Allocatin, Allocated and Releasing"`
Tags []ResourceTag `json:"tags,omitempty" doc:"the list of resource tags associated with ip address"`
VirtualMachineDisplayName string `json:"virtualmachinedisplayname,omitempty" doc:"virtual machine display name the ip address is assigned to (not null only for static nat Ip)"`
VirtualMachineID *UUID `json:"virtualmachineid,omitempty" doc:"virtual machine id the ip address is assigned to (not null only for static nat Ip)"`
VirtualMachineName string `json:"virtualmachinename,omitempty" doc:"virtual machine name the ip address is assigned to (not null only for static nat Ip)"`
VlanID *UUID `json:"vlanid,omitempty" doc:"the ID of the VLAN associated with the IP address. This parameter is visible to ROOT admins only"`
VlanName string `json:"vlanname,omitempty" doc:"the VLAN associated with the IP address"`
VMIPAddress net.IP `json:"vmipaddress,omitempty" doc:"virtual machine (dnat) ip address (not null only for static nat Ip)"`
ZoneID *UUID `json:"zoneid,omitempty" doc:"the ID of the zone the public IP address belongs to"`
ZoneName string `json:"zonename,omitempty" doc:"the name of the zone the public IP address belongs to"`
}
// ResourceType returns the type of the resource
func (IPAddress) ResourceType() string {
return "PublicIpAddress"
}
// ListRequest builds the ListAdresses request
func (ipaddress IPAddress) ListRequest() (ListCommand, error) {
req := &ListPublicIPAddresses{
Account: ipaddress.Account,
AssociatedNetworkID: ipaddress.AssociatedNetworkID,
DomainID: ipaddress.DomainID,
ID: ipaddress.ID,
IPAddress: ipaddress.IPAddress,
Account: ipaddress.Account,
DomainID: ipaddress.DomainID,
PhysicalNetworkID: ipaddress.PhysicalNetworkID,
VlanID: ipaddress.VlanID,
ZoneID: ipaddress.ZoneID,
}
if ipaddress.IsElastic {
req.IsElastic = &(ipaddress.IsElastic)
req.IsElastic = &ipaddress.IsElastic
}
if ipaddress.IsSourceNat {
req.IsSourceNat = &ipaddress.IsSourceNat
}
if ipaddress.ForDisplay {
req.ForDisplay = &ipaddress.ForDisplay
}
if ipaddress.ForVirtualNetwork {
req.ForVirtualNetwork = &ipaddress.ForVirtualNetwork
}
resp, err := client.RequestWithContext(ctx, req)
if err != nil {
return err
}
ips := resp.(*ListPublicIPAddressesResponse)
count := len(ips.PublicIPAddress)
if count == 0 {
return &ErrorResponse{
ErrorCode: ParamError,
ErrorText: fmt.Sprintf("PublicIPAddress not found. id: %s, ipaddress: %s", ipaddress.ID, ipaddress.IPAddress),
}
} else if count > 1 {
return fmt.Errorf("More than one PublicIPAddress was found")
}
return copier.Copy(ipaddress, ips.PublicIPAddress[0])
return req, nil
}
// Delete removes the resource
func (ipaddress *IPAddress) Delete(ctx context.Context, client *Client) error {
if ipaddress.ID == "" {
return fmt.Errorf("An IPAddress may only be deleted using ID")
func (ipaddress IPAddress) Delete(ctx context.Context, client *Client) error {
if ipaddress.ID == nil {
return fmt.Errorf("an IPAddress may only be deleted using ID")
}
return client.BooleanRequestWithContext(ctx, &DisassociateIPAddress{
@ -55,65 +84,92 @@ func (ipaddress *IPAddress) Delete(ctx context.Context, client *Client) error {
})
}
// ResourceType returns the type of the resource
func (*IPAddress) ResourceType() string {
return "PublicIpAddress"
// AssociateIPAddress (Async) represents the IP creation
type AssociateIPAddress struct {
Account string `json:"account,omitempty" doc:"the account to associate with this IP address"`
DomainID *UUID `json:"domainid,omitempty" doc:"the ID of the domain to associate with this IP address"`
ForDisplay *bool `json:"fordisplay,omitempty" doc:"an optional field, whether to the display the ip to the end user or not"`
IsPortable *bool `json:"isportable,omitempty" doc:"should be set to true if public IP is required to be transferable across zones, if not specified defaults to false"`
NetworkdID *UUID `json:"networkid,omitempty" doc:"The network this ip address should be associated to."`
RegionID int `json:"regionid,omitempty" doc:"region ID from where portable ip is to be associated."`
ZoneID *UUID `json:"zoneid,omitempty" doc:"the ID of the availability zone you want to acquire an public IP address from"`
_ bool `name:"associateIpAddress" description:"Acquires and associates a public IP to an account."`
}
// name returns the CloudStack API command name
func (*AssociateIPAddress) name() string {
return "associateIpAddress"
func (AssociateIPAddress) response() interface{} {
return new(AsyncJobResult)
}
func (*AssociateIPAddress) asyncResponse() interface{} {
return new(AssociateIPAddressResponse)
func (AssociateIPAddress) asyncResponse() interface{} {
return new(IPAddress)
}
// name returns the CloudStack API command name
func (*DisassociateIPAddress) name() string {
return "disassociateIpAddress"
// DisassociateIPAddress (Async) represents the IP deletion
type DisassociateIPAddress struct {
ID *UUID `json:"id" doc:"the id of the public ip address to disassociate"`
_ bool `name:"disassociateIpAddress" description:"Disassociates an ip address from the account."`
}
func (*DisassociateIPAddress) asyncResponse() interface{} {
func (DisassociateIPAddress) response() interface{} {
return new(AsyncJobResult)
}
func (DisassociateIPAddress) asyncResponse() interface{} {
return new(booleanResponse)
}
// name returns the CloudStack API command name
func (*UpdateIPAddress) name() string {
return "updateIpAddress"
}
func (*UpdateIPAddress) asyncResponse() interface{} {
return new(UpdateIPAddressResponse)
// UpdateIPAddress (Async) represents the IP modification
type UpdateIPAddress struct {
ID *UUID `json:"id" doc:"the id of the public ip address to update"`
CustomID *UUID `json:"customid,omitempty" doc:"an optional field, in case you want to set a custom id to the resource. Allowed to Root Admins only"`
ForDisplay *bool `json:"fordisplay,omitempty" doc:"an optional field, whether to the display the ip to the end user or not"`
_ bool `name:"updateIpAddress" description:"Updates an ip address"`
}
// name returns the CloudStack API command name
func (*ListPublicIPAddresses) name() string {
return "listPublicIpAddresses"
func (UpdateIPAddress) response() interface{} {
return new(AsyncJobResult)
}
func (*ListPublicIPAddresses) response() interface{} {
func (UpdateIPAddress) asyncResponse() interface{} {
return new(IPAddress)
}
// ListPublicIPAddresses represents a search for public IP addresses
type ListPublicIPAddresses struct {
Account string `json:"account,omitempty" doc:"list resources by account. Must be used with the domainId parameter."`
AllocatedOnly *bool `json:"allocatedonly,omitempty" doc:"limits search results to allocated public IP addresses"`
AssociatedNetworkID *UUID `json:"associatednetworkid,omitempty" doc:"lists all public IP addresses associated to the network specified"`
DomainID *UUID `json:"domainid,omitempty" doc:"list only resources belonging to the domain specified"`
ForDisplay *bool `json:"fordisplay,omitempty" doc:"list resources by display flag; only ROOT admin is eligible to pass this parameter"`
ForLoadBalancing *bool `json:"forloadbalancing,omitempty" doc:"list only ips used for load balancing"`
ForVirtualNetwork *bool `json:"forvirtualnetwork,omitempty" doc:"the virtual network for the IP address"`
ID *UUID `json:"id,omitempty" doc:"lists ip address by id"`
IPAddress net.IP `json:"ipaddress,omitempty" doc:"lists the specified IP address"`
IsElastic *bool `json:"iselastic,omitempty" doc:"list only elastic ip addresses"`
IsRecursive *bool `json:"isrecursive,omitempty" doc:"defaults to false, but if true, lists all resources from the parent specified by the domainId till leaves."`
IsSourceNat *bool `json:"issourcenat,omitempty" doc:"list only source nat ip addresses"`
IsStaticNat *bool `json:"isstaticnat,omitempty" doc:"list only static nat ip addresses"`
Keyword string `json:"keyword,omitempty" doc:"List by keyword"`
ListAll *bool `json:"listall,omitempty" doc:"If set to false, list only resources belonging to the command's caller; if set to true - list resources that the caller is authorized to see. Default value is false"`
Page int `json:"page,omitempty"`
PageSize int `json:"pagesize,omitempty"`
PhysicalNetworkID *UUID `json:"physicalnetworkid,omitempty" doc:"lists all public IP addresses by physical network id"`
Tags []ResourceTag `json:"tags,omitempty" doc:"List resources by tags (key/value pairs)"`
VlanID *UUID `json:"vlanid,omitempty" doc:"lists all public IP addresses by VLAN ID"`
ZoneID *UUID `json:"zoneid,omitempty" doc:"lists all public IP addresses by Zone ID"`
_ bool `name:"listPublicIpAddresses" description:"Lists all public ip addresses"`
}
// ListPublicIPAddressesResponse represents a list of public IP addresses
type ListPublicIPAddressesResponse struct {
Count int `json:"count"`
PublicIPAddress []IPAddress `json:"publicipaddress"`
}
func (ListPublicIPAddresses) response() interface{} {
return new(ListPublicIPAddressesResponse)
}
// ListRequest builds the ListAdresses request
func (ipaddress *IPAddress) ListRequest() (ListCommand, error) {
req := &ListPublicIPAddresses{
Account: ipaddress.Account,
AssociatedNetworkID: ipaddress.AssociatedNetworkID,
DomainID: ipaddress.DomainID,
ForDisplay: &ipaddress.ForDisplay,
ForVirtualNetwork: &ipaddress.ForVirtualNetwork,
ID: ipaddress.ID,
IPAddress: ipaddress.IPAddress,
IsElastic: &ipaddress.IsElastic,
IsSourceNat: &ipaddress.IsSourceNat,
PhysicalNetworkID: ipaddress.PhysicalNetworkID,
VlanID: ipaddress.VlanID,
ZoneID: ipaddress.ZoneID,
}
return req, nil
}
// SetPage sets the current page
func (ls *ListPublicIPAddresses) SetPage(page int) {
ls.Page = page
@ -124,10 +180,10 @@ func (ls *ListPublicIPAddresses) SetPageSize(pageSize int) {
ls.PageSize = pageSize
}
func (*ListPublicIPAddresses) each(resp interface{}, callback IterateItemFunc) {
func (ListPublicIPAddresses) each(resp interface{}, callback IterateItemFunc) {
ips, ok := resp.(*ListPublicIPAddressesResponse)
if !ok {
callback(nil, fmt.Errorf("ListPublicIPAddressesResponse expected, got %t", resp))
callback(nil, fmt.Errorf("wrong type. ListPublicIPAddressesResponse expected, got %T", resp))
return
}

View file

@ -1,108 +0,0 @@
package egoscale
import (
"net"
)
// IPAddress represents an IP Address
type IPAddress struct {
Account string `json:"account,omitempty" doc:"the account the public IP address is associated with"`
Allocated string `json:"allocated,omitempty" doc:"date the public IP address was acquired"`
Associated string `json:"associated,omitempty" doc:"date the public IP address was associated"`
AssociatedNetworkID string `json:"associatednetworkid,omitempty" doc:"the ID of the Network associated with the IP address"`
AssociatedNetworkName string `json:"associatednetworkname,omitempty" doc:"the name of the Network associated with the IP address"`
Domain string `json:"domain,omitempty" doc:"the domain the public IP address is associated with"`
DomainID string `json:"domainid,omitempty" doc:"the domain ID the public IP address is associated with"`
ForDisplay bool `json:"fordisplay,omitempty" doc:"is public ip for display to the regular user"`
ForVirtualNetwork bool `json:"forvirtualnetwork,omitempty" doc:"the virtual network for the IP address"`
ID string `json:"id,omitempty" doc:"public IP address id"`
IPAddress net.IP `json:"ipaddress,omitempty" doc:"public IP address"`
IsElastic bool `json:"iselastic,omitempty" doc:"is an elastic ip"`
IsPortable bool `json:"isportable,omitempty" doc:"is public IP portable across the zones"`
IsSourceNat bool `json:"issourcenat,omitempty" doc:"true if the IP address is a source nat address, false otherwise"`
IsStaticNat *bool `json:"isstaticnat,omitempty" doc:"true if this ip is for static nat, false otherwise"`
IsSystem bool `json:"issystem,omitempty" doc:"true if this ip is system ip (was allocated as a part of deployVm or createLbRule)"`
NetworkID string `json:"networkid,omitempty" doc:"the ID of the Network where ip belongs to"`
PhysicalNetworkID string `json:"physicalnetworkid,omitempty" doc:"the physical network this belongs to"`
Purpose string `json:"purpose,omitempty" doc:"purpose of the IP address. In Acton this value is not null for Ips with isSystem=true, and can have either StaticNat or LB value"`
State string `json:"state,omitempty" doc:"State of the ip address. Can be: Allocatin, Allocated and Releasing"`
Tags []ResourceTag `json:"tags,omitempty" doc:"the list of resource tags associated with ip address"`
VirtualMachineDisplayName string `json:"virtualmachinedisplayname,omitempty" doc:"virtual machine display name the ip address is assigned to (not null only for static nat Ip)"`
VirtualMachineID string `json:"virtualmachineid,omitempty" doc:"virtual machine id the ip address is assigned to (not null only for static nat Ip)"`
VirtualMachineName string `json:"virtualmachinename,omitempty" doc:"virtual machine name the ip address is assigned to (not null only for static nat Ip)"`
VlanID string `json:"vlanid,omitempty" doc:"the ID of the VLAN associated with the IP address. This parameter is visible to ROOT admins only"`
VlanName string `json:"vlanname,omitempty" doc:"the VLAN associated with the IP address"`
VMIPAddress net.IP `json:"vmipaddress,omitempty" doc:"virutal machine (dnat) ip address (not null only for static nat Ip)"`
ZoneID string `json:"zoneid,omitempty" doc:"the ID of the zone the public IP address belongs to"`
ZoneName string `json:"zonename,omitempty" doc:"the name of the zone the public IP address belongs to"`
}
// AssociateIPAddress (Async) represents the IP creation
//
// CloudStack API: https://cloudstack.apache.org/api/apidocs-4.10/apis/associateIpAddress.html
type AssociateIPAddress struct {
Account string `json:"account,omitempty" doc:"the account to associate with this IP address"`
DomainID string `json:"domainid,omitempty" doc:"the ID of the domain to associate with this IP address"`
ForDisplay *bool `json:"fordisplay,omitempty" doc:"an optional field, whether to the display the ip to the end user or not"`
IsPortable *bool `json:"isportable,omitempty" doc:"should be set to true if public IP is required to be transferable across zones, if not specified defaults to false"`
NetworkdID string `json:"networkid,omitempty" doc:"The network this ip address should be associated to."`
RegionID int `json:"regionid,omitempty" doc:"region ID from where portable ip is to be associated."`
ZoneID string `json:"zoneid,omitempty" doc:"the ID of the availability zone you want to acquire an public IP address from"`
}
// AssociateIPAddressResponse represents the response to the creation of an IPAddress
type AssociateIPAddressResponse struct {
IPAddress IPAddress `json:"ipaddress"`
}
// DisassociateIPAddress (Async) represents the IP deletion
//
// CloudStack API: https://cloudstack.apache.org/api/apidocs-4.10/apis/disassociateIpAddress.html
type DisassociateIPAddress struct {
ID string `json:"id" doc:"the id of the public ip address to disassociate"`
}
// UpdateIPAddress (Async) represents the IP modification
//
// CloudStack API: https://cloudstack.apache.org/api/apidocs-4.10/apis/updateIpAddress.html
type UpdateIPAddress struct {
ID string `json:"id" doc:"the id of the public ip address to update"`
CustomID string `json:"customid,omitempty" doc:"an optional field, in case you want to set a custom id to the resource. Allowed to Root Admins only"`
ForDisplay *bool `json:"fordisplay,omitempty" doc:"an optional field, whether to the display the ip to the end user or not"`
}
// UpdateIPAddressResponse represents the modified IP Address
type UpdateIPAddressResponse AssociateIPAddressResponse
// ListPublicIPAddresses represents a search for public IP addresses
//
// CloudStack API: https://cloudstack.apache.org/api/apidocs-4.10/apis/listPublicIpAddresses.html
type ListPublicIPAddresses struct {
Account string `json:"account,omitempty" doc:"list resources by account. Must be used with the domainId parameter."`
AllocatedOnly *bool `json:"allocatedonly,omitempty" doc:"limits search results to allocated public IP addresses"`
AssociatedNetworkID string `json:"associatednetworkid,omitempty" doc:"lists all public IP addresses associated to the network specified"`
DomainID string `json:"domainid,omitempty" doc:"list only resources belonging to the domain specified"`
ForDisplay *bool `json:"fordisplay,omitempty" doc:"list resources by display flag; only ROOT admin is eligible to pass this parameter"`
ForLoadBalancing *bool `json:"forloadbalancing,omitempty" doc:"list only ips used for load balancing"`
ForVirtualNetwork *bool `json:"forvirtualnetwork,omitempty" doc:"the virtual network for the IP address"`
ID string `json:"id,omitempty" doc:"lists ip address by id"`
IPAddress net.IP `json:"ipaddress,omitempty" doc:"lists the specified IP address"`
IsElastic *bool `json:"iselastic,omitempty" doc:"list only elastic ip addresses"`
IsRecursive *bool `json:"isrecursive,omitempty" doc:"defaults to false, but if true, lists all resources from the parent specified by the domainId till leaves."`
IsSourceNat *bool `json:"issourcenat,omitempty" doc:"list only source nat ip addresses"`
IsStaticNat *bool `json:"isstaticnat,omitempty" doc:"list only static nat ip addresses"`
Keyword string `json:"keyword,omitempty" doc:"List by keyword"`
ListAll *bool `json:"listall,omitempty" doc:"If set to false, list only resources belonging to the command's caller; if set to true - list resources that the caller is authorized to see. Default value is false"`
Page int `json:"page,omitempty"`
PageSize int `json:"pagesize,omitempty"`
PhysicalNetworkID string `json:"physicalnetworkid,omitempty" doc:"lists all public IP addresses by physical network id"`
Tags []ResourceTag `json:"tags,omitempty" doc:"List resources by tags (key/value pairs)"`
VlanID string `json:"vlanid,omitempty" doc:"lists all public IP addresses by VLAN ID"`
ZoneID string `json:"zoneid,omitempty" doc:"lists all public IP addresses by Zone ID"`
}
// ListPublicIPAddressesResponse represents a list of public IP addresses
type ListPublicIPAddressesResponse struct {
Count int `json:"count"`
PublicIPAddress []IPAddress `json:"publicipaddress"`
}

View file

@ -4,43 +4,35 @@ import (
"context"
"fmt"
"net/url"
"github.com/jinzhu/copier"
)
// Get loads the given Affinity Group
func (ag *AffinityGroup) Get(ctx context.Context, client *Client) error {
if ag.ID == "" && ag.Name == "" {
return fmt.Errorf("An Affinity Group may only be searched using ID or Name")
}
// AffinityGroup represents an (anti-)affinity group
//
// Affinity and Anti-Affinity groups provide a way to influence where VMs should run.
// See: http://docs.cloudstack.apache.org/projects/cloudstack-administration/en/stable/virtual_machines.html#affinity-groups
type AffinityGroup struct {
Account string `json:"account,omitempty" doc:"the account owning the affinity group"`
Description string `json:"description,omitempty" doc:"the description of the affinity group"`
Domain string `json:"domain,omitempty" doc:"the domain name of the affinity group"`
DomainID *UUID `json:"domainid,omitempty" doc:"the domain ID of the affinity group"`
ID *UUID `json:"id,omitempty" doc:"the ID of the affinity group"`
Name string `json:"name,omitempty" doc:"the name of the affinity group"`
Type string `json:"type,omitempty" doc:"the type of the affinity group"`
VirtualMachineIDs []string `json:"virtualmachineIds,omitempty" doc:"virtual machine Ids associated with this affinity group"`
}
resp, err := client.RequestWithContext(ctx, &ListAffinityGroups{
// ListRequest builds the ListAffinityGroups request
func (ag AffinityGroup) ListRequest() (ListCommand, error) {
return &ListAffinityGroups{
ID: ag.ID,
Name: ag.Name,
})
if err != nil {
return err
}
ags := resp.(*ListAffinityGroupsResponse)
count := len(ags.AffinityGroup)
if count == 0 {
return &ErrorResponse{
ErrorCode: ParamError,
ErrorText: fmt.Sprintf("AffinityGroup not found id: %s, name: %s", ag.ID, ag.Name),
}
} else if count > 1 {
return fmt.Errorf("More than one Affinity Group was found. Query; id: %s, name: %s", ag.ID, ag.Name)
}
return copier.Copy(ag, ags.AffinityGroup[0])
}, nil
}
// Delete removes the given Affinity Group
func (ag *AffinityGroup) Delete(ctx context.Context, client *Client) error {
if ag.ID == "" && ag.Name == "" {
return fmt.Errorf("An Affinity Group may only be deleted using ID or Name")
func (ag AffinityGroup) Delete(ctx context.Context, client *Client) error {
if ag.ID == nil && ag.Name == "" {
return fmt.Errorf("an Affinity Group may only be deleted using ID or Name")
}
req := &DeleteAffinityGroup{
@ -48,7 +40,7 @@ func (ag *AffinityGroup) Delete(ctx context.Context, client *Client) error {
DomainID: ag.DomainID,
}
if ag.ID != "" {
if ag.ID != nil {
req.ID = ag.ID
} else {
req.Name = ag.Name
@ -57,25 +49,38 @@ func (ag *AffinityGroup) Delete(ctx context.Context, client *Client) error {
return client.BooleanRequestWithContext(ctx, req)
}
// name returns the CloudStack API command name
func (*CreateAffinityGroup) name() string {
return "createAffinityGroup"
// AffinityGroupType represent an affinity group type
type AffinityGroupType struct {
Type string `json:"type,omitempty" doc:"the type of the affinity group"`
}
func (*CreateAffinityGroup) asyncResponse() interface{} {
return new(CreateAffinityGroupResponse)
// CreateAffinityGroup (Async) represents a new (anti-)affinity group
type CreateAffinityGroup struct {
Account string `json:"account,omitempty" doc:"an account for the affinity group. Must be used with domainId."`
Description string `json:"description,omitempty" doc:"optional description of the affinity group"`
DomainID *UUID `json:"domainid,omitempty" doc:"domainId of the account owning the affinity group"`
Name string `json:"name" doc:"name of the affinity group"`
Type string `json:"type" doc:"Type of the affinity group from the available affinity/anti-affinity group types"`
_ bool `name:"createAffinityGroup" description:"Creates an affinity/anti-affinity group"`
}
// name returns the CloudStack API command name
func (*UpdateVMAffinityGroup) name() string {
return "updateVMAffinityGroup"
func (CreateAffinityGroup) response() interface{} {
return new(AsyncJobResult)
}
func (*UpdateVMAffinityGroup) asyncResponse() interface{} {
return new(UpdateVMAffinityGroupResponse)
func (CreateAffinityGroup) asyncResponse() interface{} {
return new(AffinityGroup)
}
func (req *UpdateVMAffinityGroup) onBeforeSend(params *url.Values) error {
// UpdateVMAffinityGroup (Async) represents a modification of a (anti-)affinity group
type UpdateVMAffinityGroup struct {
ID *UUID `json:"id" doc:"The ID of the virtual machine"`
AffinityGroupIDs []UUID `json:"affinitygroupids,omitempty" doc:"comma separated list of affinity groups id that are going to be applied to the virtual machine. Should be passed only when vm is created from a zone with Basic Network support. Mutually exclusive with securitygroupnames parameter"`
AffinityGroupNames []string `json:"affinitygroupnames,omitempty" doc:"comma separated list of affinity groups names that are going to be applied to the virtual machine. Should be passed only when vm is created from a zone with Basic Network support. Mutually exclusive with securitygroupids parameter"`
_ bool `name:"updateVMAffinityGroup" description:"Updates the affinity/anti-affinity group associations of a virtual machine. The VM has to be stopped and restarted for the new properties to take effect."`
}
func (req UpdateVMAffinityGroup) onBeforeSend(params url.Values) error {
// Either AffinityGroupIDs or AffinityGroupNames must be set
if len(req.AffinityGroupIDs) == 0 && len(req.AffinityGroupNames) == 0 {
params.Set("affinitygroupids", "")
@ -83,29 +88,95 @@ func (req *UpdateVMAffinityGroup) onBeforeSend(params *url.Values) error {
return nil
}
// name returns the CloudStack API command name
func (*DeleteAffinityGroup) name() string {
return "deleteAffinityGroup"
func (UpdateVMAffinityGroup) response() interface{} {
return new(AsyncJobResult)
}
func (*DeleteAffinityGroup) asyncResponse() interface{} {
func (UpdateVMAffinityGroup) asyncResponse() interface{} {
return new(VirtualMachine)
}
// DeleteAffinityGroup (Async) represents an (anti-)affinity group to be deleted
type DeleteAffinityGroup struct {
Account string `json:"account,omitempty" doc:"the account of the affinity group. Must be specified with domain ID"`
DomainID *UUID `json:"domainid,omitempty" doc:"the domain ID of account owning the affinity group"`
ID *UUID `json:"id,omitempty" doc:"The ID of the affinity group. Mutually exclusive with name parameter"`
Name string `json:"name,omitempty" doc:"The name of the affinity group. Mutually exclusive with ID parameter"`
_ bool `name:"deleteAffinityGroup" description:"Deletes affinity group"`
}
func (DeleteAffinityGroup) response() interface{} {
return new(AsyncJobResult)
}
func (DeleteAffinityGroup) asyncResponse() interface{} {
return new(booleanResponse)
}
// name returns the CloudStack API command name
func (*ListAffinityGroups) name() string {
return "listAffinityGroups"
// ListAffinityGroups represents an (anti-)affinity groups search
type ListAffinityGroups struct {
Account string `json:"account,omitempty" doc:"list resources by account. Must be used with the domainId parameter."`
DomainID *UUID `json:"domainid,omitempty" doc:"list only resources belonging to the domain specified"`
ID *UUID `json:"id,omitempty" doc:"list the affinity group by the ID provided"`
IsRecursive *bool `json:"isrecursive,omitempty" doc:"defaults to false, but if true, lists all resources from the parent specified by the domainId till leaves."`
Keyword string `json:"keyword,omitempty" doc:"List by keyword"`
ListAll *bool `json:"listall,omitempty" doc:"If set to false, list only resources belonging to the command's caller; if set to true - list resources that the caller is authorized to see. Default value is false"`
Name string `json:"name,omitempty" doc:"lists affinity groups by name"`
Page int `json:"page,omitempty"`
PageSize int `json:"pagesize,omitempty"`
Type string `json:"type,omitempty" doc:"lists affinity groups by type"`
VirtualMachineID *UUID `json:"virtualmachineid,omitempty" doc:"lists affinity groups by virtual machine ID"`
_ bool `name:"listAffinityGroups" description:"Lists affinity groups"`
}
func (*ListAffinityGroups) response() interface{} {
func (ListAffinityGroups) response() interface{} {
return new(ListAffinityGroupsResponse)
}
// name returns the CloudStack API command name
func (*ListAffinityGroupTypes) name() string {
return "listAffinityGroupTypes"
// SetPage sets the current page
func (ls *ListAffinityGroups) SetPage(page int) {
ls.Page = page
}
func (*ListAffinityGroupTypes) response() interface{} {
// SetPageSize sets the page size
func (ls *ListAffinityGroups) SetPageSize(pageSize int) {
ls.PageSize = pageSize
}
func (ListAffinityGroups) each(resp interface{}, callback IterateItemFunc) {
vms, ok := resp.(*ListAffinityGroupsResponse)
if !ok {
callback(nil, fmt.Errorf("wrong type. ListAffinityGroupsResponse expected, got %T", resp))
return
}
for i := range vms.AffinityGroup {
if !callback(&vms.AffinityGroup[i], nil) {
break
}
}
}
// ListAffinityGroupsResponse represents a list of (anti-)affinity groups
type ListAffinityGroupsResponse struct {
Count int `json:"count"`
AffinityGroup []AffinityGroup `json:"affinitygroup"`
}
// ListAffinityGroupTypes represents an (anti-)affinity groups search
type ListAffinityGroupTypes struct {
Keyword string `json:"keyword,omitempty" doc:"List by keyword"`
Page int `json:"page,omitempty"`
PageSize int `json:"pagesize,omitempty"`
_ bool `name:"listAffinityGroupTypes" description:"Lists affinity group types available"`
}
func (ListAffinityGroupTypes) response() interface{} {
return new(ListAffinityGroupTypesResponse)
}
// ListAffinityGroupTypesResponse represents a list of (anti-)affinity group types
type ListAffinityGroupTypesResponse struct {
Count int `json:"count"`
AffinityGroupType []AffinityGroupType `json:"affinitygrouptype"`
}

View file

@ -1,97 +0,0 @@
package egoscale
// AffinityGroup represents an (anti-)affinity group
//
// Affinity and Anti-Affinity groups provide a way to influence where VMs should run.
// See: http://docs.cloudstack.apache.org/projects/cloudstack-administration/en/stable/virtual_machines.html#affinity-groups
type AffinityGroup struct {
Account string `json:"account,omitempty" doc:"the account owning the affinity group"`
Description string `json:"description,omitempty" doc:"the description of the affinity group"`
Domain string `json:"domain,omitempty" doc:"the domain name of the affinity group"`
DomainID string `json:"domainid,omitempty" doc:"the domain ID of the affinity group"`
ID string `json:"id,omitempty" doc:"the ID of the affinity group"`
Name string `json:"name,omitempty" doc:"the name of the affinity group"`
Type string `json:"type,omitempty" doc:"the type of the affinity group"`
VirtualMachineIDs []string `json:"virtualmachineIds,omitempty" doc:"virtual machine Ids associated with this affinity group "`
}
// AffinityGroupType represent an affinity group type
type AffinityGroupType struct {
Type string `json:"type,omitempty" doc:"the type of the affinity group"`
}
// CreateAffinityGroup (Async) represents a new (anti-)affinity group
//
// CloudStack API: http://cloudstack.apache.org/api/apidocs-4.10/apis/createAffinityGroup.html
type CreateAffinityGroup struct {
Account string `json:"account,omitempty" doc:"an account for the affinity group. Must be used with domainId."`
Description string `json:"description,omitempty" doc:"optional description of the affinity group"`
DomainID string `json:"domainid,omitempty" doc:"domainId of the account owning the affinity group"`
Name string `json:"name" doc:"name of the affinity group"`
Type string `json:"type" doc:"Type of the affinity group from the available affinity/anti-affinity group types"`
}
// CreateAffinityGroupResponse represents the response of the creation of an (anti-)affinity group
type CreateAffinityGroupResponse struct {
AffinityGroup AffinityGroup `json:"affinitygroup"`
}
// UpdateVMAffinityGroup (Async) represents a modification of a (anti-)affinity group
//
// CloudStack API: http://cloudstack.apache.org/api/apidocs-4.10/apis/updateVMAffinityGroup.html
type UpdateVMAffinityGroup struct {
ID string `json:"id" doc:"The ID of the virtual machine"`
AffinityGroupIDs []string `json:"affinitygroupids,omitempty" doc:"comma separated list of affinity groups id that are going to be applied to the virtual machine. Should be passed only when vm is created from a zone with Basic Network support. Mutually exclusive with securitygroupnames parameter"`
AffinityGroupNames []string `json:"affinitygroupnames,omitempty" doc:"comma separated list of affinity groups names that are going to be applied to the virtual machine. Should be passed only when vm is created from a zone with Basic Network support. Mutually exclusive with securitygroupids parameter"`
}
// UpdateVMAffinityGroupResponse represents the new VM
type UpdateVMAffinityGroupResponse VirtualMachineResponse
// DeleteAffinityGroup (Async) represents an (anti-)affinity group to be deleted
//
// CloudStack API: http://cloudstack.apache.org/api/apidocs-4.10/apis/deleteAffinityGroup.html
type DeleteAffinityGroup struct {
Account string `json:"account,omitempty" doc:"the account of the affinity group. Must be specified with domain ID"`
DomainID string `json:"domainid,omitempty" doc:"the domain ID of account owning the affinity group"`
ID string `json:"id,omitempty" doc:"The ID of the affinity group. Mutually exclusive with name parameter"`
Name string `json:"name,omitempty" doc:"The name of the affinity group. Mutually exclusive with ID parameter"`
}
// ListAffinityGroups represents an (anti-)affinity groups search
//
// CloudStack API: http://cloudstack.apache.org/api/apidocs-4.10/apis/listAffinityGroups.html
type ListAffinityGroups struct {
Account string `json:"account,omitempty" doc:"list resources by account. Must be used with the domainId parameter."`
DomainID string `json:"domainid,omitempty" doc:"list only resources belonging to the domain specified"`
ID string `json:"id,omitempty" doc:"list the affinity group by the ID provided"`
IsRecursive *bool `json:"isrecursive,omitempty" doc:"defaults to false, but if true, lists all resources from the parent specified by the domainId till leaves."`
Keyword string `json:"keyword,omitempty" doc:"List by keyword"`
ListAll *bool `json:"listall,omitempty" doc:"If set to false, list only resources belonging to the command's caller; if set to true - list resources that the caller is authorized to see. Default value is false"`
Name string `json:"name,omitempty" doc:"lists affinity groups by name"`
Page int `json:"page,omitempty"`
PageSize int `json:"pagesize,omitempty"`
Type string `json:"type,omitempty" doc:"lists affinity groups by type"`
VirtualMachineID string `json:"virtualmachineid,omitempty" doc:"lists affinity groups by virtual machine ID"`
}
// ListAffinityGroupsResponse represents a list of (anti-)affinity groups
type ListAffinityGroupsResponse struct {
Count int `json:"count"`
AffinityGroup []AffinityGroup `json:"affinitygroup"`
}
// ListAffinityGroupTypes represents an (anti-)affinity groups search
//
// CloudStack API: http://cloudstack.apache.org/api/apidocs-4.10/apis/listAffinityGroupTypes.html
type ListAffinityGroupTypes struct {
Keyword string `json:"keyword,omitempty" doc:"List by keyword"`
Page int `json:"page,omitempty"`
PageSize int `json:"pagesize,omitempty"`
}
// ListAffinityGroupTypesResponse represents a list of (anti-)affinity group types
type ListAffinityGroupTypesResponse struct {
Count int `json:"count"`
AffinityGroupType []AffinityGroupType `json:"affinitygrouptype"`
}

View file

@ -1,7 +1,45 @@
package egoscale
func (*ListAPIs) name() string {
return "listApis"
// API represents an API service
type API struct {
Description string `json:"description,omitempty" doc:"description of the api"`
IsAsync bool `json:"isasync,omitempty" doc:"true if api is asynchronous"`
Name string `json:"name,omitempty" doc:"the name of the api command"`
Related string `json:"related,omitempty" doc:"comma separated related apis"`
Since string `json:"since,omitempty" doc:"version of CloudStack the api was introduced in"`
Type string `json:"type,omitempty" doc:"response field type"`
Params []APIParam `json:"params,omitempty" doc:"the list params the api accepts"`
Response []APIField `json:"response,omitempty" doc:"api response fields"`
}
// APIParam represents an API parameter field
type APIParam struct {
Description string `json:"description"`
Length int64 `json:"length"`
Name string `json:"name"`
Required bool `json:"required"`
Since string `json:"since,omitempty"`
Type string `json:"type"`
}
// APIField represents an API response field
type APIField struct {
Description string `json:"description"`
Name string `json:"name"`
Response []APIField `json:"response,omitempty"`
Type string `json:"type"`
}
// ListAPIs represents a query to list the api
type ListAPIs struct {
Name string `json:"name,omitempty" doc:"API name"`
_ bool `name:"listApis" description:"lists all available apis on the server, provided by the Api Discovery plugin"`
}
// ListAPIsResponse represents a list of API
type ListAPIsResponse struct {
Count int `json:"count"`
API []API `json:"api"`
}
func (*ListAPIs) response() interface{} {

View file

@ -1,36 +0,0 @@
package egoscale
// API represents an API service
type API struct {
Description string `json:"description,omitempty" doc:"description of the api"`
IsAsync bool `json:"isasync,omitempty" doc:"true if api is asynchronous"`
Name string `json:"name,omitempty" doc:"the name of the api command"`
Related string `json:"related,omitempty" doc:"comma separated related apis"`
Since string `json:"since,omitempty" doc:"version of CloudStack the api was introduced in"`
Type string `json:"type,omitempty" doc:"response field type"`
Params []APIParam `json:"params,omitempty" doc:"the list params the api accepts"`
Response []APIParam `json:"response,omitempty" doc:"api response fields"`
}
// APIParam represents an API parameter field
type APIParam struct {
Description string `json:"description"`
Length int64 `json:"length"`
Name string `json:"name"`
Required bool `json:"required"`
Since string `json:"since"`
Type string `json:"type"`
}
// ListAPIs represents a query to list the api
//
// CloudStack API: https://cloudstack.apache.org/api/apidocs-4.10/apis/listApis.html
type ListAPIs struct {
Name string `json:"name,omitempty" doc:"API name"`
}
// ListAPIsResponse represents a list of API
type ListAPIsResponse struct {
Count int `json:"count"`
API []API `json:"api"`
}

View file

@ -1,49 +1,95 @@
package egoscale
import "encoding/json"
import (
"encoding/json"
"errors"
)
// QueryAsyncJobResult represents a query to fetch the status of async job
//
// CloudStack API: https://cloudstack.apache.org/api/apidocs-4.10/apis/queryAsyncJobResult.html
type QueryAsyncJobResult struct {
JobID string `json:"jobid" doc:"the ID of the asychronous job"`
// AsyncJobResult represents an asynchronous job result
type AsyncJobResult struct {
AccountID *UUID `json:"accountid,omitempty" doc:"the account that executed the async command"`
Cmd string `json:"cmd,omitempty" doc:"the async command executed"`
Created string `json:"created,omitempty" doc:"the created date of the job"`
JobID *UUID `json:"jobid,omitempty" doc:"extra field for the initial async call"`
JobInstanceID *UUID `json:"jobinstanceid,omitempty" doc:"the unique ID of the instance/entity object related to the job"`
JobInstanceType string `json:"jobinstancetype,omitempty" doc:"the instance/entity object related to the job"`
JobProcStatus int `json:"jobprocstatus,omitempty" doc:"the progress information of the PENDING job"`
JobResult *json.RawMessage `json:"jobresult,omitempty" doc:"the result reason"`
JobResultCode int `json:"jobresultcode,omitempty" doc:"the result code for the job"`
JobResultType string `json:"jobresulttype,omitempty" doc:"the result type"`
JobStatus JobStatusType `json:"jobstatus,omitempty" doc:"the current job status-should be 0 for PENDING"`
UserID *UUID `json:"userid,omitempty" doc:"the user that executed the async command"`
}
// name returns the CloudStack API command name
func (*QueryAsyncJobResult) name() string {
return "queryAsyncJobResult"
}
func (*QueryAsyncJobResult) response() interface{} {
return new(QueryAsyncJobResultResponse)
}
// name returns the CloudStack API command name
func (*ListAsyncJobs) name() string {
return "listAsyncJobs"
}
func (*ListAsyncJobs) response() interface{} {
return new(ListAsyncJobsResponse)
}
//Response return response of AsyncJobResult from a given type
func (a *AsyncJobResult) Response(i interface{}) error {
if a.JobStatus == Failure {
return a.Error()
}
if a.JobStatus == Success {
if err := json.Unmarshal(*(a.JobResult), i); err != nil {
return err
}
}
return nil
}
func (a *AsyncJobResult) Error() error {
func (a AsyncJobResult) Error() error {
r := new(ErrorResponse)
if e := json.Unmarshal(*a.JobResult, r); e != nil {
return e
}
return r
}
// QueryAsyncJobResult represents a query to fetch the status of async job
type QueryAsyncJobResult struct {
JobID *UUID `json:"jobid" doc:"the ID of the asynchronous job"`
_ bool `name:"queryAsyncJobResult" description:"Retrieves the current status of asynchronous job."`
}
func (QueryAsyncJobResult) response() interface{} {
return new(AsyncJobResult)
}
// ListAsyncJobs list the asynchronous jobs
type ListAsyncJobs struct {
Account string `json:"account,omitempty" doc:"list resources by account. Must be used with the domainId parameter."`
DomainID *UUID `json:"domainid,omitempty" doc:"list only resources belonging to the domain specified"`
IsRecursive *bool `json:"isrecursive,omitempty" doc:"defaults to false, but if true, lists all resources from the parent specified by the domainId till leaves."`
Keyword string `json:"keyword,omitempty" doc:"List by keyword"`
ListAll *bool `json:"listall,omitempty" doc:"If set to false, list only resources belonging to the command's caller; if set to true - list resources that the caller is authorized to see. Default value is false"`
Page int `json:"page,omitempty"`
PageSize int `json:"pagesize,omitempty"`
StartDate string `json:"startdate,omitempty" doc:"the start date of the async job"`
_ bool `name:"listAsyncJobs" description:"Lists all pending asynchronous jobs for the account."`
}
// ListAsyncJobsResponse represents a list of job results
type ListAsyncJobsResponse struct {
Count int `json:"count"`
AsyncJobs []AsyncJobResult `json:"asyncjobs"`
}
func (ListAsyncJobs) response() interface{} {
return new(ListAsyncJobsResponse)
}
// Result unmarshals the result of an AsyncJobResult into the given interface
func (a AsyncJobResult) Result(i interface{}) error {
if a.JobStatus == Failure {
return a.Error()
}
if a.JobStatus == Success {
m := map[string]json.RawMessage{}
err := json.Unmarshal(*(a.JobResult), &m)
if err == nil {
if len(m) >= 1 {
if _, ok := m["success"]; ok {
return json.Unmarshal(*(a.JobResult), i)
}
// more than one keys are list...response
if len(m) > 1 {
return json.Unmarshal(*(a.JobResult), i)
}
// otherwise, pick the first key
for k := range m {
return json.Unmarshal(m[k], i)
}
}
return errors.New("empty response")
}
}
return nil
}

View file

@ -1,44 +0,0 @@
package egoscale
import (
"encoding/json"
)
// AsyncJobResult represents an asynchronous job result
type AsyncJobResult struct {
AccountID string `json:"accountid"`
Cmd string `json:"cmd"`
Created string `json:"created"`
JobInstanceID string `json:"jobinstanceid,omitempty"`
JobInstanceType string `json:"jobinstancetype,omitempty"`
JobProcStatus int `json:"jobprocstatus"`
JobResult *json.RawMessage `json:"jobresult"`
JobResultCode int `json:"jobresultcode"`
JobResultType string `json:"jobresulttype"`
JobStatus JobStatusType `json:"jobstatus"`
UserID string `json:"userid"`
JobID string `json:"jobid"`
}
// QueryAsyncJobResultResponse represents the current status of an asynchronous job
type QueryAsyncJobResultResponse AsyncJobResult
// ListAsyncJobs list the asynchronous jobs
//
// CloudStack API: https://cloudstack.apache.org/api/apidocs-4.10/apis/listAsyncJobs.html
type ListAsyncJobs struct {
Account string `json:"account,omitempty" doc:"list resources by account. Must be used with the domainId parameter."`
DomainID string `json:"domainid,omitempty" doc:"list only resources belonging to the domain specified"`
IsRecursive *bool `json:"isrecursive,omitempty" doc:"defaults to false, but if true, lists all resources from the parent specified by the domainId till leaves."`
Keyword string `json:"keyword,omitempty" doc:"List by keyword"`
ListAll *bool `json:"listall,omitempty" doc:"If set to false, list only resources belonging to the command's caller; if set to true - list resources that the caller is authorized to see. Default value is false"`
Page int `json:"page,omitempty"`
PageSize int `json:"pagesize,omitempty"`
StartDate string `json:"startdate,omitempty" doc:"the start date of the async job"`
}
// ListAsyncJobsResponse represents a list of job results
type ListAsyncJobsResponse struct {
Count int `json:"count"`
AsyncJobs []AsyncJobResult `json:"asyncjobs"`
}

62
vendor/github.com/exoscale/egoscale/cidr.go generated vendored Normal file
View file

@ -0,0 +1,62 @@
package egoscale
import (
"bytes"
"encoding/json"
"fmt"
"net"
)
// CIDR represents a nicely JSON serializable net.IPNet
type CIDR struct {
net.IPNet
}
// UnmarshalJSON unmarshals the raw JSON into the MAC address
func (cidr *CIDR) UnmarshalJSON(b []byte) error {
var s string
if err := json.Unmarshal(b, &s); err != nil {
return err
}
c, err := ParseCIDR(s)
if err != nil {
return err
}
*cidr = CIDR{c.IPNet}
return nil
}
// MarshalJSON converts the CIDR to a string representation
func (cidr CIDR) MarshalJSON() ([]byte, error) {
return []byte(fmt.Sprintf("%q", cidr)), nil
}
// String returns the string representation of a CIDR
func (cidr CIDR) String() string {
return cidr.IPNet.String()
}
// ParseCIDR parses a CIDR from a string
func ParseCIDR(s string) (*CIDR, error) {
_, net, err := net.ParseCIDR(s)
if err != nil {
return nil, err
}
return &CIDR{*net}, nil
}
// MustParseCIDR forces parseCIDR or panics
func MustParseCIDR(s string) *CIDR {
cidr, err := ParseCIDR(s)
if err != nil {
panic(err)
}
return cidr
}
// Equal compare two CIDR
func (cidr CIDR) Equal(c CIDR) bool {
return (cidr.IPNet.IP.Equal(c.IPNet.IP) && bytes.Equal(cidr.IPNet.Mask, c.IPNet.Mask))
}

View file

@ -2,12 +2,100 @@ package egoscale
import (
"context"
"crypto/tls"
"fmt"
"io/ioutil"
"log"
"net/http"
"net/http/httputil"
"os"
"reflect"
"strings"
"time"
)
// Taggable represents a resource which can have tags attached
//
// This is a helper to fill the resourcetype of a CreateTags call
type Taggable interface {
// CloudStack resource type of the Taggable type
ResourceType() string
}
// Deletable represents an Interface that can be "Delete" by the client
type Deletable interface {
// Delete removes the given resource(s) or throws
Delete(context context.Context, client *Client) error
}
// Listable represents an Interface that can be "List" by the client
type Listable interface {
// ListRequest builds the list command
ListRequest() (ListCommand, error)
}
// Gettable represents an Interface that can be "Get" by the client
type Gettable interface {
Listable
}
// Client represents the CloudStack API client
type Client struct {
// HTTPClient holds the HTTP client
HTTPClient *http.Client
// Endpoints is CloudStack API
Endpoint string
// APIKey is the API identifier
APIKey string
// apisecret is the API secret, hence non exposed
apiSecret string
// PageSize represents the default size for a paginated result
PageSize int
// Timeout represents the default timeout for the async requests
Timeout time.Duration
// RetryStrategy represents the waiting strategy for polling the async requests
RetryStrategy RetryStrategyFunc
// Logger contains any log, plug your own
Logger *log.Logger
}
// RetryStrategyFunc represents a how much time to wait between two calls to CloudStack
type RetryStrategyFunc func(int64) time.Duration
// IterateItemFunc represents the callback to iterate a list of results, if false stops
type IterateItemFunc func(interface{}, error) bool
// WaitAsyncJobResultFunc represents the callback to wait a results of an async request, if false stops
type WaitAsyncJobResultFunc func(*AsyncJobResult, error) bool
// NewClient creates a CloudStack API client with default timeout (60)
//
// Timeout is set to both the HTTP client and the client itself.
func NewClient(endpoint, apiKey, apiSecret string) *Client {
timeout := 60 * time.Second
httpClient := &http.Client{
Transport: http.DefaultTransport,
}
client := &Client{
HTTPClient: httpClient,
Endpoint: endpoint,
APIKey: apiKey,
apiSecret: apiSecret,
PageSize: 50,
Timeout: timeout,
RetryStrategy: MonotonicRetryStrategyFunc(2),
Logger: log.New(ioutil.Discard, "", 0),
}
if prefix, ok := os.LookupEnv("EXOSCALE_TRACE"); ok {
client.Logger = log.New(os.Stderr, prefix, log.LstdFlags)
client.TraceOn()
}
return client
}
// Get populates the given resource or fails
func (client *Client) Get(g Gettable) error {
ctx, cancel := context.WithTimeout(context.Background(), client.Timeout)
@ -18,7 +106,36 @@ func (client *Client) Get(g Gettable) error {
// GetWithContext populates the given resource or fails
func (client *Client) GetWithContext(ctx context.Context, g Gettable) error {
return g.Get(ctx, client)
gs, err := client.ListWithContext(ctx, g)
if err != nil {
return err
}
count := len(gs)
if count != 1 {
req, err := g.ListRequest()
if err != nil {
return err
}
params, err := client.Payload(req)
if err != nil {
return err
}
// formatting the query string nicely
payload := params.Encode()
payload = strings.Replace(payload, "&", ", ", -1)
if count == 0 {
return &ErrorResponse{
ErrorCode: ParamError,
ErrorText: fmt.Sprintf("not found, query: %s", payload),
}
}
return fmt.Errorf("more than one element found: %s", payload)
}
return Copy(g, gs[0])
}
// Delete removes the given resource of fails
@ -46,6 +163,10 @@ func (client *Client) List(g Listable) ([]interface{}, error) {
func (client *Client) ListWithContext(ctx context.Context, g Listable) ([]interface{}, error) {
s := make([]interface{}, 0)
if g == nil || reflect.ValueOf(g).IsNil() {
return s, fmt.Errorf("g Listable shouldn't be nil, got %#v", g)
}
req, err := g.ListRequest()
if err != nil {
return s, err
@ -170,53 +291,77 @@ func (client *Client) PaginateWithContext(ctx context.Context, req ListCommand,
}
// APIName returns the CloudStack name of the given command
func (client *Client) APIName(request Command) string {
return request.name()
func (client *Client) APIName(command Command) string {
// This is due to a limitation of Go<=1.7
if _, ok := command.(*AuthorizeSecurityGroupEgress); ok {
return "authorizeSecurityGroupEgress"
}
info, err := info(command)
if err != nil {
panic(err)
}
return info.Name
}
// APIDescription returns the description of the given CloudStack command
func (client *Client) APIDescription(command Command) string {
info, err := info(command)
if err != nil {
panic(err)
}
return info.Description
}
// Response returns the response structure of the given command
func (client *Client) Response(request Command) interface{} {
switch request.(type) {
case syncCommand:
return (request.(syncCommand)).response()
func (client *Client) Response(command Command) interface{} {
switch command.(type) {
case AsyncCommand:
return (request.(AsyncCommand)).asyncResponse()
return (command.(AsyncCommand)).asyncResponse()
default:
panic(fmt.Errorf("The command %s is not a proper Sync or Async command", request.name()))
return command.response()
}
}
// NewClientWithTimeout creates a CloudStack API client
//
// Timeout is set to both the HTTP client and the client itself.
func NewClientWithTimeout(endpoint, apiKey, apiSecret string, timeout time.Duration) *Client {
client := &http.Client{
Timeout: timeout,
Transport: &http.Transport{
Proxy: http.ProxyFromEnvironment,
TLSClientConfig: &tls.Config{
InsecureSkipVerify: false,
},
},
// TraceOn activates the HTTP tracer
func (client *Client) TraceOn() {
if _, ok := client.HTTPClient.Transport.(*traceTransport); !ok {
client.HTTPClient.Transport = &traceTransport{
transport: client.HTTPClient.Transport,
logger: client.Logger,
}
cs := &Client{
HTTPClient: client,
Endpoint: endpoint,
APIKey: apiKey,
apiSecret: apiSecret,
PageSize: 50,
Timeout: timeout,
RetryStrategy: MonotonicRetryStrategyFunc(2),
}
return cs
}
// NewClient creates a CloudStack API client with default timeout (60)
func NewClient(endpoint, apiKey, apiSecret string) *Client {
timeout := 60 * time.Second
return NewClientWithTimeout(endpoint, apiKey, apiSecret, timeout)
// TraceOff deactivates the HTTP tracer
func (client *Client) TraceOff() {
if rt, ok := client.HTTPClient.Transport.(*traceTransport); ok {
client.HTTPClient.Transport = rt.transport
}
}
// traceTransport contains the original HTTP transport to enable it to be reverted
type traceTransport struct {
transport http.RoundTripper
logger *log.Logger
}
// RoundTrip executes a single HTTP transaction
func (t *traceTransport) RoundTrip(req *http.Request) (*http.Response, error) {
if dump, err := httputil.DumpRequest(req, true); err == nil {
t.logger.Printf("%s", dump)
}
resp, err := t.transport.RoundTrip(req)
if err != nil {
return nil, err
}
if dump, err := httputil.DumpResponse(resp, true); err == nil {
t.logger.Printf("%s", dump)
}
return resp, nil
}
// MonotonicRetryStrategyFunc returns a function that waits for n seconds for each iteration

View file

@ -1,60 +0,0 @@
package egoscale
import (
"context"
"net/http"
"time"
)
// Taggable represents a resource which can have tags attached
//
// This is a helper to fill the resourcetype of a CreateTags call
type Taggable interface {
// CloudStack resource type of the Taggable type
ResourceType() string
}
// Gettable represents an Interface that can be "Get" by the client
type Gettable interface {
// Get populates the given resource or throws
Get(context context.Context, client *Client) error
}
// Deletable represents an Interface that can be "Delete" by the client
type Deletable interface {
// Delete removes the given resource(s) or throws
Delete(context context.Context, client *Client) error
}
// Listable represents an Interface that can be "List" by the client
type Listable interface {
// ListRequest builds the list command
ListRequest() (ListCommand, error)
}
// Client represents the CloudStack API client
type Client struct {
// HTTPClient holds the HTTP client
HTTPClient *http.Client
// Endpoints is CloudStack API
Endpoint string
// APIKey is the API identifier
APIKey string
// apisecret is the API secret, hence non exposed
apiSecret string
// PageSize represents the default size for a paginated result
PageSize int
// Timeout represents the default timeout for the async requests
Timeout time.Duration
// RetryStrategy represents the waiting strategy for polling the async requests
RetryStrategy RetryStrategyFunc
}
// RetryStrategyFunc represents a how much time to wait between two calls to CloudStack
type RetryStrategyFunc func(int64) time.Duration
// IterateItemFunc represents the callback to iterate a list of results, if false stops
type IterateItemFunc func(interface{}, error) bool
// WaitAsyncJobResultFunc represents the callback to wait a results of an async request, if false stops
type WaitAsyncJobResultFunc func(*AsyncJobResult, error) bool

37
vendor/github.com/exoscale/egoscale/copier.go generated vendored Normal file
View file

@ -0,0 +1,37 @@
package egoscale
import (
"fmt"
"reflect"
)
// Copy copies the value from from into to. The type of "from" must be convertible into the type of "to".
func Copy(to, from interface{}) error {
tt := reflect.TypeOf(to)
tv := reflect.ValueOf(to)
ft := reflect.TypeOf(from)
fv := reflect.ValueOf(from)
if tt.Kind() != reflect.Ptr {
return fmt.Errorf("must copy to a pointer, got %q", tt.Name())
}
tt = tt.Elem()
tv = tv.Elem()
for {
if ft.ConvertibleTo(tt) {
break
}
if ft.Kind() == reflect.Ptr {
ft = ft.Elem()
fv = fv.Elem()
} else {
return fmt.Errorf("cannot convert %q into %q", tt.Name(), ft.Name())
}
}
tv.Set(fv.Convert(tt))
return nil
}

View file

@ -4,7 +4,7 @@ package egoscale
import "strconv"
const _CSErrorCode_name = "CloudRuntimeExceptionExecutionExceptionHypervisorVersionChangedExceptionCloudExceptionAccountLimitExceptionAgentUnavailableExceptionCloudAuthenticationExceptionConcurrentOperationExceptionConflictingNetworkSettingsExceptionDiscoveredWithErrorExceptionHAStateExceptionInsufficientAddressCapacityExceptionInsufficientCapacityExceptionInsufficientNetworkCapacityExceptionInsufficientServerCapacityExceptionInsufficientStorageCapacityExceptionInternalErrorExceptionInvalidParameterValueExceptionManagementServerExceptionNetworkRuleConflictExceptionPermissionDeniedExceptionResourceAllocationExceptionResourceInUseExceptionResourceUnavailableExceptionStorageUnavailableExceptionUnsupportedServiceExceptionVirtualMachineMigrationExceptionAsyncCommandQueuedRequestLimitExceptionServerApiException"
const _CSErrorCode_name = "CloudRuntimeExceptionExecutionExceptionHypervisorVersionChangedExceptionCloudExceptionAccountLimitExceptionAgentUnavailableExceptionCloudAuthenticationExceptionConcurrentOperationExceptionConflictingNetworkSettingsExceptionDiscoveredWithErrorExceptionHAStateExceptionInsufficientAddressCapacityExceptionInsufficientCapacityExceptionInsufficientNetworkCapacityExceptionInsufficientServerCapacityExceptionInsufficientStorageCapacityExceptionInternalErrorExceptionInvalidParameterValueExceptionManagementServerExceptionNetworkRuleConflictExceptionPermissionDeniedExceptionResourceAllocationExceptionResourceInUseExceptionResourceUnavailableExceptionStorageUnavailableExceptionUnsupportedServiceExceptionVirtualMachineMigrationExceptionAsyncCommandQueuedRequestLimitExceptionServerAPIException"
var _CSErrorCode_map = map[CSErrorCode]string{
4250: _CSErrorCode_name[0:21],

View file

@ -5,6 +5,7 @@ import (
"fmt"
"io/ioutil"
"net/http"
"net/url"
"strconv"
"strings"
)
@ -53,27 +54,81 @@ type DNSRecordResponse struct {
Record DNSRecord `json:"record"`
}
// UpdateDNSRecord represents a DNS record
type UpdateDNSRecord struct {
ID int64 `json:"id,omitempty"`
DomainID int64 `json:"domain_id,omitempty"`
Name string `json:"name,omitempty"`
TTL int `json:"ttl,omitempty"`
CreatedAt string `json:"created_at,omitempty"`
UpdatedAt string `json:"updated_at,omitempty"`
Content string `json:"content,omitempty"`
RecordType string `json:"record_type,omitempty"`
Prio int `json:"prio,omitempty"`
}
// UpdateDNSRecordResponse represents the creation of a DNS record
type UpdateDNSRecordResponse struct {
Record UpdateDNSRecord `json:"record"`
}
// DNSErrorResponse represents an error in the API
type DNSErrorResponse struct {
Message string `json:"message,omitempty"`
Errors *DNSError `json:"errors"`
Errors map[string][]string `json:"errors"`
}
// DNSError represents an error
type DNSError struct {
Name []string `json:"name"`
}
// Record represent record type
type Record int
//go:generate stringer -type=Record
const (
// A record type
A Record = iota
// AAAA record type
AAAA
// ALIAS record type
ALIAS
// CNAME record type
CNAME
// HINFO record type
HINFO
// MX record type
MX
// NAPTR record type
NAPTR
// NS record type
NS
// POOL record type
POOL
// SPF record type
SPF
// SRV record type
SRV
// SSHFP record type
SSHFP
// TXT record type
TXT
// URL record type
URL
)
// Error formats the DNSerror into a string
func (req *DNSErrorResponse) Error() error {
if req.Errors != nil {
return fmt.Errorf("DNS error: %s", strings.Join(req.Errors.Name, ", "))
func (req *DNSErrorResponse) Error() string {
if len(req.Errors) > 0 {
errs := []string{}
for name, ss := range req.Errors {
if len(ss) > 0 {
errs = append(errs, fmt.Sprintf("%s: %s", name, strings.Join(ss, ", ")))
}
return fmt.Errorf("DNS error: %s", req.Message)
}
return fmt.Sprintf("dns error: %s (%s)", req.Message, strings.Join(errs, "; "))
}
return fmt.Sprintf("dns error: %s", req.Message)
}
// CreateDomain creates a DNS domain
func (exo *Client) CreateDomain(name string) (*DNSDomain, error) {
func (client *Client) CreateDomain(name string) (*DNSDomain, error) {
m, err := json.Marshal(DNSDomainResponse{
Domain: &DNSDomain{
Name: name,
@ -83,7 +138,7 @@ func (exo *Client) CreateDomain(name string) (*DNSDomain, error) {
return nil, err
}
resp, err := exo.dnsRequest("/v1/domains", string(m), "POST")
resp, err := client.dnsRequest("/v1/domains", nil, string(m), "POST")
if err != nil {
return nil, err
}
@ -97,8 +152,8 @@ func (exo *Client) CreateDomain(name string) (*DNSDomain, error) {
}
// GetDomain gets a DNS domain
func (exo *Client) GetDomain(name string) (*DNSDomain, error) {
resp, err := exo.dnsRequest("/v1/domains/"+name, "", "GET")
func (client *Client) GetDomain(name string) (*DNSDomain, error) {
resp, err := client.dnsRequest("/v1/domains/"+name, nil, "", "GET")
if err != nil {
return nil, err
}
@ -111,20 +166,35 @@ func (exo *Client) GetDomain(name string) (*DNSDomain, error) {
return d.Domain, nil
}
// DeleteDomain delets a DNS domain
func (exo *Client) DeleteDomain(name string) error {
_, err := exo.dnsRequest("/v1/domains/"+name, "", "DELETE")
// GetDomains gets DNS domains
func (client *Client) GetDomains() ([]DNSDomain, error) {
resp, err := client.dnsRequest("/v1/domains", nil, "", "GET")
if err != nil {
return err
return nil, err
}
return nil
var d []DNSDomainResponse
if err := json.Unmarshal(resp, &d); err != nil {
return nil, err
}
domains := make([]DNSDomain, len(d))
for i := range d {
domains[i] = *d[i].Domain
}
return domains, nil
}
// DeleteDomain delets a DNS domain
func (client *Client) DeleteDomain(name string) error {
_, err := client.dnsRequest("/v1/domains/"+name, nil, "", "DELETE")
return err
}
// GetRecord returns a DNS record
func (exo *Client) GetRecord(domain string, recordID int64) (*DNSRecord, error) {
func (client *Client) GetRecord(domain string, recordID int64) (*DNSRecord, error) {
id := strconv.FormatInt(recordID, 10)
resp, err := exo.dnsRequest("/v1/domains/"+domain+"/records/"+id, "", "GET")
resp, err := client.dnsRequest("/v1/domains/"+domain+"/records/"+id, nil, "", "GET")
if err != nil {
return nil, err
}
@ -138,8 +208,37 @@ func (exo *Client) GetRecord(domain string, recordID int64) (*DNSRecord, error)
}
// GetRecords returns the DNS records
func (exo *Client) GetRecords(name string) ([]DNSRecord, error) {
resp, err := exo.dnsRequest("/v1/domains/"+name+"/records", "", "GET")
func (client *Client) GetRecords(domain string) ([]DNSRecord, error) {
resp, err := client.dnsRequest("/v1/domains/"+domain+"/records", nil, "", "GET")
if err != nil {
return nil, err
}
var r []DNSRecordResponse
if err = json.Unmarshal(resp, &r); err != nil {
return nil, err
}
records := make([]DNSRecord, 0, len(r))
for _, rec := range r {
records = append(records, rec.Record)
}
return records, nil
}
// GetRecordsWithFilters returns the DNS records (filters can be empty)
func (client *Client) GetRecordsWithFilters(domain, name, recordType string) ([]DNSRecord, error) {
filters := url.Values{}
if name != "" {
filters.Add("name", name)
}
if recordType != "" {
filters.Add("record_type", recordType)
}
resp, err := client.dnsRequest("/v1/domains/"+domain+"/records", filters, "", "GET")
if err != nil {
return nil, err
}
@ -158,7 +257,7 @@ func (exo *Client) GetRecords(name string) ([]DNSRecord, error) {
}
// CreateRecord creates a DNS record
func (exo *Client) CreateRecord(name string, rec DNSRecord) (*DNSRecord, error) {
func (client *Client) CreateRecord(name string, rec DNSRecord) (*DNSRecord, error) {
body, err := json.Marshal(DNSRecordResponse{
Record: rec,
})
@ -166,7 +265,7 @@ func (exo *Client) CreateRecord(name string, rec DNSRecord) (*DNSRecord, error)
return nil, err
}
resp, err := exo.dnsRequest("/v1/domains/"+name+"/records", string(body), "POST")
resp, err := client.dnsRequest("/v1/domains/"+name+"/records", nil, string(body), "POST")
if err != nil {
return nil, err
}
@ -180,8 +279,8 @@ func (exo *Client) CreateRecord(name string, rec DNSRecord) (*DNSRecord, error)
}
// UpdateRecord updates a DNS record
func (exo *Client) UpdateRecord(name string, rec DNSRecord) (*DNSRecord, error) {
body, err := json.Marshal(DNSRecordResponse{
func (client *Client) UpdateRecord(name string, rec UpdateDNSRecord) (*DNSRecord, error) {
body, err := json.Marshal(UpdateDNSRecordResponse{
Record: rec,
})
if err != nil {
@ -189,7 +288,7 @@ func (exo *Client) UpdateRecord(name string, rec DNSRecord) (*DNSRecord, error)
}
id := strconv.FormatInt(rec.ID, 10)
resp, err := exo.dnsRequest("/v1/domains/"+name+"/records/"+id, string(body), "PUT")
resp, err := client.dnsRequest("/v1/domains/"+name+"/records/"+id, nil, string(body), "PUT")
if err != nil {
return nil, err
}
@ -203,22 +302,35 @@ func (exo *Client) UpdateRecord(name string, rec DNSRecord) (*DNSRecord, error)
}
// DeleteRecord deletes a record
func (exo *Client) DeleteRecord(name string, recordID int64) error {
func (client *Client) DeleteRecord(name string, recordID int64) error {
id := strconv.FormatInt(recordID, 10)
_, err := exo.dnsRequest("/v1/domains/"+name+"/records/"+id, "", "DELETE")
_, err := client.dnsRequest("/v1/domains/"+name+"/records/"+id, nil, "", "DELETE")
return err
}
func (exo *Client) dnsRequest(uri string, params string, method string) (json.RawMessage, error) {
url := exo.Endpoint + uri
req, err := http.NewRequest(method, url, strings.NewReader(params))
func (client *Client) dnsRequest(uri string, urlValues url.Values, params, method string) (json.RawMessage, error) {
rawURL := client.Endpoint + uri
url, err := url.Parse(rawURL)
if err != nil {
return nil, err
}
q := url.Query()
for k, vs := range urlValues {
for _, v := range vs {
q.Add(k, v)
}
}
url.RawQuery = q.Encode()
req, err := http.NewRequest(method, url.String(), strings.NewReader(params))
if err != nil {
return nil, err
}
var hdr = make(http.Header)
hdr.Add("X-DNS-TOKEN", exo.APIKey+":"+exo.apiSecret)
hdr.Add("X-DNS-TOKEN", client.APIKey+":"+client.apiSecret)
hdr.Add("User-Agent", fmt.Sprintf("exoscale/egoscale (%v)", Version))
hdr.Add("Accept", "application/json")
if params != "" {
@ -226,23 +338,28 @@ func (exo *Client) dnsRequest(uri string, params string, method string) (json.Ra
}
req.Header = hdr
response, err := exo.HTTPClient.Do(req)
resp, err := client.HTTPClient.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close() // nolint: errcheck
contentType := resp.Header.Get("content-type")
if !strings.Contains(contentType, "application/json") {
return nil, fmt.Errorf(`response content-type expected to be "application/json", got %q`, contentType)
}
b, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
}
defer response.Body.Close() // nolint: errcheck
b, err := ioutil.ReadAll(response.Body)
if err != nil {
if resp.StatusCode >= 400 {
e := new(DNSErrorResponse)
if err := json.Unmarshal(b, e); err != nil {
return nil, err
}
if response.StatusCode >= 400 {
var e DNSErrorResponse
if err := json.Unmarshal(b, &e); err != nil {
return nil, err
}
return nil, e.Error()
return nil, e
}
return b, nil

View file

@ -4,7 +4,7 @@ Package egoscale is a mapping for with the CloudStack API (http://cloudstack.apa
Requests and Responses
To build a request, construct the adequate struct. This library expects a pointer for efficiency reasons only. The response is a struct corresponding to the request itself. E.g. DeployVirtualMachine gives DeployVirtualMachineResponse, as a pointer as well to avoid big copies.
To build a request, construct the adequate struct. This library expects a pointer for efficiency reasons only. The response is a struct corresponding to the data at stake. E.g. DeployVirtualMachine gives a VirtualMachine, as a pointer as well to avoid big copies.
Then everything within the struct is not a pointer. Find below some examples of how egoscale may be used to interact with a CloudStack endpoint, especially Exoscale itself. If anything feels odd or unclear, please let us know: https://github.com/exoscale/egoscale/issues
@ -21,10 +21,10 @@ Then everything within the struct is not a pointer. Find below some examples of
panic(err)
}
vm := resp.(*egoscale.DeployVirtualMachineResponse).VirtualMachine
vm := resp.(*egoscale.VirtualMachine)
fmt.Printf("Virtual Machine ID: %s\n", vm.ID)
This exemple deploys a virtual machine while controlling the job status as it goes. It enables a finer control over errors, e.g. HTTP timeout, and eventually a way to kill it of (from the client side).
This example deploys a virtual machine while controlling the job status as it goes. It enables a finer control over errors, e.g. HTTP timeout, and eventually a way to kill it of (from the client side).
req := &egoscale.DeployVirtualMachine{
Size: 10,
@ -32,7 +32,7 @@ This exemple deploys a virtual machine while controlling the job status as it go
TemplateID: "...",
ZoneID: "...",
}
resp := &egoscale.DeployVirtualMachineResponse{}
vm := &egoscale.VirtualMachine{}
fmt.Println("Deployment started")
cs.AsyncRequest(req, func(jobResult *egoscale.AsyncJobResult, err error) bool {
@ -48,7 +48,7 @@ This exemple deploys a virtual machine while controlling the job status as it go
}
// Unmarshal the response into the response struct
if err := jobResult.Response(resp); err != nil {
if err := jobResult.Response(vm); err != nil {
// JSON unmarshaling error
panic(err)
}
@ -57,7 +57,19 @@ This exemple deploys a virtual machine while controlling the job status as it go
return false
})
fmt.Printf("Virtual Machine ID: %s\n", resp.VirtualMachine.ID)
fmt.Printf("Virtual Machine ID: %s\n", vm.ID)
Debugging and traces
As this library is mostly an HTTP client, you can reuse all the existing tools around it.
cs := egoscale.NewClient("https://api.exoscale.ch/compute", "EXO...", "...")
// sets a logger on stderr
cs.Logger = log.Newos.Stderr, "prefix", log.LstdFlags)
// activates the HTTP traces
cs.TraceOn()
Nota bene: when running the tests or the egoscale library via another tool, e.g. the exo cli (or the cs cli), the environment variable EXOSCALE_TRACE=prefix does the above configuration for you. As a developer using egoscale as a library, you'll find it more convenient to plug your favorite io.Writer as it's Logger.
APIs
@ -86,7 +98,7 @@ Security Groups provide a way to isolate traffic to VMs. Rules are added via the
Name: "Load balancer",
Description: "Opens HTTP/HTTPS ports from the outside world",
})
securityGroup := resp.(*egoscale.CreateSecurityGroupResponse).SecurityGroup
securityGroup := resp.(*egoscale.SecurityGroup)
resp, err = cs.Request(&egoscale.AuthorizeSecurityGroupIngress{
Description: "SSH traffic",
@ -97,7 +109,7 @@ Security Groups provide a way to isolate traffic to VMs. Rules are added via the
EndPort: 22,
})
// The modified SecurityGroup is returned
securityGroup := resp.(*egoscale.AuthorizeSecurityGroupResponse).SecurityGroup
securityGroup := resp.(*egoscale.SecurityGroup)
// ...
err = client.BooleanRequest(&egoscale.DeleteSecurityGroup{

View file

@ -6,10 +6,10 @@ type Event struct {
Created string `json:"created,omitempty" doc:"the date the event was created"`
Description string `json:"description,omitempty" doc:"a brief description of the event"`
Domain string `json:"domain,omitempty" doc:"the name of the account's domain"`
DomainID string `json:"domainid,omitempty" doc:"the id of the account's domain"`
ID string `json:"id,omitempty" doc:"the ID of the event"`
DomainID *UUID `json:"domainid,omitempty" doc:"the id of the account's domain"`
ID *UUID `json:"id,omitempty" doc:"the ID of the event"`
Level string `json:"level,omitempty" doc:"the event level (INFO, WARN, ERROR)"`
ParentID string `json:"parentid,omitempty" doc:"whether the event is parented"`
ParentID *UUID `json:"parentid,omitempty" doc:"whether the event is parented"`
State string `json:"state,omitempty" doc:"the state of the event"`
Type string `json:"type,omitempty" doc:"the type of the event (see event types)"`
UserName string `json:"username,omitempty" doc:"the name of the user who performed the action (can be different from the account if an admin is performing an action for a user, e.g. starting/stopping a user's virtual machine)"`
@ -21,32 +21,22 @@ type EventType struct {
}
// ListEvents list the events
//
// CloudStack API: http://cloudstack.apache.org/api/apidocs-4.10/apis/listEvents.html
type ListEvents struct {
Account string `json:"account,omitempty" doc:"list resources by account. Must be used with the domainId parameter."`
DomainID string `json:"domainid,omitempty" doc:"list only resources belonging to the domain specified"`
DomainID *UUID `json:"domainid,omitempty" doc:"list only resources belonging to the domain specified"`
Duration int `json:"duration,omitempty" doc:"the duration of the event"`
EndDate string `json:"enddate,omitempty" doc:"the end date range of the list you want to retrieve (use format \"yyyy-MM-dd\" or the new format \"yyyy-MM-dd HH:mm:ss\")"`
EntryTime int `json:"entrytime,omitempty" doc:"the time the event was entered"`
ID string `json:"id,omitempty" doc:"the ID of the event"`
ID *UUID `json:"id,omitempty" doc:"the ID of the event"`
IsRecursive *bool `json:"isrecursive,omitempty" doc:"defaults to false, but if true, lists all resources from the parent specified by the domainId till leaves." doc:"defaults to false, but if true, lists all resources from the parent specified by the domainId till leaves."`
Keyword string `json:"keyword,omitempty" doc:"List by keyword"`
Level string `json:"level,omitempty" doc:"the event level (INFO, WARN, ERROR)"`
ListAll *bool `json:"listall,omitempty"`
ListAll *bool `json:"listall,omitempty" doc:"If set to false, list only resources belonging to the command's caller; if set to true - list resources that the caller is authorized to see. Default value is false"`
Page int `json:"page,omitempty" `
PageSize int `json:"pagesize,omitempty" doc:"If set to false, list only resources belonging to the command's caller; if set to true - list resources that the caller is authorized to see. Default value is false"`
PageSize int `json:"pagesize,omitempty"`
StartDate string `json:"startdate,omitempty" doc:"the start date range of the list you want to retrieve (use format \"yyyy-MM-dd\" or the new format \"yyyy-MM-dd HH:mm:ss\")"`
Type string `json:"type,omitempty" doc:"the event type (see event types)"`
}
// name returns the CloudStack API command name
func (*ListEvents) name() string {
return "listEvents"
}
func (*ListEvents) response() interface{} {
return new(ListEventsResponse)
_ bool `name:"listEvents" description:"A command to list events."`
}
// ListEventsResponse represents a response of a list query
@ -55,22 +45,22 @@ type ListEventsResponse struct {
Event []Event `json:"event"`
}
// ListEventTypes list the event types
//
// CloudStack API: http://cloudstack.apache.org/api/apidocs-4.10/apis/listEventTypes.html
type ListEventTypes struct{}
// name returns the CloudStack API command name
func (*ListEventTypes) name() string {
return "listEventTypes"
func (ListEvents) response() interface{} {
return new(ListEventsResponse)
}
func (*ListEventTypes) response() interface{} {
return new(ListEventTypesResponse)
// ListEventTypes list the event types
type ListEventTypes struct {
_ bool `name:"listEventTypes" description:"List Event Types"`
}
// ListEventTypesResponse represents a response of a list query
type ListEventTypesResponse struct {
Count int `json:"count"`
EventType []EventType `json:"eventtype"`
_ bool `name:"listEventTypes" description:"List Event Types"`
}
func (ListEventTypes) response() interface{} {
return new(ListEventTypesResponse)
}

99
vendor/github.com/exoscale/egoscale/hosts.go generated vendored Normal file
View file

@ -0,0 +1,99 @@
package egoscale
import (
"net"
)
// Host represents the Hypervisor
type Host struct {
Capabilities string `json:"capabilities,omitempty" doc:"capabilities of the host"`
ClusterID *UUID `json:"clusterid,omitempty" doc:"the cluster ID of the host"`
ClusterName string `json:"clustername,omitempty" doc:"the cluster name of the host"`
ClusterType string `json:"clustertype,omitempty" doc:"the cluster type of the cluster that host belongs to"`
CPUAllocated int64 `json:"cpuallocated,omitempty" doc:"the amount of the host's CPU currently allocated"`
CPUNumber int `json:"cpunumber,omitempty" doc:"the CPU number of the host"`
CPUSockets int `json:"cpusockets,omitempty" doc:"the number of CPU sockets on the host"`
CPUSpeed int64 `json:"cpuspeed,omitempty" doc:"the CPU speed of the host"`
CPUUsed int64 `json:"cpuused,omitempty" doc:"the amount of the host's CPU currently used"`
CPUWithOverProvisioning int64 `json:"cpuwithoverprovisioning,omitempty" doc:"the amount of the host's CPU after applying the cpu.overprovisioning.factor"`
Created string `json:"created,omitempty" doc:"the date and time the host was created"`
Disconnected string `json:"disconnected,omitempty" doc:"true if the host is disconnected. False otherwise."`
DiskSizeAllocated int64 `json:"disksizeallocated,omitempty" doc:"the host's or host storage pool's currently allocated disk size"`
DiskSizeTotal int64 `json:"disksizetotal,omitempty" doc:"the total disk size of the host or host storage pool"`
DiskSizeUsed int64 `json:"disksizeused,omitempty" doc:"the host's or host storage pool's currently used disk size"`
DiskWithOverProvisioning int64 `json:"diskwithoverprovisioning,omitempty" doc:"the total disk size of the host or host storage pool with over provisioning factor"`
Events string `json:"events,omitempty" doc:"events available for the host"`
HAHost *bool `json:"hahost,omitempty" doc:"true if the host is Ha host (dedicated to vms started by HA process; false otherwise"`
HostTags string `json:"hosttags,omitempty" doc:"comma-separated list of tags for the host"`
Hypervisor string `json:"hypervisor,omitempty" doc:"the host hypervisor"`
HypervisorVersion string `json:"hypervisorversion,omitempty" doc:"the hypervisor version"`
ID *UUID `json:"id,omitempty" doc:"the ID of the host"`
IPAddress net.IP `json:"ipaddress,omitempty" doc:"the IP address of the host"`
IsLocalstorageActive *bool `json:"islocalstorageactive,omitempty" doc:"true if local storage is active, false otherwise"`
LastPinged string `json:"lastpinged,omitempty" doc:"the date and time the host was last pinged"`
ManagementServerID *UUID `json:"managementserverid,omitempty" doc:"the management server ID of the host"`
MemoryAllocated int64 `json:"memoryallocated,omitempty" doc:"the amount of VM's memory allocated onto the host"`
MemoryPhysical int64 `json:"memoryphysical,omitempty" doc:"the total physical memory of the host"`
MemoryReserved int64 `json:"memoryreserved,omitempty" doc:"the amount of the host's memory reserved"`
MemoryTotal int64 `json:"memorytotal,omitempty" doc:"the total memory of the host available (must be physical - reserved)"`
MemoryUsed int64 `json:"memoryused,omitempty" doc:"the amount of the host's memory used by running vm"`
Name string `json:"name,omitempty" doc:"the name of the host"`
NetworkKbsRead int64 `json:"networkkbsread,omitempty" doc:"the incoming network traffic on the host"`
NetworkKbsWrite int64 `json:"networkkbswrite,omitempty" doc:"the outgoing network traffic on the host"`
OSCategoryID *UUID `json:"oscategoryid,omitempty" doc:"the OS category ID of the host"`
OSCategoryName string `json:"oscategoryname,omitempty" doc:"the OS category name of the host"`
PCIDevices []PCIDevice `json:"pcidevices,omitempty" doc:"PCI cards present in the host"`
PodID *UUID `json:"podid,omitempty" doc:"the Pod ID of the host"`
PodName string `json:"podname,omitempty" doc:"the Pod name of the host"`
Removed string `json:"removed,omitempty" doc:"the date and time the host was removed"`
ResourceState string `json:"resourcestate,omitempty" doc:"the resource state of the host"`
State string `json:"state,omitempty" doc:"the state of the host"`
StorageID *UUID `json:"storageid,omitempty" doc:"the host's storage pool id"`
Type string `json:"type,omitempty" doc:"the host type"`
Version string `json:"version,omitempty" doc:"the host version"`
ZoneID *UUID `json:"zoneid,omitempty" doc:"the Zone ID of the host"`
ZoneName string `json:"zonename,omitempty" doc:"the Zone name of the host"`
}
// ListHosts lists hosts
type ListHosts struct {
ClusterID *UUID `json:"clusterid,omitempty" doc:"lists hosts existing in particular cluster"`
Details []string `json:"details,omitempty" doc:"comma separated list of host details requested, value can be a list of [ min, all, capacity, events, stats]"`
HAHost *bool `json:"hahost,omitempty" doc:"if true, list only hosts dedicated to HA"`
Hypervisor string `json:"hypervisor,omitempty" doc:"hypervisor type of host: KVM,Simulator"`
ID *UUID `json:"id,omitempty" doc:"the id of the host"`
Keyword string `json:"keyword,omitempty" doc:"List by keyword"`
Name string `json:"name,omitempty" doc:"the name of the host"`
Page int `json:"page,omitempty"`
PageSize int `json:"pagesize,omitempty"`
PodID *UUID `json:"podid,omitempty" doc:"the Pod ID for the host"`
ResourceState string `json:"resourcestate,omitempty" doc:"list hosts by resource state. Resource state represents current state determined by admin of host, value can be one of [Enabled, Disabled, Unmanaged, PrepareForMaintenance, ErrorInMaintenance, Maintenance, Error]"`
State string `json:"state,omitempty" doc:"the state of the host"`
Type string `json:"type,omitempty" doc:"the host type"`
ZoneID *UUID `json:"zoneid,omitempty" doc:"the Zone ID for the host"`
_ bool `name:"listHosts" description:"Lists hosts."`
}
func (ListHosts) response() interface{} {
return new(ListHostsResponse)
}
// ListHostsResponse represents a list of hosts
type ListHostsResponse struct {
Count int `json:"count"`
Host []Host `json:"host"`
}
// UpdateHost changes the resources state of a host
type UpdateHost struct {
Allocationstate string `json:"allocationstate,omitempty" doc:"Change resource state of host, valid values are [Enable, Disable]. Operation may failed if host in states not allowing Enable/Disable"`
HostTags []string `json:"hosttags,omitempty" doc:"list of tags to be added to the host"`
ID *UUID `json:"id" doc:"the ID of the host to update"`
OSCategoryID *UUID `json:"oscategoryid,omitempty" doc:"the id of Os category to update the host with"`
URL string `json:"url,omitempty" doc:"the new uri for the secondary storage: nfs://host/path"`
_ bool `name:"updateHost" description:"Updates a host."`
}
func (UpdateHost) response() interface{} {
return new(Host)
}

View file

@ -1,122 +0,0 @@
package egoscale
import (
"context"
"fmt"
"github.com/jinzhu/copier"
)
// Get populates the given SSHKeyPair
func (ssh *SSHKeyPair) Get(ctx context.Context, client *Client) error {
sshs, err := client.ListWithContext(ctx, ssh)
if err != nil {
return err
}
count := len(sshs)
if count == 0 {
return &ErrorResponse{
ErrorCode: ParamError,
ErrorText: fmt.Sprintf("SSHKeyPair not found"),
}
} else if count > 1 {
return fmt.Errorf("More than one SSHKeyPair was found")
}
return copier.Copy(ssh, sshs[0])
}
// Delete removes the given SSH key, by Name
func (ssh *SSHKeyPair) Delete(ctx context.Context, client *Client) error {
if ssh.Name == "" {
return fmt.Errorf("An SSH Key Pair may only be deleted using Name")
}
return client.BooleanRequestWithContext(ctx, &DeleteSSHKeyPair{
Name: ssh.Name,
Account: ssh.Account,
DomainID: ssh.DomainID,
})
}
// ListRequest builds the ListSSHKeyPairs request
func (ssh *SSHKeyPair) ListRequest() (ListCommand, error) {
req := &ListSSHKeyPairs{
Account: ssh.Account,
DomainID: ssh.DomainID,
Fingerprint: ssh.Fingerprint,
Name: ssh.Name,
}
return req, nil
}
// name returns the CloudStack API command name
func (*CreateSSHKeyPair) name() string {
return "createSSHKeyPair"
}
func (*CreateSSHKeyPair) response() interface{} {
return new(CreateSSHKeyPairResponse)
}
// name returns the CloudStack API command name
func (*DeleteSSHKeyPair) name() string {
return "deleteSSHKeyPair"
}
func (*DeleteSSHKeyPair) response() interface{} {
return new(booleanResponse)
}
// name returns the CloudStack API command name
func (*RegisterSSHKeyPair) name() string {
return "registerSSHKeyPair"
}
func (*RegisterSSHKeyPair) response() interface{} {
return new(RegisterSSHKeyPairResponse)
}
// name returns the CloudStack API command name
func (*ListSSHKeyPairs) name() string {
return "listSSHKeyPairs"
}
func (*ListSSHKeyPairs) response() interface{} {
return new(ListSSHKeyPairsResponse)
}
func (*ListSSHKeyPairs) each(resp interface{}, callback IterateItemFunc) {
sshs, ok := resp.(*ListSSHKeyPairsResponse)
if !ok {
callback(nil, fmt.Errorf("ListSSHKeyPairsResponse expected, got %t", resp))
return
}
for i := range sshs.SSHKeyPair {
if !callback(&sshs.SSHKeyPair[i], nil) {
break
}
}
}
// SetPage sets the current page
func (ls *ListSSHKeyPairs) SetPage(page int) {
ls.Page = page
}
// SetPageSize sets the page size
func (ls *ListSSHKeyPairs) SetPageSize(pageSize int) {
ls.PageSize = pageSize
}
// name returns the CloudStack API command name
func (*ResetSSHKeyForVirtualMachine) name() string {
return "resetSSHKeyForVirtualMachine"
}
func (*ResetSSHKeyForVirtualMachine) asyncResponse() interface{} {
return new(ResetSSHKeyForVirtualMachineResponse)
}

View file

@ -1,25 +1,139 @@
package egoscale
func (*ListResourceLimits) name() string {
return "listResourceLimits"
// https://github.com/apache/cloudstack/blob/master/api/src/main/java/com/cloud/configuration/Resource.java
// ResourceTypeName represents the name of a resource type (for limits)
type ResourceTypeName string
const (
// VirtualMachineTypeName is the resource type name of a VM
VirtualMachineTypeName ResourceTypeName = "user_vm"
// IPAddressTypeName is the resource type name of an IP address
IPAddressTypeName ResourceTypeName = "public_ip"
// VolumeTypeName is the resource type name of a volume
VolumeTypeName ResourceTypeName = "volume"
// SnapshotTypeName is the resource type name of a snapshot
SnapshotTypeName ResourceTypeName = "snapshot"
// TemplateTypeName is the resource type name of a template
TemplateTypeName ResourceTypeName = "template"
// ProjectTypeName is the resource type name of a project
ProjectTypeName ResourceTypeName = "project"
// NetworkTypeName is the resource type name of a network
NetworkTypeName ResourceTypeName = "network"
// VPCTypeName is the resource type name of a VPC
VPCTypeName ResourceTypeName = "vpc"
// CPUTypeName is the resource type name of a CPU
CPUTypeName ResourceTypeName = "cpu"
// MemoryTypeName is the resource type name of Memory
MemoryTypeName ResourceTypeName = "memory"
// PrimaryStorageTypeName is the resource type name of primary storage
PrimaryStorageTypeName ResourceTypeName = "primary_storage"
// SecondaryStorageTypeName is the resource type name of secondary storage
SecondaryStorageTypeName ResourceTypeName = "secondary_storage"
)
// ResourceType represents the ID of a resource type (for limits)
type ResourceType string
const (
// VirtualMachineType is the resource type ID of a VM
VirtualMachineType ResourceType = "0"
// IPAddressType is the resource type ID of an IP address
IPAddressType ResourceType = "1"
// VolumeType is the resource type ID of a volume
VolumeType ResourceType = "2"
// SnapshotType is the resource type ID of a snapshot
SnapshotType ResourceType = "3"
// TemplateType is the resource type ID of a template
TemplateType ResourceType = "4"
// ProjectType is the resource type ID of a project
ProjectType ResourceType = "5"
// NetworkType is the resource type ID of a network
NetworkType ResourceType = "6"
// VPCType is the resource type ID of a VPC
VPCType ResourceType = "7"
// CPUType is the resource type ID of a CPU
CPUType ResourceType = "8"
// MemoryType is the resource type ID of Memory
MemoryType ResourceType = "9"
// PrimaryStorageType is the resource type ID of primary storage
PrimaryStorageType ResourceType = "10"
// SecondaryStorageType is the resource type ID of secondary storage
SecondaryStorageType ResourceType = "11"
)
// ResourceLimit represents the limit on a particular resource
type ResourceLimit struct {
Account string `json:"account,omitempty" doc:"the account of the resource limit"`
Domain string `json:"domain,omitempty" doc:"the domain name of the resource limit"`
DomainID string `json:"domainid,omitempty" doc:"the domain ID of the resource limit"`
Max int64 `json:"max,omitempty" doc:"the maximum number of the resource. A -1 means the resource currently has no limit."`
ResourceType ResourceType `json:"resourcetype,omitempty" doc:"resource type. Values include 0, 1, 2, 3, 4, 6, 7, 8, 9, 10, 11. See the resourceType parameter for more information on these values."`
ResourceTypeName string `json:"resourcetypename,omitempty" doc:"resource type name. Values include user_vm, public_ip, volume, snapshot, template, project, network, vpc, cpu, memory, primary_storage, secondary_storage."`
}
func (*ListResourceLimits) response() interface{} {
// APILimit represents the limit count
type APILimit struct {
Account string `json:"account,omitempty" doc:"the account name of the api remaining count"`
Accountid string `json:"accountid,omitempty" doc:"the account uuid of the api remaining count"`
APIAllowed int `json:"apiAllowed,omitempty" doc:"currently allowed number of apis"`
APIIssued int `json:"apiIssued,omitempty" doc:"number of api already issued"`
ExpireAfter int64 `json:"expireAfter,omitempty" doc:"seconds left to reset counters"`
}
// ListResourceLimits lists the resource limits
type ListResourceLimits struct {
Account string `json:"account,omitempty" doc:"list resources by account. Must be used with the domainId parameter."`
DomainID string `json:"domainid,omitempty" doc:"list only resources belonging to the domain specified"`
ID int64 `json:"id,omitempty" doc:"Lists resource limits by ID."`
IsRecursive *bool `json:"isrecursive,omitempty" doc:"defaults to false, but if true, lists all resources from the parent specified by the domainId till leaves."`
Keyword string `json:"keyword,omitempty" doc:"List by keyword"`
ListAll *bool `json:"listall,omitempty" doc:"If set to false, list only resources belonging to the command's caller; if set to true - list resources that the caller is authorized to see. Default value is false"`
Page int `json:"page,omitempty"`
PageSize int `json:"pagesize,omitempty"`
ResourceType ResourceType `json:"resourcetype,omitempty" doc:"Type of resource. Values are 0, 1, 2, 3, 4, 6, 7, 8, 9, 10 and 11. 0 - Instance. Number of instances a user can create. 1 - IP. Number of public IP addresses an account can own. 2 - Volume. Number of disk volumes an account can own. 3 - Snapshot. Number of snapshots an account can own. 4 - Template. Number of templates an account can register/create. 5 - Project. Number of projects an account can own. 6 - Network. Number of networks an account can own. 7 - VPC. Number of VPC an account can own. 8 - CPU. Number of CPU an account can allocate for his resources. 9 - Memory. Amount of RAM an account can allocate for his resources. 10 - PrimaryStorage. Total primary storage space (in GiB) a user can use. 11 - SecondaryStorage. Total secondary storage space (in GiB) a user can use. 12 - Elastic IP. Number of public elastic IP addresses an account can own. 13 - SMTP. If the account is allowed SMTP outbound traffic."`
ResourceTypeName string `json:"resourcetypename,omitempty" doc:"Type of resource (wins over resourceType if both are provided). Values are: user_vm - Instance. Number of instances a user can create. public_ip - IP. Number of public IP addresses an account can own. volume - Volume. Number of disk volumes an account can own. snapshot - Snapshot. Number of snapshots an account can own. template - Template. Number of templates an account can register/create. project - Project. Number of projects an account can own. network - Network. Number of networks an account can own. vpc - VPC. Number of VPC an account can own. cpu - CPU. Number of CPU an account can allocate for his resources. memory - Memory. Amount of RAM an account can allocate for his resources. primary_storage - PrimaryStorage. Total primary storage space (in GiB) a user can use. secondary_storage - SecondaryStorage. Total secondary storage space (in GiB) a user can use. public_elastic_ip - IP. Number of public elastic IP addresses an account can own. smtp - SG. If the account is allowed SMTP outbound traffic."`
_ bool `name:"listResourceLimits" description:"Lists resource limits."`
}
// ListResourceLimitsResponse represents a list of resource limits
type ListResourceLimitsResponse struct {
Count int `json:"count"`
ResourceLimit []ResourceLimit `json:"resourcelimit"`
}
func (ListResourceLimits) response() interface{} {
return new(ListResourceLimitsResponse)
}
func (*UpdateResourceLimit) name() string {
return "updateResourceLimit"
// UpdateResourceLimit updates the resource limit
type UpdateResourceLimit struct {
Account string `json:"account,omitempty" doc:"Update resource for a specified account. Must be used with the domainId parameter."`
DomainID string `json:"domainid,omitempty" doc:"Update resource limits for all accounts in specified domain. If used with the account parameter, updates resource limits for a specified account in specified domain."`
Max int64 `json:"max,omitempty" doc:"Maximum resource limit."`
ResourceType ResourceType `json:"resourcetype" doc:"Type of resource to update. Values are 0, 1, 2, 3, 4, 6, 7, 8, 9, 10 and 11. 0 - Instance. Number of instances a user can create. 1 - IP. Number of public IP addresses a user can own. 2 - Volume. Number of disk volumes a user can create. 3 - Snapshot. Number of snapshots a user can create. 4 - Template. Number of templates that a user can register/create. 6 - Network. Number of guest network a user can create. 7 - VPC. Number of VPC a user can create. 8 - CPU. Total number of CPU cores a user can use. 9 - Memory. Total Memory (in MB) a user can use. 10 - PrimaryStorage. Total primary storage space (in GiB) a user can use. 11 - SecondaryStorage. Total secondary storage space (in GiB) a user can use."`
_ bool `name:"updateResourceLimit" description:"Updates resource limits for an account or domain."`
}
func (*UpdateResourceLimit) response() interface{} {
// UpdateResourceLimitResponse represents an updated resource limit
type UpdateResourceLimitResponse struct {
ResourceLimit ResourceLimit `json:"resourcelimit"`
}
func (UpdateResourceLimit) response() interface{} {
return new(UpdateResourceLimitResponse)
}
func (*GetAPILimit) name() string {
return "getAPILimit"
// GetAPILimit gets API limit count for the caller
type GetAPILimit struct {
_ bool `name:"getApiLimit" description:"Get API limit count for the caller"`
}
func (*GetAPILimit) response() interface{} {
// GetAPILimitResponse represents the limits towards the API call
type GetAPILimitResponse struct {
APILimit APILimit `json:"apilimit"`
}
func (GetAPILimit) response() interface{} {
return new(GetAPILimitResponse)
}

View file

@ -1,130 +0,0 @@
package egoscale
// https://github.com/apache/cloudstack/blob/master/api/src/main/java/com/cloud/configuration/Resource.java
// ResourceTypeName represents the name of a resource type (for limits)
type ResourceTypeName string
const (
// VirtualMachineTypeName is the resource type name of a VM
VirtualMachineTypeName ResourceTypeName = "user_vm"
// IPAddressTypeName is the resource type name of an IP address
IPAddressTypeName ResourceTypeName = "public_ip"
// VolumeTypeName is the resource type name of a volume
VolumeTypeName ResourceTypeName = "volume"
// SnapshotTypeName is the resource type name of a snapshot
SnapshotTypeName ResourceTypeName = "snapshot"
// TemplateTypeName is the resource type name of a template
TemplateTypeName ResourceTypeName = "template"
// ProjectTypeName is the resource type name of a project
ProjectTypeName ResourceTypeName = "project"
// NetworkTypeName is the resource type name of a network
NetworkTypeName ResourceTypeName = "network"
// VPCTypeName is the resource type name of a VPC
VPCTypeName ResourceTypeName = "vpc"
// CPUTypeName is the resource type name of a CPU
CPUTypeName ResourceTypeName = "cpu"
// MemoryTypeName is the resource type name of Memory
MemoryTypeName ResourceTypeName = "memory"
// PrimaryStorageTypeName is the resource type name of primary storage
PrimaryStorageTypeName ResourceTypeName = "primary_storage"
// SecondaryStorageTypeName is the resource type name of secondary storage
SecondaryStorageTypeName ResourceTypeName = "secondary_storage"
)
// ResourceType represents the ID of a resource type (for limits)
type ResourceType int64
//go:generate stringer -type=ResourceType
const (
// VirtualMachineType is the resource type ID of a VM
VirtualMachineType ResourceType = iota
// IPAddressType is the resource type ID of an IP address
IPAddressType
// VolumeType is the resource type ID of a volume
VolumeType
// SnapshotType is the resource type ID of a snapshot
SnapshotType
// TemplateType is the resource type ID of a template
TemplateType
// ProjectType is the resource type ID of a project
ProjectType
// NetworkType is the resource type ID of a network
NetworkType
// VPCType is the resource type ID of a VPC
VPCType
// CPUType is the resource type ID of a CPU
CPUType
// MemoryType is the resource type ID of Memory
MemoryType
// PrimaryStorageType is the resource type ID of primary storage
PrimaryStorageType
// SecondaryStorageType is the resource type ID of secondary storage
SecondaryStorageType
)
// ResourceLimit represents the limit on a particular resource
type ResourceLimit struct {
Account string `json:"account,omitempty" doc:"the account of the resource limit"`
Domain string `json:"domain,omitempty" doc:"the domain name of the resource limit"`
DomainID string `json:"domainid,omitempty" doc:"the domain ID of the resource limit"`
Max int64 `json:"max,omitempty" doc:"the maximum number of the resource. A -1 means the resource currently has no limit."`
ResourceType ResourceType `json:"resourcetype,omitempty" doc:"resource type. Values include 0, 1, 2, 3, 4, 6, 7, 8, 9, 10, 11. See the resourceType parameter for more information on these values."`
ResourceTypeName ResourceTypeName `json:"resourcetypename,omitempty" doc:"resource type name. Values include user_vm, public_ip, volume, snapshot, template, project, network, vpc, cpu, memory, primary_storage, secondary_storage."`
}
// APILimit represents the limit count
type APILimit struct {
Account string `json:"account,omitempty" doc:"the account name of the api remaining count"`
Accountid string `json:"accountid,omitempty" doc:"the account uuid of the api remaining count"`
APIAllowed int `json:"apiAllowed,omitempty" doc:"currently allowed number of apis"`
APIIssued int `json:"apiIssued,omitempty" doc:"number of api already issued"`
ExpireAfter int64 `json:"expireAfter,omitempty" doc:"seconds left to reset counters"`
}
// ListResourceLimits lists the resource limits
//
// CloudStack API: http://cloudstack.apache.org/api/apidocs-4.4/user/listResourceLimits.html
type ListResourceLimits struct {
Account string `json:"account,omitempty" doc:"list resources by account. Must be used with the domainId parameter."`
DomainID string `json:"domainid,omitempty" doc:"list only resources belonging to the domain specified"`
ID int64 `json:"id,omitempty" doc:"Lists resource limits by ID."`
IsRecursive *bool `json:"isrecursive,omitempty" doc:"defaults to false, but if true, lists all resources from the parent specified by the domainId till leaves."`
Keyword string `json:"keyword,omitempty" doc:"List by keyword"`
ListAll *bool `json:"listall,omitempty" doc:"If set to false, list only resources belonging to the command's caller; if set to true - list resources that the caller is authorized to see. Default value is false"`
Page int `json:"page,omitempty"`
PageSize int `json:"pagesize,omitempty"`
ResourceType ResourceType `json:"resourcetype,omitempty" doc:"Type of resource. Values are 0, 1, 2, 3, 4, 6, 7, 8, 9, 10 and 11. 0 - Instance. Number of instances a user can create. 1 - IP. Number of public IP addresses an account can own. 2 - Volume. Number of disk volumes an account can own. 3 - Snapshot. Number of snapshots an account can own. 4 - Template. Number of templates an account can register/create. 5 - Project. Number of projects an account can own. 6 - Network. Number of networks an account can own. 7 - VPC. Number of VPC an account can own. 8 - CPU. Number of CPU an account can allocate for his resources. 9 - Memory. Amount of RAM an account can allocate for his resources. 10 - PrimaryStorage. Total primary storage space (in GiB) a user can use. 11 - SecondaryStorage. Total secondary storage space (in GiB) a user can use. 12 - Elastic IP. Number of public elastic IP addresses an account can own. 13 - SMTP. If the account is allowed SMTP outbound traffic."`
ResourceTypeName ResourceTypeName `json:"resourcetypename,omitempty" doc:"Type of resource (wins over resourceType if both are provided). Values are: user_vm - Instance. Number of instances a user can create. public_ip - IP. Number of public IP addresses an account can own. volume - Volume. Number of disk volumes an account can own. snapshot - Snapshot. Number of snapshots an account can own. template - Template. Number of templates an account can register/create. project - Project. Number of projects an account can own. network - Network. Number of networks an account can own. vpc - VPC. Number of VPC an account can own. cpu - CPU. Number of CPU an account can allocate for his resources. memory - Memory. Amount of RAM an account can allocate for his resources. primary_storage - PrimaryStorage. Total primary storage space (in GiB) a user can use. secondary_storage - SecondaryStorage. Total secondary storage space (in GiB) a user can use. public_elastic_ip - IP. Number of public elastic IP addresses an account can own. smtp - SG. If the account is allowed SMTP outbound traffic."`
}
// ListResourceLimitsResponse represents a list of resource limits
type ListResourceLimitsResponse struct {
Count int `json:"count"`
ResourceLimit []ResourceLimit `json:"resourcelimit"`
}
// UpdateResourceLimit updates the resource limit
//
// CloudStack API: http://cloudstack.apache.org/api/apidocs-4.4/root_admin/updateResourceLimit.html
type UpdateResourceLimit struct {
Account string `json:"account,omitempty" doc:"Update resource for a specified account. Must be used with the domainId parameter."`
DomainID string `json:"domainid,omitempty" doc:"Update resource limits for all accounts in specified domain. If used with the account parameter, updates resource limits for a specified account in specified domain."`
Max int64 `json:"max,omitempty" doc:"Maximum resource limit."`
ResourceType ResourceType `json:"resourcetype" doc:"Type of resource to update. Values are 0, 1, 2, 3, 4, 6, 7, 8, 9, 10 and 11. 0 - Instance. Number of instances a user can create. 1 - IP. Number of public IP addresses a user can own. 2 - Volume. Number of disk volumes a user can create. 3 - Snapshot. Number of snapshots a user can create. 4 - Template. Number of templates that a user can register/create. 6 - Network. Number of guest network a user can create. 7 - VPC. Number of VPC a user can create. 8 - CPU. Total number of CPU cores a user can use. 9 - Memory. Total Memory (in MB) a user can use. 10 - PrimaryStorage. Total primary storage space (in GiB) a user can use. 11 - SecondaryStorage. Total secondary storage space (in GiB) a user can use."`
}
// UpdateResourceLimitResponse represents an updated resource limit
type UpdateResourceLimitResponse struct {
ResourceLimit ResourceLimit `json:"resourcelimit"`
}
// GetAPILimit gets API limit count for the caller
//
// CloudStack API: http://cloudstack.apache.org/api/apidocs-4.4/user/getApiLimit.html
type GetAPILimit struct{}
// GetAPILimitResponse represents the limits towards the API call
type GetAPILimitResponse struct {
APILimit APILimit `json:"apilimit"`
}

63
vendor/github.com/exoscale/egoscale/macaddress.go generated vendored Normal file
View file

@ -0,0 +1,63 @@
package egoscale
import (
"encoding/json"
"fmt"
"net"
)
// MACAddress is a nicely JSON serializable net.HardwareAddr
type MACAddress net.HardwareAddr
// String returns the MAC address in standard format
func (mac MACAddress) String() string {
return (net.HardwareAddr)(mac).String()
}
// MAC48 builds a MAC-48 MACAddress
func MAC48(a, b, c, d, e, f byte) MACAddress {
m := make(MACAddress, 6)
m[0] = a
m[1] = b
m[2] = c
m[3] = d
m[4] = e
m[5] = f
return m
}
// UnmarshalJSON unmarshals the raw JSON into the MAC address
func (mac *MACAddress) UnmarshalJSON(b []byte) error {
var addr string
if err := json.Unmarshal(b, &addr); err != nil {
return err
}
hw, err := ParseMAC(addr)
if err != nil {
return err
}
*mac = make(MACAddress, 6)
copy(*mac, hw)
return nil
}
// MarshalJSON converts the MAC Address to a string representation
func (mac MACAddress) MarshalJSON() ([]byte, error) {
return []byte(fmt.Sprintf("%q", mac.String())), nil
}
// ParseMAC converts a string into a MACAddress
func ParseMAC(s string) (MACAddress, error) {
hw, err := net.ParseMAC(s)
return (MACAddress)(hw), err
}
// MustParseMAC acts like ParseMAC but panics if in case of an error
func MustParseMAC(s string) MACAddress {
mac, err := ParseMAC(s)
if err != nil {
panic(err)
}
return mac
}

View file

@ -1,17 +1,77 @@
package egoscale
func (*ListNetworkOfferings) name() string {
return "listNetworkOfferings"
// NetworkOffering corresponds to the Compute Offerings
type NetworkOffering struct {
Availability string `json:"availability,omitempty" doc:"availability of the network offering"`
ConserveMode bool `json:"conservemode,omitempty" doc:"true if network offering is ip conserve mode enabled"`
Created string `json:"created,omitempty" doc:"the date this network offering was created"`
Details map[string]string `json:"details,omitempty" doc:"additional key/value details tied with network offering"`
DisplayText string `json:"displaytext,omitempty" doc:"an alternate display text of the network offering."`
EgressDefaultPolicy bool `json:"egressdefaultpolicy,omitempty" doc:"true if guest network default egress policy is allow; false if default egress policy is deny"`
GuestIPType string `json:"guestiptype,omitempty" doc:"guest type of the network offering, can be Shared or Isolated"`
ID *UUID `json:"id,omitempty" doc:"the id of the network offering"`
IsDefault bool `json:"isdefault,omitempty" doc:"true if network offering is default, false otherwise"`
IsPersistent bool `json:"ispersistent,omitempty" doc:"true if network offering supports persistent networks, false otherwise"`
MaxConnections int `json:"maxconnections,omitempty" doc:"maximum number of concurrents connections to be handled by lb"`
Name string `json:"name,omitempty" doc:"the name of the network offering"`
NetworkRate int `json:"networkrate,omitempty" doc:"data transfer rate in megabits per second allowed."`
Service []Service `json:"service,omitempty" doc:"the list of supported services"`
ServiceOfferingID *UUID `json:"serviceofferingid,omitempty" doc:"the ID of the service offering used by virtual router provider"`
SpecifyIPRanges bool `json:"specifyipranges,omitempty" doc:"true if network offering supports specifying ip ranges, false otherwise"`
SpecifyVlan bool `json:"specifyvlan,omitempty" doc:"true if network offering supports vlans, false otherwise"`
State string `json:"state,omitempty" doc:"state of the network offering. Can be Disabled/Enabled/Inactive"`
SupportsStrechedL2Subnet bool `json:"supportsstrechedl2subnet,omitempty" doc:"true if network offering supports network that span multiple zones"`
Tags string `json:"tags,omitempty" doc:"the tags for the network offering"`
TrafficType string `json:"traffictype,omitempty" doc:"the traffic type for the network offering, supported types are Public, Management, Control, Guest, Vlan or Storage."`
}
func (*ListNetworkOfferings) response() interface{} {
// ListNetworkOfferings represents a query for network offerings
type ListNetworkOfferings struct {
Availability string `json:"availability,omitempty" doc:"the availability of network offering. Default value is Required"`
DisplayText string `json:"displaytext,omitempty" doc:"list network offerings by display text"`
GuestIPType string `json:"guestiptype,omitempty" doc:"list network offerings by guest type: Shared or Isolated"`
ID *UUID `json:"id,omitempty" doc:"list network offerings by id"`
IsDefault *bool `json:"isdefault,omitempty" doc:"true if need to list only default network offerings. Default value is false"`
IsTagged *bool `json:"istagged,omitempty" doc:"true if offering has tags specified"`
Keyword string `json:"keyword,omitempty" doc:"List by keyword"`
Name string `json:"name,omitempty" doc:"list network offerings by name"`
NetworkID *UUID `json:"networkid,omitempty" doc:"the ID of the network. Pass this in if you want to see the available network offering that a network can be changed to."`
Page int `json:"page,omitempty"`
PageSize int `json:"pagesize,omitempty"`
SourceNATSupported *bool `json:"sourcenatsupported,omitempty" doc:"true if need to list only netwok offerings where source nat is supported, false otherwise"`
SpecifyIPRanges *bool `json:"specifyipranges,omitempty" doc:"true if need to list only network offerings which support specifying ip ranges"`
SpecifyVlan *bool `json:"specifyvlan,omitempty" doc:"the tags for the network offering."`
State string `json:"state,omitempty" doc:"list network offerings by state"`
SupportedServices []Service `json:"supportedservices,omitempty" doc:"list network offerings supporting certain services"`
Tags string `json:"tags,omitempty" doc:"list network offerings by tags"`
TrafficType string `json:"traffictype,omitempty" doc:"list by traffic type"`
ZoneID *UUID `json:"zoneid,omitempty" doc:"list network offerings available for network creation in specific zone"`
_ bool `name:"listNetworkOfferings" description:"Lists all available network offerings."`
}
// ListNetworkOfferingsResponse represents a list of service offerings
type ListNetworkOfferingsResponse struct {
Count int `json:"count"`
NetworkOffering []NetworkOffering `json:"networkoffering"`
}
func (ListNetworkOfferings) response() interface{} {
return new(ListNetworkOfferingsResponse)
}
func (*UpdateNetworkOffering) name() string {
return "updateNetworkOffering"
// UpdateNetworkOffering represents a modification of a network offering
type UpdateNetworkOffering struct {
Availability string `json:"availability,omitempty" doc:"the availability of network offering. Default value is Required for Guest Virtual network offering; Optional for Guest Direct network offering"`
DisplayText string `json:"displaytext,omitempty" doc:"the display text of the network offering"`
ID *UUID `json:"id,omitempty" doc:"the id of the network offering"`
KeepAliveEnabled *bool `json:"keepaliveenabled,omitempty" doc:"if true keepalive will be turned on in the loadbalancer. At the time of writing this has only an effect on haproxy; the mode http and httpclose options are unset in the haproxy conf file."`
MaxConnections int `json:"maxconnections,omitempty" doc:"maximum number of concurrent connections supported by the network offering"`
Name string `json:"name,omitempty" doc:"the name of the network offering"`
SortKey int `json:"sortkey,omitempty" doc:"sort key of the network offering, integer"`
State string `json:"state,omitempty" doc:"update state for the network offering"`
_ bool `name:"updateNetworkOffering" description:"Updates a network offering."`
}
func (*UpdateNetworkOffering) response() interface{} {
return new(UpdateNetworkOfferingResponse)
func (UpdateNetworkOffering) response() interface{} {
return new(NetworkOffering)
}

View file

@ -1,76 +0,0 @@
package egoscale
// NetworkOffering corresponds to the Compute Offerings
type NetworkOffering struct {
Availability string `json:"availability,omitempty" doc:"availability of the network offering"`
ConserveMode bool `json:"conservemode,omitempty" doc:"true if network offering is ip conserve mode enabled"`
Created string `json:"created,omitempty" doc:"the date this network offering was created"`
Details map[string]string `json:"details,omitempty" doc:"additional key/value details tied with network offering"`
DisplayText string `json:"displaytext,omitempty" doc:"an alternate display text of the network offering."`
EgressDefaultPolicy bool `json:"egressdefaultpolicy,omitempty" doc:"true if guest network default egress policy is allow; false if default egress policy is deny"`
GuestIPType string `json:"guestiptype,omitempty" doc:"guest type of the network offering, can be Shared or Isolated"`
ID string `json:"id,omitempty" doc:"the id of the network offering"`
IsDefault bool `json:"isdefault,omitempty" doc:"true if network offering is default, false otherwise"`
IsPersistent bool `json:"ispersistent,omitempty" doc:"true if network offering supports persistent networks, false otherwise"`
MaxConnections int `json:"maxconnections,omitempty" doc:"maximum number of concurrents connections to be handled by lb"`
Name string `json:"name,omitempty" doc:"the name of the network offering"`
NetworkRate int `json:"networkrate,omitempty" doc:"data transfer rate in megabits per second allowed."`
Service []Service `json:"service,omitempty" doc:"the list of supported services"`
ServiceOfferingID string `json:"serviceofferingid,omitempty" doc:"the ID of the service offering used by virtual router provider"`
SpecifyIPRanges bool `json:"specifyipranges,omitempty" doc:"true if network offering supports specifying ip ranges, false otherwise"`
SpecifyVlan bool `json:"specifyvlan,omitempty" doc:"true if network offering supports vlans, false otherwise"`
State string `json:"state,omitempty" doc:"state of the network offering. Can be Disabled/Enabled/Inactive"`
SupportsStrechedL2Subnet bool `json:"supportsstrechedl2subnet,omitempty" doc:"true if network offering supports network that span multiple zones"`
Tags string `json:"tags,omitempty" doc:"the tags for the network offering"`
TrafficType string `json:"traffictype,omitempty" doc:"the traffic type for the network offering, supported types are Public, Management, Control, Guest, Vlan or Storage."`
}
// ListNetworkOfferings represents a query for network offerings
//
// CloudStack API: https://cloudstack.apache.org/api/apidocs-4.10/apis/listNetworkOfferings.html
type ListNetworkOfferings struct {
Availability string `json:"availability,omitempty" doc:"the availability of network offering. Default value is Required"`
DisplayText string `json:"displaytext,omitempty" doc:"list network offerings by display text"`
GuestIPType string `json:"guestiptype,omitempty" doc:"list network offerings by guest type: Shared or Isolated"`
ID string `json:"id,omitempty" doc:"list network offerings by id"`
IsDefault *bool `json:"isdefault,omitempty" doc:"true if need to list only default network offerings. Default value is false"`
IsTagged *bool `json:"istagged,omitempty" doc:"true if offering has tags specified"`
Keyword string `json:"keyword,omitempty" doc:"List by keyword"`
Name string `json:"name,omitempty" doc:"list network offerings by name"`
NetworkID string `json:"networkid,omitempty" doc:"the ID of the network. Pass this in if you want to see the available network offering that a network can be changed to."`
Page int `json:"page,omitempty"`
PageSize int `json:"pagesize,omitempty"`
SourceNATSupported *bool `json:"sourcenatsupported,omitempty" doc:"true if need to list only netwok offerings where source nat is supported, false otherwise"`
SpecifyIPRanges *bool `json:"specifyipranges,omitempty" doc:"true if need to list only network offerings which support specifying ip ranges"`
SpecifyVlan *bool `json:"specifyvlan,omitempty" doc:"the tags for the network offering."`
State string `json:"state,omitempty" doc:"list network offerings by state"`
SupportedServices []Service `json:"supportedservices,omitempty" doc:"list network offerings supporting certain services"`
Tags string `json:"tags,omitempty" doc:"list network offerings by tags"`
TrafficType string `json:"traffictype,omitempty" doc:"list by traffic type"`
ZoneID string `json:"zoneid,omitempty" doc:"list netowrk offerings available for network creation in specific zone"`
}
// ListNetworkOfferingsResponse represents a list of service offerings
type ListNetworkOfferingsResponse struct {
Count int `json:"count"`
NetworkOffering []NetworkOffering `json:"networkoffering"`
}
// UpdateNetworkOffering represents a modification of a network offering
//
// CloudStack API: https://cloudstack.apache.org/api/apidocs-4.10/apis/updateNetworkOffering.html
type UpdateNetworkOffering struct {
Availability string `json:"availability,omitempty" doc:"the availability of network offering. Default value is Required for Guest Virtual network offering; Optional for Guest Direct network offering"`
DisplayText string `json:"displaytext,omitempty" doc:"the display text of the network offering"`
ID string `json:"id,omitempty" doc:"the id of the network offering"`
KeepAliveEnabled *bool `json:"keepaliveenabled,omitempty" doc:"if true keepalive will be turned on in the loadbalancer. At the time of writing this has only an effect on haproxy; the mode http and httpclose options are unset in the haproxy conf file."`
MaxConnections int `json:"maxconnections,omitempty" doc:"maximum number of concurrent connections supported by the network offering"`
Name string `json:"name,omitempty" doc:"the name of the network offering"`
SortKey int `json:"sortkey,omitempty" doc:"sort key of the network offering, integer"`
State string `json:"state,omitempty" doc:"update state for the network offering"`
}
// UpdateNetworkOfferingResponse represents a newly modified network offering
type UpdateNetworkOfferingResponse struct {
NetworkOffering NetworkOffering `json:"networkoffering"`
}

View file

@ -2,43 +2,142 @@ package egoscale
import (
"fmt"
"net"
"net/url"
)
// Network represents a network
//
// See: http://docs.cloudstack.apache.org/projects/cloudstack-administration/en/latest/networking_and_traffic.html
type Network struct {
Account string `json:"account,omitempty" doc:"the owner of the network"`
AccountID *UUID `json:"accountid,omitempty" doc:"the owner ID of the network"`
BroadcastDomainType string `json:"broadcastdomaintype,omitempty" doc:"Broadcast domain type of the network"`
BroadcastURI string `json:"broadcasturi,omitempty" doc:"broadcast uri of the network."`
CanUseForDeploy bool `json:"canusefordeploy,omitempty" doc:"list networks available for vm deployment"`
CIDR *CIDR `json:"cidr,omitempty" doc:"Cloudstack managed address space, all CloudStack managed VMs get IP address from CIDR"`
DisplayNetwork bool `json:"displaynetwork,omitempty" doc:"an optional field, whether to the display the network to the end user or not."`
DisplayText string `json:"displaytext,omitempty" doc:"the displaytext of the network"`
DNS1 net.IP `json:"dns1,omitempty" doc:"the first DNS for the network"`
DNS2 net.IP `json:"dns2,omitempty" doc:"the second DNS for the network"`
Domain string `json:"domain,omitempty" doc:"the domain name of the network owner"`
DomainID *UUID `json:"domainid,omitempty" doc:"the domain id of the network owner"`
Gateway net.IP `json:"gateway,omitempty" doc:"the network's gateway"`
ID *UUID `json:"id,omitempty" doc:"the id of the network"`
IP6CIDR *CIDR `json:"ip6cidr,omitempty" doc:"the cidr of IPv6 network"`
IP6Gateway net.IP `json:"ip6gateway,omitempty" doc:"the gateway of IPv6 network"`
IsDefault bool `json:"isdefault,omitempty" doc:"true if network is default, false otherwise"`
IsPersistent bool `json:"ispersistent,omitempty" doc:"list networks that are persistent"`
IsSystem bool `json:"issystem,omitempty" doc:"true if network is system, false otherwise"`
Name string `json:"name,omitempty" doc:"the name of the network"`
Netmask net.IP `json:"netmask,omitempty" doc:"the network's netmask"`
NetworkCIDR *CIDR `json:"networkcidr,omitempty" doc:"the network CIDR of the guest network configured with IP reservation. It is the summation of CIDR and RESERVED_IP_RANGE"`
NetworkDomain string `json:"networkdomain,omitempty" doc:"the network domain"`
NetworkOfferingAvailability string `json:"networkofferingavailability,omitempty" doc:"availability of the network offering the network is created from"`
NetworkOfferingConserveMode bool `json:"networkofferingconservemode,omitempty" doc:"true if network offering is ip conserve mode enabled"`
NetworkOfferingDisplayText string `json:"networkofferingdisplaytext,omitempty" doc:"display text of the network offering the network is created from"`
NetworkOfferingID *UUID `json:"networkofferingid,omitempty" doc:"network offering id the network is created from"`
NetworkOfferingName string `json:"networkofferingname,omitempty" doc:"name of the network offering the network is created from"`
PhysicalNetworkID *UUID `json:"physicalnetworkid,omitempty" doc:"the physical network id"`
Related string `json:"related,omitempty" doc:"related to what other network configuration"`
ReservedIPRange string `json:"reservediprange,omitempty" doc:"the network's IP range not to be used by CloudStack guest VMs and can be used for non CloudStack purposes"`
RestartRequired bool `json:"restartrequired,omitempty" doc:"true network requires restart"`
Service []Service `json:"service,omitempty" doc:"the list of services"`
SpecifyIPRanges bool `json:"specifyipranges,omitempty" doc:"true if network supports specifying ip ranges, false otherwise"`
State string `json:"state,omitempty" doc:"state of the network"`
StrechedL2Subnet bool `json:"strechedl2subnet,omitempty" doc:"true if network can span multiple zones"`
SubdomainAccess bool `json:"subdomainaccess,omitempty" doc:"true if users from subdomains can access the domain level network"`
Tags []ResourceTag `json:"tags,omitempty" doc:"the list of resource tags associated with network"`
TrafficType string `json:"traffictype,omitempty" doc:"the traffic type of the network"`
Type string `json:"type,omitempty" doc:"the type of the network"`
Vlan string `json:"vlan,omitemtpy" doc:"The vlan of the network. This parameter is visible to ROOT admins only"`
ZoneID *UUID `json:"zoneid,omitempty" doc:"zone id of the network"`
ZoneName string `json:"zonename,omitempty" doc:"the name of the zone the network belongs to"`
ZonesNetworkSpans []Zone `json:"zonesnetworkspans,omitempty" doc:"If a network is enabled for 'streched l2 subnet' then represents zones on which network currently spans"`
}
// ListRequest builds the ListNetworks request
func (network *Network) ListRequest() (ListCommand, error) {
func (network Network) ListRequest() (ListCommand, error) {
//TODO add tags support
req := &ListNetworks{
Account: network.Account,
ACLType: network.ACLType,
CanUseForDeploy: &network.CanUseForDeploy,
DomainID: network.DomainID,
ID: network.ID,
PhysicalNetworkID: network.PhysicalNetworkID,
RestartRequired: &network.RestartRequired,
TrafficType: network.TrafficType,
Type: network.Type,
ZoneID: network.ZoneID,
}
if network.CanUseForDeploy {
req.CanUseForDeploy = &network.CanUseForDeploy
}
if network.RestartRequired {
req.RestartRequired = &network.RestartRequired
}
return req, nil
}
// ResourceType returns the type of the resource
func (*Network) ResourceType() string {
func (Network) ResourceType() string {
return "Network"
}
// name returns the CloudStack API command name
func (*CreateNetwork) name() string {
return "createNetwork"
// Service is a feature of a network
type Service struct {
Capability []ServiceCapability `json:"capability,omitempty"`
Name string `json:"name"`
Provider []ServiceProvider `json:"provider,omitempty"`
}
func (*CreateNetwork) response() interface{} {
return new(CreateNetworkResponse)
// ServiceCapability represents optional capability of a service
type ServiceCapability struct {
CanChooseServiceCapability bool `json:"canchooseservicecapability"`
Name string `json:"name"`
Value string `json:"value"`
}
func (req *CreateNetwork) onBeforeSend(params *url.Values) error {
// ServiceProvider represents the provider of the service
type ServiceProvider struct {
CanEnableIndividualService bool `json:"canenableindividualservice"`
DestinationPhysicalNetworkID *UUID `json:"destinationphysicalnetworkid"`
ID *UUID `json:"id"`
Name string `json:"name"`
PhysicalNetworkID *UUID `json:"physicalnetworkid"`
ServiceList []string `json:"servicelist,omitempty"`
}
// CreateNetwork creates a network
type CreateNetwork struct {
Account string `json:"account,omitempty" doc:"account who will own the network"`
DisplayNetwork *bool `json:"displaynetwork,omitempty" doc:"an optional field, whether to the display the network to the end user or not."`
DisplayText string `json:"displaytext,omitempty" doc:"the display text of the network"` // This field is required but might be empty
DomainID *UUID `json:"domainid,omitempty" doc:"domain ID of the account owning a network"`
EndIP net.IP `json:"endip,omitempty" doc:"the ending IP address in the network IP range. If not specified, will be defaulted to startIP"`
EndIpv6 net.IP `json:"endipv6,omitempty" doc:"the ending IPv6 address in the IPv6 network range"`
Gateway net.IP `json:"gateway,omitempty" doc:"the gateway of the network. Required for Shared networks and Isolated networks when it belongs to VPC"`
IP6CIDR *CIDR `json:"ip6cidr,omitempty" doc:"the CIDR of IPv6 network, must be at least /64"`
IP6Gateway net.IP `json:"ip6gateway,omitempty" doc:"the gateway of the IPv6 network. Required for Shared networks and Isolated networks when it belongs to VPC"`
IsolatedPVlan string `json:"isolatedpvlan,omitempty" doc:"the isolated private vlan for this network"`
Name string `json:"name,omitempty" doc:"the name of the network"` // This field is required but might be empty
Netmask net.IP `json:"netmask,omitempty" doc:"the netmask of the network. Required for Shared networks and Isolated networks when it belongs to VPC"`
NetworkDomain string `json:"networkdomain,omitempty" doc:"network domain"`
NetworkOfferingID *UUID `json:"networkofferingid" doc:"the network offering id"`
PhysicalNetworkID *UUID `json:"physicalnetworkid,omitempty" doc:"the Physical Network ID the network belongs to"`
StartIP net.IP `json:"startip,omitempty" doc:"the beginning IP address in the network IP range"`
StartIpv6 net.IP `json:"startipv6,omitempty" doc:"the beginning IPv6 address in the IPv6 network range"`
SubdomainAccess *bool `json:"subdomainaccess,omitempty" doc:"Defines whether to allow subdomains to use networks dedicated to their parent domain(s). Should be used with aclType=Domain, defaulted to allow.subdomain.network.access global config if not specified"`
Vlan string `json:"vlan,omitempty" doc:"the ID or VID of the network"`
ZoneID *UUID `json:"zoneid" doc:"the Zone ID for the network"`
_ bool `name:"createNetwork" description:"Creates a network"`
}
func (CreateNetwork) response() interface{} {
return new(Network)
}
func (req CreateNetwork) onBeforeSend(params url.Values) error {
// Those fields are required but might be empty
if req.Name == "" {
params.Set("name", "")
@ -49,39 +148,89 @@ func (req *CreateNetwork) onBeforeSend(params *url.Values) error {
return nil
}
// name returns the CloudStack API command name
func (*UpdateNetwork) name() string {
return "updateNetwork"
// UpdateNetwork (Async) updates a network
type UpdateNetwork struct {
ID *UUID `json:"id" doc:"the ID of the network"`
ChangeCIDR *bool `json:"changecidr,omitempty" doc:"Force update even if cidr type is different"`
CustomID *UUID `json:"customid,omitempty" doc:"an optional field, in case you want to set a custom id to the resource. Allowed to Root Admins only"`
DisplayNetwork *bool `json:"displaynetwork,omitempty" doc:"an optional field, whether to the display the network to the end user or not."`
DisplayText string `json:"displaytext,omitempty" doc:"the new display text for the network"`
GuestVMCIDR *CIDR `json:"guestvmcidr,omitempty" doc:"CIDR for Guest VMs,Cloudstack allocates IPs to Guest VMs only from this CIDR"`
Name string `json:"name,omitempty" doc:"the new name for the network"`
NetworkDomain string `json:"networkdomain,omitempty" doc:"network domain"`
NetworkOfferingID *UUID `json:"networkofferingid,omitempty" doc:"network offering ID"`
_ bool `name:"updateNetwork" description:"Updates a network"`
}
func (*UpdateNetwork) asyncResponse() interface{} {
return new(UpdateNetworkResponse)
func (UpdateNetwork) response() interface{} {
return new(AsyncJobResult)
}
// name returns the CloudStack API command name
func (*RestartNetwork) name() string {
return "restartNetwork"
func (UpdateNetwork) asyncResponse() interface{} {
return new(Network)
}
func (*RestartNetwork) asyncResponse() interface{} {
return new(RestartNetworkResponse)
// RestartNetwork (Async) updates a network
type RestartNetwork struct {
ID *UUID `json:"id" doc:"The id of the network to restart."`
Cleanup *bool `json:"cleanup,omitempty" doc:"If cleanup old network elements"`
_ bool `name:"restartNetwork" description:"Restarts the network; includes 1) restarting network elements - virtual routers, dhcp servers 2) reapplying all public ips 3) reapplying loadBalancing/portForwarding rules"`
}
// name returns the CloudStack API command name
func (*DeleteNetwork) name() string {
return "deleteNetwork"
func (RestartNetwork) response() interface{} {
return new(AsyncJobResult)
}
func (*DeleteNetwork) asyncResponse() interface{} {
func (RestartNetwork) asyncResponse() interface{} {
return new(Network)
}
// DeleteNetwork deletes a network
type DeleteNetwork struct {
ID *UUID `json:"id" doc:"the ID of the network"`
Forced *bool `json:"forced,omitempty" doc:"Force delete a network. Network will be marked as 'Destroy' even when commands to shutdown and cleanup to the backend fails."`
_ bool `name:"deleteNetwork" description:"Deletes a network"`
}
func (DeleteNetwork) response() interface{} {
return new(AsyncJobResult)
}
func (DeleteNetwork) asyncResponse() interface{} {
return new(booleanResponse)
}
// name returns the CloudStack API command name
func (*ListNetworks) name() string {
return "listNetworks"
// ListNetworks represents a query to a network
type ListNetworks struct {
Account string `json:"account,omitempty" doc:"list resources by account. Must be used with the domainId parameter."`
CanUseForDeploy *bool `json:"canusefordeploy,omitempty" doc:"list networks available for vm deployment"`
DisplayNetwork *bool `json:"displaynetwork,omitempty" doc:"list resources by display flag; only ROOT admin is eligible to pass this parameter"`
DomainID *UUID `json:"domainid,omitempty" doc:"list only resources belonging to the domain specified"`
ID *UUID `json:"id,omitempty" doc:"list networks by id"`
IsRecursive *bool `json:"isrecursive,omitempty" doc:"defaults to false, but if true, lists all resources from the parent specified by the domainId till leaves."`
IsSystem *bool `json:"issystem,omitempty" doc:"true if network is system, false otherwise"`
Keyword string `json:"keyword,omitempty" doc:"List by keyword"`
ListAll *bool `json:"listall,omitempty" doc:"If set to false, list only resources belonging to the command's caller; if set to true - list resources that the caller is authorized to see. Default value is false"`
Page int `json:"page,omitempty"`
PageSize int `json:"pagesize,omitempty"`
PhysicalNetworkID *UUID `json:"physicalnetworkid,omitempty" doc:"list networks by physical network id"`
RestartRequired *bool `json:"restartrequired,omitempty" doc:"list networks by restartRequired"`
SpecifyIPRanges *bool `json:"specifyipranges,omitempty" doc:"true if need to list only networks which support specifying ip ranges"`
SupportedServices []Service `json:"supportedservices,omitempty" doc:"list networks supporting certain services"`
Tags []ResourceTag `json:"tags,omitempty" doc:"List resources by tags (key/value pairs)"`
TrafficType string `json:"traffictype,omitempty" doc:"type of the traffic"`
Type string `json:"type,omitempty" doc:"the type of the network. Supported values are: Isolated and Shared"`
ZoneID *UUID `json:"zoneid,omitempty" doc:"the Zone ID of the network"`
_ bool `name:"listNetworks" description:"Lists all available networks."`
}
func (*ListNetworks) response() interface{} {
// ListNetworksResponse represents the list of networks
type ListNetworksResponse struct {
Count int `json:"count"`
Network []Network `json:"network"`
}
func (ListNetworks) response() interface{} {
return new(ListNetworksResponse)
}
@ -95,10 +244,10 @@ func (listNetwork *ListNetworks) SetPageSize(pageSize int) {
listNetwork.PageSize = pageSize
}
func (*ListNetworks) each(resp interface{}, callback IterateItemFunc) {
func (ListNetworks) each(resp interface{}, callback IterateItemFunc) {
networks, ok := resp.(*ListNetworksResponse)
if !ok {
callback(nil, fmt.Errorf("ListNetworksResponse expected, got %t", resp))
callback(nil, fmt.Errorf("type error: ListNetworksResponse expected, got %T", resp))
return
}

View file

@ -1,183 +0,0 @@
package egoscale
import "net"
// Network represents a network
//
// See: http://docs.cloudstack.apache.org/projects/cloudstack-administration/en/latest/networking_and_traffic.html
type Network struct {
Account string `json:"account,omitempty" doc:"the owner of the network"`
ACLID string `json:"aclid,omitempty" doc:"ACL Id associated with the VPC network"`
ACLType string `json:"acltype,omitempty" doc:"acl type - access type to the network"`
BroadcastDomainType string `json:"broadcastdomaintype,omitempty" doc:"Broadcast domain type of the network"`
BroadcastURI string `json:"broadcasturi,omitempty" doc:"broadcast uri of the network. This parameter is visible to ROOT admins only"`
CanUseForDeploy bool `json:"canusefordeploy,omitempty" doc:"list networks available for vm deployment"`
Cidr string `json:"cidr,omitempty" doc:"Cloudstack managed address space, all CloudStack managed VMs get IP address from CIDR"`
DisplayNetwork bool `json:"displaynetwork,omitempty" doc:"an optional field, whether to the display the network to the end user or not."`
DisplayText string `json:"displaytext,omitempty" doc:"the displaytext of the network"`
DNS1 net.IP `json:"dns1,omitempty" doc:"the first DNS for the network"`
DNS2 net.IP `json:"dns2,omitempty" doc:"the second DNS for the network"`
Domain string `json:"domain,omitempty" doc:"the domain name of the network owner"`
DomainID string `json:"domainid,omitempty" doc:"the domain id of the network owner"`
Gateway net.IP `json:"gateway,omitempty" doc:"the network's gateway"`
ID string `json:"id,omitempty" doc:"the id of the network"`
IP6Cidr string `json:"ip6cidr,omitempty" doc:"the cidr of IPv6 network"`
IP6Gateway net.IP `json:"ip6gateway,omitempty" doc:"the gateway of IPv6 network"`
IsDefault bool `json:"isdefault,omitempty" doc:"true if network is default, false otherwise"`
IsPersistent bool `json:"ispersistent,omitempty" doc:"list networks that are persistent"`
IsSystem bool `json:"issystem,omitempty" doc:"true if network is system, false otherwise"`
Name string `json:"name,omitempty" doc:"the name of the network"`
Netmask net.IP `json:"netmask,omitempty" doc:"the network's netmask"`
NetworkCidr string `json:"networkcidr,omitempty" doc:"the network CIDR of the guest network configured with IP reservation. It is the summation of CIDR and RESERVED_IP_RANGE"`
NetworkDomain string `json:"networkdomain,omitempty" doc:"the network domain"`
NetworkOfferingAvailability string `json:"networkofferingavailability,omitempty" doc:"availability of the network offering the network is created from"`
NetworkOfferingConserveMode bool `json:"networkofferingconservemode,omitempty" doc:"true if network offering is ip conserve mode enabled"`
NetworkOfferingDisplayText string `json:"networkofferingdisplaytext,omitempty" doc:"display text of the network offering the network is created from"`
NetworkOfferingID string `json:"networkofferingid,omitempty" doc:"network offering id the network is created from"`
NetworkOfferingName string `json:"networkofferingname,omitempty" doc:"name of the network offering the network is created from"`
PhysicalNetworkID string `json:"physicalnetworkid,omitempty" doc:"the physical network id"`
Related string `json:"related,omitempty" doc:"related to what other network configuration"`
ReservedIPRange string `json:"reservediprange,omitempty" doc:"the network's IP range not to be used by CloudStack guest VMs and can be used for non CloudStack purposes"`
RestartRequired bool `json:"restartrequired,omitempty" doc:"true network requires restart"`
Service []Service `json:"service,omitempty" doc:"the list of services"`
SpecifyIPRanges bool `json:"specifyipranges,omitempty" doc:"true if network supports specifying ip ranges, false otherwise"`
State string `json:"state,omitempty" doc:"state of the network"`
StrechedL2Subnet bool `json:"strechedl2subnet,omitempty" doc:"true if network can span multiple zones"`
SubdomainAccess bool `json:"subdomainaccess,omitempty" doc:"true if users from subdomains can access the domain level network"`
Tags []ResourceTag `json:"tags,omitempty" doc:"the list of resource tags associated with network"`
TrafficType string `json:"traffictype,omitempty" doc:"the traffic type of the network"`
Type string `json:"type,omitempty" doc:"the type of the network"`
Vlan string `json:"vlan,omitemtpy" doc:"The vlan of the network. This parameter is visible to ROOT admins only"`
ZoneID string `json:"zoneid,omitempty" doc:"zone id of the network"`
ZoneName string `json:"zonename,omitempty" doc:"the name of the zone the network belongs to"`
ZonesNetworkSpans []Zone `json:"zonesnetworkspans,omitempty" doc:"If a network is enabled for 'streched l2 subnet' then represents zones on which network currently spans"`
}
// Service is a feature of a network
type Service struct {
Capability []ServiceCapability `json:"capability,omitempty"`
Name string `json:"name"`
Provider []ServiceProvider `json:"provider,omitempty"`
}
// ServiceCapability represents optional capability of a service
type ServiceCapability struct {
CanChooseServiceCapability bool `json:"canchooseservicecapability"`
Name string `json:"name"`
Value string `json:"value"`
}
// ServiceProvider represents the provider of the service
type ServiceProvider struct {
CanEnableIndividualService bool `json:"canenableindividualservice"`
DestinationPhysicalNetworkID string `json:"destinationphysicalnetworkid"`
ID string `json:"id"`
Name string `json:"name"`
PhysicalNetworkID string `json:"physicalnetworkid"`
ServiceList []string `json:"servicelist,omitempty"`
}
// NetworkResponse represents a network
type NetworkResponse struct {
Network Network `json:"network"`
}
// CreateNetwork creates a network
//
// CloudStack API: http://cloudstack.apache.org/api/apidocs-4.10/apis/createNetwork.html
type CreateNetwork struct {
Account string `json:"account,omitempty" doc:"account who will own the network"`
ACLID string `json:"aclid,omitempty" doc:"Network ACL Id associated for the network"`
ACLType string `json:"acltype,omitempty" doc:"Access control type; supported values are account and domain. In 3.0 all shared networks should have aclType=Domain, and all Isolated networks - Account. Account means that only the account owner can use the network, domain - all accouns in the domain can use the network"`
DisplayNetwork *bool `json:"displaynetwork,omitempty" doc:"an optional field, whether to the display the network to the end user or not."`
DisplayText string `json:"displaytext" doc:"the display text of the network"`
DomainID string `json:"domainid,omitempty" doc:"domain ID of the account owning a network"`
EndIP net.IP `json:"endip,omitempty" doc:"the ending IP address in the network IP range. If not specified, will be defaulted to startIP"`
EndIpv6 net.IP `json:"endipv6,omitempty" doc:"the ending IPv6 address in the IPv6 network range"`
Gateway net.IP `json:"gateway,omitempty" doc:"the gateway of the network. Required for Shared networks and Isolated networks when it belongs to VPC"`
IP6Cidr string `json:"ip6cidr,omitempty" doc:"the CIDR of IPv6 network, must be at least /64"`
IP6Gateway net.IP `json:"ip6gateway,omitempty" doc:"the gateway of the IPv6 network. Required for Shared networks and Isolated networks when it belongs to VPC"`
IsolatedPVlan string `json:"isolatedpvlan,omitempty" doc:"the isolated private vlan for this network"`
Name string `json:"name" doc:"the name of the network"`
Netmask net.IP `json:"netmask,omitempty" doc:"the netmask of the network. Required for Shared networks and Isolated networks when it belongs to VPC"`
NetworkDomain string `json:"networkdomain,omitempty" doc:"network domain"`
NetworkOfferingID string `json:"networkofferingid" doc:"the network offering id"`
PhysicalNetworkID string `json:"physicalnetworkid,omitempty" doc:"the Physical Network ID the network belongs to"`
StartIP net.IP `json:"startip,omitempty" doc:"the beginning IP address in the network IP range"`
StartIpv6 net.IP `json:"startipv6,omitempty" doc:"the beginning IPv6 address in the IPv6 network range"`
SubdomainAccess *bool `json:"subdomainaccess,omitempty" doc:"Defines whether to allow subdomains to use networks dedicated to their parent domain(s). Should be used with aclType=Domain, defaulted to allow.subdomain.network.access global config if not specified"`
Vlan string `json:"vlan,omitempty" doc:"the ID or VID of the network"`
ZoneID string `json:"zoneid" doc:"the Zone ID for the network"`
}
// CreateNetworkResponse represents a freshly created network
type CreateNetworkResponse NetworkResponse
// UpdateNetwork (Async) updates a network
//
// CloudStack API: http://cloudstack.apache.org/api/apidocs-4.10/apis/updateNetwork.html
type UpdateNetwork struct {
ID string `json:"id" doc:"the ID of the network"`
ChangeCidr *bool `json:"changecidr,omitempty" doc:"Force update even if cidr type is different"`
CustomID string `json:"customid,omitempty" doc:"an optional field, in case you want to set a custom id to the resource. Allowed to Root Admins only"`
DisplayNetwork *bool `json:"displaynetwork,omitempty" doc:"an optional field, whether to the display the network to the end user or not."`
DisplayText string `json:"displaytext,omitempty" doc:"the new display text for the network"`
GuestVMCidr string `json:"guestvmcidr,omitempty" doc:"CIDR for Guest VMs,Cloudstack allocates IPs to Guest VMs only from this CIDR"`
Name string `json:"name,omitempty" doc:"the new name for the network"`
NetworkDomain string `json:"networkdomain,omitempty" doc:"network domain"`
NetworkOfferingID string `json:"networkofferingid,omitempty" doc:"network offering ID"`
}
// UpdateNetworkResponse represents a freshly created network
type UpdateNetworkResponse NetworkResponse
// RestartNetwork (Async) updates a network
//
// CloudStack API: http://cloudstack.apache.org/api/apidocs-4.10/apis/restartNetwork.html
type RestartNetwork struct {
ID string `json:"id" doc:"The id of the network to restart."`
Cleanup *bool `json:"cleanup,omitempty" doc:"If cleanup old network elements"`
}
// RestartNetworkResponse represents a freshly created network
type RestartNetworkResponse NetworkResponse
// DeleteNetwork deletes a network
//
// CloudStack API: http://cloudstack.apache.org/api/apidocs-4.10/apis/deleteNetwork.html
type DeleteNetwork struct {
ID string `json:"id" doc:"the ID of the network"`
Forced *bool `json:"forced,omitempty" doc:"Force delete a network. Network will be marked as 'Destroy' even when commands to shutdown and cleanup to the backend fails."`
}
// ListNetworks represents a query to a network
//
// CloudStack API: http://cloudstack.apache.org/api/apidocs-4.10/apis/listNetworks.html
type ListNetworks struct {
Account string `json:"account,omitempty" doc:"list resources by account. Must be used with the domainId parameter."`
ACLType string `json:"acltype,omitempty" doc:"list networks by ACL (access control list) type. Supported values are Account and Domain"`
CanUseForDeploy *bool `json:"canusefordeploy,omitempty" doc:"list networks available for vm deployment"`
DisplayNetwork *bool `json:"displaynetwork,omitempty" doc:"list resources by display flag; only ROOT admin is eligible to pass this parameter"`
DomainID string `json:"domainid,omitempty" doc:"list only resources belonging to the domain specified"`
ID string `json:"id,omitempty" doc:"list networks by id"`
IsRecursive *bool `json:"isrecursive,omitempty" doc:"defaults to false, but if true, lists all resources from the parent specified by the domainId till leaves."`
IsSystem *bool `json:"issystem,omitempty" doc:"true if network is system, false otherwise"`
Keyword string `json:"keyword,omitempty" doc:"List by keyword"`
ListAll *bool `json:"listall,omitempty" doc:"If set to false, list only resources belonging to the command's caller; if set to true - list resources that the caller is authorized to see. Default value is false"`
Page int `json:"page,omitempty"`
PageSize int `json:"pagesize,omitempty"`
PhysicalNetworkID string `json:"physicalnetworkid,omitempty" doc:"list networks by physical network id"`
RestartRequired *bool `json:"restartrequired,omitempty" doc:"list networks by restartRequired"`
SpecifyIPRanges *bool `json:"specifyipranges,omitempty" doc:"true if need to list only networks which support specifying ip ranges"`
SupportedServices []Service `json:"supportedservices,omitempty" doc:"list networks supporting certain services"`
Tags []ResourceTag `json:"tags,omitempty" doc:"List resources by tags (key/value pairs)"`
TrafficType string `json:"traffictype,omitempty" doc:"type of the traffic"`
Type string `json:"type,omitempty" doc:"the type of the network. Supported values are: Isolated and Shared"`
ZoneID string `json:"zoneid,omitempty" doc:"the Zone ID of the network"`
}
// ListNetworksResponse represents the list of networks
type ListNetworksResponse struct {
Count int `json:"count"`
Network []Network `json:"network"`
}

View file

@ -1,13 +1,39 @@
package egoscale
import (
"fmt"
"errors"
"net"
)
// Nic represents a Network Interface Controller (NIC)
//
// See: http://docs.cloudstack.apache.org/projects/cloudstack-administration/en/latest/networking_and_traffic.html#configuring-multiple-ip-addresses-on-a-single-nic
type Nic struct {
BroadcastURI string `json:"broadcasturi,omitempty" doc:"the broadcast uri of the nic"`
DeviceID *UUID `json:"deviceid,omitempty" doc:"device id for the network when plugged into the virtual machine"`
Gateway net.IP `json:"gateway,omitempty" doc:"the gateway of the nic"`
ID *UUID `json:"id,omitempty" doc:"the ID of the nic"`
IP6Address net.IP `json:"ip6address,omitempty" doc:"the IPv6 address of network"`
IP6CIDR *CIDR `json:"ip6cidr,omitempty" doc:"the cidr of IPv6 network"`
IP6Gateway net.IP `json:"ip6gateway,omitempty" doc:"the gateway of IPv6 network"`
IPAddress net.IP `json:"ipaddress,omitempty" doc:"the ip address of the nic"`
IsDefault bool `json:"isdefault,omitempty" doc:"true if nic is default, false otherwise"`
IsolationURI string `json:"isolationuri,omitempty" doc:"the isolation uri of the nic"`
MACAddress MACAddress `json:"macaddress,omitempty" doc:"true if nic is default, false otherwise"`
Netmask net.IP `json:"netmask,omitempty" doc:"the netmask of the nic"`
NetworkID *UUID `json:"networkid,omitempty" doc:"the ID of the corresponding network"`
NetworkName string `json:"networkname,omitempty" doc:"the name of the corresponding network"`
ReverseDNS []ReverseDNS `json:"reversedns,omitempty" doc:"the list of PTR record(s) associated with the virtual machine"`
SecondaryIP []NicSecondaryIP `json:"secondaryip,omitempty" doc:"the Secondary ipv4 addr of nic"`
TrafficType string `json:"traffictype,omitempty" doc:"the traffic type of the nic"`
Type string `json:"type,omitempty" doc:"the type of the nic"`
VirtualMachineID *UUID `json:"virtualmachineid,omitempty" doc:"Id of the vm to which the nic belongs"`
}
// ListRequest build a ListNics request from the given Nic
func (nic *Nic) ListRequest() (ListCommand, error) {
if nic.VirtualMachineID == "" {
return nil, fmt.Errorf("ListNics command requires the VirtualMachineID field to be set")
func (nic Nic) ListRequest() (ListCommand, error) {
if nic.VirtualMachineID == nil {
return nil, errors.New("command ListNics requires the VirtualMachineID field to be set")
}
req := &ListNics{
@ -19,12 +45,34 @@ func (nic *Nic) ListRequest() (ListCommand, error) {
return req, nil
}
// name returns the CloudStack API command name
func (*ListNics) name() string {
return "listNics"
// NicSecondaryIP represents a link between NicID and IPAddress
type NicSecondaryIP struct {
ID *UUID `json:"id,omitempty" doc:"the ID of the secondary private IP addr"`
IPAddress net.IP `json:"ipaddress,omitempty" doc:"Secondary IP address"`
NetworkID *UUID `json:"networkid,omitempty" doc:"the ID of the network"`
NicID *UUID `json:"nicid,omitempty" doc:"the ID of the nic"`
VirtualMachineID *UUID `json:"virtualmachineid,omitempty" doc:"the ID of the vm"`
}
func (*ListNics) response() interface{} {
// ListNics represents the NIC search
type ListNics struct {
ForDisplay bool `json:"fordisplay,omitempty" doc:"list resources by display flag; only ROOT admin is eligible to pass this parameter"`
Keyword string `json:"keyword,omitempty" doc:"List by keyword"`
NetworkID *UUID `json:"networkid,omitempty" doc:"list nic of the specific vm's network"`
NicID *UUID `json:"nicid,omitempty" doc:"the ID of the nic to to list IPs"`
Page int `json:"page,omitempty"`
PageSize int `json:"pagesize,omitempty"`
VirtualMachineID *UUID `json:"virtualmachineid" doc:"the ID of the vm"`
_ bool `name:"listNics" description:"list the vm nics IP to NIC"`
}
// ListNicsResponse represents a list of templates
type ListNicsResponse struct {
Count int `json:"count"`
Nic []Nic `json:"nic"`
}
func (ListNics) response() interface{} {
return new(ListNicsResponse)
}
@ -38,7 +86,7 @@ func (ls *ListNics) SetPageSize(pageSize int) {
ls.PageSize = pageSize
}
func (*ListNics) each(resp interface{}, callback IterateItemFunc) {
func (ListNics) each(resp interface{}, callback IterateItemFunc) {
nics := resp.(*ListNicsResponse)
for i := range nics.Nic {
if !callback(&(nics.Nic[i]), nil) {
@ -47,28 +95,47 @@ func (*ListNics) each(resp interface{}, callback IterateItemFunc) {
}
}
// name returns the CloudStack API command name: addIpToNic
func (*AddIPToNic) name() string {
return "addIpToNic"
}
func (*AddIPToNic) asyncResponse() interface{} {
return new(AddIPToNicResponse)
// AddIPToNic (Async) represents the assignation of a secondary IP
type AddIPToNic struct {
NicID *UUID `json:"nicid" doc:"the ID of the nic to which you want to assign private IP"`
IPAddress net.IP `json:"ipaddress,omitempty" doc:"Secondary IP Address"`
_ bool `name:"addIpToNic" description:"Assigns secondary IP to NIC"`
}
// name returns the CloudStack API command name: removeIpFromNic
func (*RemoveIPFromNic) name() string {
return "removeIpFromNic"
func (AddIPToNic) response() interface{} {
return new(AsyncJobResult)
}
func (*RemoveIPFromNic) asyncResponse() interface{} {
func (AddIPToNic) asyncResponse() interface{} {
return new(NicSecondaryIP)
}
// RemoveIPFromNic (Async) represents a deletion request
type RemoveIPFromNic struct {
ID *UUID `json:"id" doc:"the ID of the secondary ip address to nic"`
_ bool `name:"removeIpFromNic" description:"Removes secondary IP from the NIC."`
}
func (RemoveIPFromNic) response() interface{} {
return new(AsyncJobResult)
}
func (RemoveIPFromNic) asyncResponse() interface{} {
return new(booleanResponse)
}
// name returns the CloudStack API command name: activateIp6
func (*ActivateIP6) name() string {
return "activateIp6"
// ActivateIP6 (Async) activates the IP6 on the given NIC
//
// Exoscale specific API: https://community.exoscale.ch/api/compute/#activateip6_GET
type ActivateIP6 struct {
NicID *UUID `json:"nicid" doc:"the ID of the nic to which you want to assign the IPv6"`
_ bool `name:"activateIp6" description:"Activate the IPv6 on the VM's nic"`
}
func (*ActivateIP6) asyncResponse() interface{} {
return new(ActivateIP6Response)
func (ActivateIP6) response() interface{} {
return new(AsyncJobResult)
}
func (ActivateIP6) asyncResponse() interface{} {
return new(Nic)
}

View file

@ -1,89 +0,0 @@
package egoscale
import (
"net"
)
// Nic represents a Network Interface Controller (NIC)
//
// See: http://docs.cloudstack.apache.org/projects/cloudstack-administration/en/latest/networking_and_traffic.html#configuring-multiple-ip-addresses-on-a-single-nic
type Nic struct {
BroadcastURI string `json:"broadcasturi,omitempty" doc:"the broadcast uri of the nic"`
DeviceID string `json:"deviceid,omitempty" doc:"device id for the network when plugged into the virtual machine"`
Gateway net.IP `json:"gateway,omitempty" doc:"the gateway of the nic"`
ID string `json:"id,omitempty" doc:"the ID of the nic"`
IP6Address net.IP `json:"ip6address,omitempty" doc:"the IPv6 address of network"`
IP6Cidr string `json:"ip6cidr,omitempty" doc:"the cidr of IPv6 network"`
IP6Gateway net.IP `json:"ip6gateway,omitempty" doc:"the gateway of IPv6 network"`
IPAddress net.IP `json:"ipaddress,omitempty" doc:"the ip address of the nic"`
IsDefault bool `json:"isdefault,omitempty" doc:"true if nic is default, false otherwise"`
IsolationURI string `json:"isolationuri,omitempty" doc:"the isolation uri of the nic"`
MacAddress string `json:"macaddress,omitempty" doc:"true if nic is default, false otherwise"`
Netmask net.IP `json:"netmask,omitempty" doc:"the netmask of the nic"`
NetworkID string `json:"networkid,omitempty" doc:"the ID of the corresponding network"`
NetworkName string `json:"networkname,omitempty" doc:"the name of the corresponding network"`
SecondaryIP []NicSecondaryIP `json:"secondaryip,omitempty" doc:"the Secondary ipv4 addr of nic"`
TrafficType string `json:"traffictype,omitempty" doc:"the traffic type of the nic"`
Type string `json:"type,omitempty" doc:"the type of the nic"`
VirtualMachineID string `json:"virtualmachineid,omitempty" doc:"Id of the vm to which the nic belongs"`
}
// NicSecondaryIP represents a link between NicID and IPAddress
type NicSecondaryIP struct {
ID string `json:"id,omitempty" doc:"the ID of the secondary private IP addr"`
IPAddress net.IP `json:"ipaddress,omitempty" doc:"Secondary IP address"`
NetworkID string `json:"networkid,omitempty" doc:"the ID of the network"`
NicID string `json:"nicid,omitempty" doc:"the ID of the nic"`
VirtualMachineID string `json:"virtualmachineid,omitempty" doc:"the ID of the vm"`
}
// ListNics represents the NIC search
//
// CloudStack API: http://cloudstack.apache.org/api/apidocs-4.10/apis/listNics.html
type ListNics struct {
ForDisplay bool `json:"fordisplay,omitempty" doc:"list resources by display flag; only ROOT admin is eligible to pass this parameter"`
Keyword string `json:"keyword,omitempty" doc:"List by keyword"`
NetworkID string `json:"networkid,omitempty" doc:"list nic of the specific vm's network"`
NicID string `json:"nicid,omitempty" doc:"the ID of the nic to to list IPs"`
Page int `json:"page,omitempty"`
PageSize int `json:"pagesize,omitempty"`
VirtualMachineID string `json:"virtualmachineid" doc:"the ID of the vm"`
}
// ListNicsResponse represents a list of templates
type ListNicsResponse struct {
Count int `json:"count"`
Nic []Nic `json:"nic"`
}
// AddIPToNic (Async) represents the assignation of a secondary IP
//
// CloudStack API: http://cloudstack.apache.org/api/apidocs-4.10/apis/addIpToNic.html
type AddIPToNic struct {
NicID string `json:"nicid" doc:"the ID of the nic to which you want to assign private IP"`
IPAddress net.IP `json:"ipaddress,omitempty" doc:"Secondary IP Address"`
}
// AddIPToNicResponse represents the addition of an IP to a NIC
type AddIPToNicResponse struct {
NicSecondaryIP NicSecondaryIP `json:"nicsecondaryip"`
}
// RemoveIPFromNic (Async) represents a deletion request
//
// CloudStack API: http://cloudstack.apache.org/api/apidocs-4.10/apis/removeIpFromNic.html
type RemoveIPFromNic struct {
ID string `json:"id" doc:"the ID of the secondary ip address to nic"`
}
// ActivateIP6 (Async) activates the IP6 on the given NIC
//
// Exoscale specific API: https://community.exoscale.ch/api/compute/#activateip6_GET
type ActivateIP6 struct {
NicID string `json:"nicid" doc:"the ID of the nic to which you want to assign the IPv6"`
}
// ActivateIP6Response represents the modified NIC
type ActivateIP6Response struct {
Nic Nic `json:"nic"`
}

16
vendor/github.com/exoscale/egoscale/record_string.go generated vendored Normal file
View file

@ -0,0 +1,16 @@
// Code generated by "stringer -type=Record"; DO NOT EDIT.
package egoscale
import "strconv"
const _Record_name = "AAAAAALIASCNAMEHINFOMXNAPTRNSPOOLSPFSRVSSHFPTXTURL"
var _Record_index = [...]uint8{0, 1, 5, 10, 15, 20, 22, 27, 29, 33, 36, 39, 44, 47, 50}
func (i Record) String() string {
if i < 0 || i >= Record(len(_Record_index)-1) {
return "Record(" + strconv.FormatInt(int64(i), 10) + ")"
}
return _Record_name[_Record_index[i]:_Record_index[i+1]]
}

Some files were not shown because too many files have changed in this diff Show more