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

View file

@ -76,3 +76,7 @@
[[constraint]] [[constraint]]
branch = "master" branch = "master"
name = "github.com/sacloud/libsacloud" 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 return err
} }
if recordID == 0 {
record := egoscale.DNSRecord{ record := egoscale.DNSRecord{
Name: recordName, Name: recordName,
TTL: ttl, TTL: ttl,
@ -65,13 +66,19 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error {
RecordType: "TXT", RecordType: "TXT",
} }
if recordID == 0 {
_, err := d.client.CreateRecord(zone, record) _, err := d.client.CreateRecord(zone, record)
if err != nil { if err != nil {
return errors.New("Error while creating DNS record: " + err.Error()) return errors.New("Error while creating DNS record: " + err.Error())
} }
} else { } else {
record.ID = recordID record := egoscale.UpdateDNSRecord{
ID: recordID,
Name: recordName,
TTL: ttl,
Content: value,
RecordType: "TXT",
}
_, err := d.client.UpdateRecord(zone, record) _, err := d.client.UpdateRecord(zone, record)
if err != nil { if err != nil {
return errors.New("Error while updating DNS record: " + err.Error()) 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"); // Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. // you may not use this file except in compliance with the License.
@ -64,7 +64,7 @@ var (
) )
var ( var (
metaClient = &http.Client{ defaultClient = &Client{hc: &http.Client{
Transport: &http.Transport{ Transport: &http.Transport{
Dial: (&net.Dialer{ Dial: (&net.Dialer{
Timeout: 2 * time.Second, Timeout: 2 * time.Second,
@ -72,15 +72,15 @@ var (
}).Dial, }).Dial,
ResponseHeaderTimeout: 2 * time.Second, ResponseHeaderTimeout: 2 * time.Second,
}, },
} }}
subscribeClient = &http.Client{ subscribeClient = &Client{hc: &http.Client{
Transport: &http.Transport{ Transport: &http.Transport{
Dial: (&net.Dialer{ Dial: (&net.Dialer{
Timeout: 2 * time.Second, Timeout: 2 * time.Second,
KeepAlive: 30 * time.Second, KeepAlive: 30 * time.Second,
}).Dial, }).Dial,
}, },
} }}
) )
// NotDefinedError is returned when requested metadata is not defined. // 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)) return fmt.Sprintf("metadata: GCE metadata %q not defined", string(suffix))
} }
// Get returns a value from the metadata service. func (c *cachedValue) get(cl *Client) (v string, err error) {
// 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) {
defer c.mu.Unlock() defer c.mu.Unlock()
c.mu.Lock() c.mu.Lock()
if c.v != "" { if c.v != "" {
return c.v, nil return c.v, nil
} }
if c.trim { if c.trim {
v, err = getTrimmed(c.k) v, err = cl.getTrimmed(c.k)
} else { } else {
v, err = Get(c.k) v, err = cl.Get(c.k)
} }
if err == nil { if err == nil {
c.v = v c.v = v
@ -201,7 +143,7 @@ func testOnGCE() bool {
go func() { go func() {
req, _ := http.NewRequest("GET", "http://"+metadataIP, nil) req, _ := http.NewRequest("GET", "http://"+metadataIP, nil)
req.Header.Set("User-Agent", userAgent) req.Header.Set("User-Agent", userAgent)
res, err := ctxhttp.Do(ctx, metaClient, req) res, err := ctxhttp.Do(ctx, defaultClient.hc, req)
if err != nil { if err != nil {
resc <- false resc <- false
return return
@ -266,6 +208,255 @@ func systemInfoSuggestsGCE() bool {
return name == "Google" || name == "Google Compute Engine" 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. // Subscribe subscribes to a value from the metadata service.
// The suffix is appended to "http://${GCE_METADATA_HOST}/computeMetadata/v1/". // The suffix is appended to "http://${GCE_METADATA_HOST}/computeMetadata/v1/".
// The suffix may contain query parameters. // 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 // 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 // is deleted. Subscribe returns the error value returned from the last call to
// fn, which may be nil when ok == false. // 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 const failedSubscribeSleep = time.Second * 5
// First check to see if the metadata value exists at all. // 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 { if err != nil {
return err 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=" suffix += "?wait_for_change=true&last_etag="
} }
for { for {
val, etag, err := getETag(subscribeClient, suffix+url.QueryEscape(lastETag)) val, etag, err := c.getETag(suffix + url.QueryEscape(lastETag))
if err != nil { if err != nil {
if _, deleted := err.(NotDefinedError); !deleted { if _, deleted := err.(NotDefinedError); !deleted {
time.Sleep(failedSubscribeSleep) 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. // ZonesDeleteFuture an abstraction for monitoring and retrieving the results of a long-running operation.
type ZonesDeleteFuture struct { type ZonesDeleteFuture struct {
azure.Future azure.Future
req *http.Request
} }
// Result returns the result of the asynchronous operation. // Result returns the result of the asynchronous operation.
// If the operation has not completed it will return an error. // 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 var done bool
done, err = future.Done(client) done, err = future.Done(client)
if err != nil { if err != nil {
@ -730,34 +729,9 @@ func (future ZonesDeleteFuture) Result(client ZonesClient) (ar autorest.Response
return return
} }
if !done { if !done {
return ar, azure.NewAsyncOpIncompleteError("dns.ZonesDeleteFuture") err = 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")
}
return return
} }
var req *http.Request ar.Response = future.Response()
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")
}
return return
} }

View file

@ -21,6 +21,7 @@ import (
"context" "context"
"github.com/Azure/go-autorest/autorest" "github.com/Azure/go-autorest/autorest"
"github.com/Azure/go-autorest/autorest/azure" "github.com/Azure/go-autorest/autorest/azure"
"github.com/Azure/go-autorest/autorest/validation"
"net/http" "net/http"
) )
@ -41,7 +42,7 @@ func NewRecordSetsClientWithBaseURI(baseURI string, subscriptionID string) Recor
// CreateOrUpdate creates or updates a record set within a DNS zone. // CreateOrUpdate creates or updates a record set within a DNS zone.
// Parameters: // 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). // 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. // 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 // 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 // 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. // 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) { 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) req, err := client.CreateOrUpdatePreparer(ctx, resourceGroupName, zoneName, relativeRecordSetName, recordType, parameters, ifMatch, ifNoneMatch)
if err != nil { if err != nil {
err = autorest.NewErrorWithError(err, "dns.RecordSetsClient", "CreateOrUpdate", nil, "Failure preparing request") 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. // Delete deletes a record set from a DNS zone. This operation cannot be undone.
// Parameters: // 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). // 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. // 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 // 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 // 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. // 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) { 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) req, err := client.DeletePreparer(ctx, resourceGroupName, zoneName, relativeRecordSetName, recordType, ifMatch)
if err != nil { if err != nil {
err = autorest.NewErrorWithError(err, "dns.RecordSetsClient", "Delete", nil, "Failure preparing request") 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. // Get gets a record set.
// Parameters: // 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). // 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. // relativeRecordSetName - the name of the record set, relative to the name of the zone.
// recordType - the type of DNS record in this record set. // 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) { 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) req, err := client.GetPreparer(ctx, resourceGroupName, zoneName, relativeRecordSetName, recordType)
if err != nil { if err != nil {
err = autorest.NewErrorWithError(err, "dns.RecordSetsClient", "Get", nil, "Failure preparing request") 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. // ListByDNSZone lists all record sets in a DNS zone.
// Parameters: // 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). // 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. // 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 // 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 // enumerations. If this parameter is specified, Enumeration will return only records that end with
// .<recordSetNameSuffix> // .<recordSetNameSuffix>
func (client RecordSetsClient) ListByDNSZone(ctx context.Context, resourceGroupName string, zoneName string, top *int32, recordsetnamesuffix string) (result RecordSetListResultPage, err error) { 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 result.fn = client.listByDNSZoneNextResults
req, err := client.ListByDNSZonePreparer(ctx, resourceGroupName, zoneName, top, recordsetnamesuffix) req, err := client.ListByDNSZonePreparer(ctx, resourceGroupName, zoneName, top, recordsetnamesuffix)
if err != nil { 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. // ListByType lists the record sets of a specified type in a DNS zone.
// Parameters: // 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). // zoneName - the name of the DNS zone (without a terminating dot).
// recordType - the type of record sets to enumerate. // 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. // 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 // enumerations. If this parameter is specified, Enumeration will return only records that end with
// .<recordSetNameSuffix> // .<recordSetNameSuffix>
func (client RecordSetsClient) ListByType(ctx context.Context, resourceGroupName string, zoneName string, recordType RecordType, top *int32, recordsetnamesuffix string) (result RecordSetListResultPage, err error) { 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 result.fn = client.listByTypeNextResults
req, err := client.ListByTypePreparer(ctx, resourceGroupName, zoneName, recordType, top, recordsetnamesuffix) req, err := client.ListByTypePreparer(ctx, resourceGroupName, zoneName, recordType, top, recordsetnamesuffix)
if err != nil { if err != nil {
@ -488,7 +529,7 @@ func (client RecordSetsClient) ListByTypeComplete(ctx context.Context, resourceG
// Update updates a record set within a DNS zone. // Update updates a record set within a DNS zone.
// Parameters: // 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). // 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. // relativeRecordSetName - the name of the record set, relative to the name of the zone.
// recordType - the type of DNS record in this record set. // 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 // 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. // 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) { 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) req, err := client.UpdatePreparer(ctx, resourceGroupName, zoneName, relativeRecordSetName, recordType, parameters, ifMatch)
if err != nil { if err != nil {
err = autorest.NewErrorWithError(err, "dns.RecordSetsClient", "Update", nil, "Failure preparing request") err = autorest.NewErrorWithError(err, "dns.RecordSetsClient", "Update", nil, "Failure preparing request")

View file

@ -21,6 +21,7 @@ import (
"context" "context"
"github.com/Azure/go-autorest/autorest" "github.com/Azure/go-autorest/autorest"
"github.com/Azure/go-autorest/autorest/azure" "github.com/Azure/go-autorest/autorest/azure"
"github.com/Azure/go-autorest/autorest/validation"
"net/http" "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. // CreateOrUpdate creates or updates a DNS zone. Does not modify DNS records within the zone.
// Parameters: // 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). // zoneName - the name of the DNS zone (without a terminating dot).
// parameters - parameters supplied to the CreateOrUpdate operation. // 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 // 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. // ifNoneMatch - set to '*' to allow a new DNS zone to be created, but to prevent updating an existing zone.
// Other values will be ignored. // 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) { 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) req, err := client.CreateOrUpdatePreparer(ctx, resourceGroupName, zoneName, parameters, ifMatch, ifNoneMatch)
if err != nil { if err != nil {
err = autorest.NewErrorWithError(err, "dns.ZonesClient", "CreateOrUpdate", nil, "Failure preparing request") 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 // Delete deletes a DNS zone. WARNING: All DNS records in the zone will also be deleted. This operation cannot be
// undone. // undone.
// Parameters: // 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). // 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 // 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. // 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) { 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) req, err := client.DeletePreparer(ctx, resourceGroupName, zoneName, ifMatch)
if err != nil { if err != nil {
err = autorest.NewErrorWithError(err, "dns.ZonesClient", "Delete", nil, "Failure preparing request") 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 // DeleteSender sends the Delete request. The method will close the
// http.Response Body if it receives an error. // http.Response Body if it receives an error.
func (client ZonesClient) DeleteSender(req *http.Request) (future ZonesDeleteFuture, err error) { func (client ZonesClient) DeleteSender(req *http.Request) (future ZonesDeleteFuture, err error) {
sender := autorest.DecorateSender(client, azure.DoRetryWithRegistration(client.Client)) var resp *http.Response
future.Future = azure.NewFuture(req) resp, err = autorest.SendWithSender(client, req,
future.req = req azure.DoRetryWithRegistration(client.Client))
_, err = future.Done(sender)
if err != nil { if err != nil {
return return
} }
err = autorest.Respond(future.Response(), err = autorest.Respond(resp, azure.WithErrorUnlessStatusCode(http.StatusOK, http.StatusAccepted, http.StatusNoContent))
azure.WithErrorUnlessStatusCode(http.StatusOK, http.StatusAccepted, http.StatusNoContent)) if err != nil {
return
}
future.Future, err = azure.NewFutureFromResponse(resp)
return 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. // Get gets a DNS zone. Retrieves the zone properties, but not the record sets within the zone.
// Parameters: // 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). // 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) { 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) req, err := client.GetPreparer(ctx, resourceGroupName, zoneName)
if err != nil { if err != nil {
err = autorest.NewErrorWithError(err, "dns.ZonesClient", "Get", nil, "Failure preparing request") 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. // ListByResourceGroup lists the DNS zones within a resource group.
// Parameters: // 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. // 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) { 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 result.fn = client.listByResourceGroupNextResults
req, err := client.ListByResourceGroupPreparer(ctx, resourceGroupName, top) req, err := client.ListByResourceGroupPreparer(ctx, resourceGroupName, top)
if err != nil { 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. // Changes may cause incorrect behavior and will be lost if the code is regenerated.
// Number contains the semantic version of this SDK. // 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 // OAuthConfig represents the endpoints needed
// in OAuth operations // in OAuth operations
type OAuthConfig struct { type OAuthConfig struct {
AuthorityEndpoint url.URL AuthorityEndpoint url.URL `json:"authorityEndpoint"`
AuthorizeEndpoint url.URL AuthorizeEndpoint url.URL `json:"authorizeEndpoint"`
TokenEndpoint url.URL TokenEndpoint url.URL `json:"tokenEndpoint"`
DeviceCodeEndpoint url.URL DeviceCodeEndpoint url.URL `json:"deviceCodeEndpoint"`
} }
// IsZero returns true if the OAuthConfig object is zero-initialized. // IsZero returns true if the OAuthConfig object is zero-initialized.

View file

@ -22,8 +22,10 @@ import (
"crypto/x509" "crypto/x509"
"encoding/base64" "encoding/base64"
"encoding/json" "encoding/json"
"errors"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"math"
"net" "net"
"net/http" "net/http"
"net/url" "net/url"
@ -33,6 +35,7 @@ import (
"time" "time"
"github.com/Azure/go-autorest/autorest/date" "github.com/Azure/go-autorest/autorest/date"
"github.com/Azure/go-autorest/version"
"github.com/dgrijalva/jwt-go" "github.com/dgrijalva/jwt-go"
) )
@ -59,6 +62,9 @@ const (
// msiEndpoint is the well known endpoint for getting MSI authentications tokens // msiEndpoint is the well known endpoint for getting MSI authentications tokens
msiEndpoint = "http://169.254.169.254/metadata/identity/oauth2/token" 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 // 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 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 // ServicePrincipalNoSecret represents a secret type that contains no secret
// meaning it is not valid for fetching a fresh token. This is used by Manual // meaning it is not valid for fetching a fresh token. This is used by Manual
type ServicePrincipalNoSecret struct { 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") 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 // MarshalJSON implements the json.Marshaler interface.
// that is submitted when acquiring an oAuth token. func (noSecret ServicePrincipalNoSecret) MarshalJSON() ([]byte, error) {
type ServicePrincipalSecret interface { type tokenType struct {
SetAuthenticationValues(spt *ServicePrincipalToken, values *url.Values) error Type string `json:"type"`
}
return json.Marshal(tokenType{
Type: "ServicePrincipalNoSecret",
})
} }
// ServicePrincipalTokenSecret implements ServicePrincipalSecret for client_secret type authorization. // ServicePrincipalTokenSecret implements ServicePrincipalSecret for client_secret type authorization.
type ServicePrincipalTokenSecret struct { type ServicePrincipalTokenSecret struct {
ClientSecret string ClientSecret string `json:"value"`
} }
// SetAuthenticationValues is a method of the interface ServicePrincipalSecret. // SetAuthenticationValues is a method of the interface ServicePrincipalSecret.
@ -165,49 +181,24 @@ func (tokenSecret *ServicePrincipalTokenSecret) SetAuthenticationValues(spt *Ser
return nil 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. // ServicePrincipalCertificateSecret implements ServicePrincipalSecret for generic RSA cert auth with signed JWTs.
type ServicePrincipalCertificateSecret struct { type ServicePrincipalCertificateSecret struct {
Certificate *x509.Certificate Certificate *x509.Certificate
PrivateKey *rsa.PrivateKey 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. // SignJwt returns the JWT signed with the certificate's private key.
func (secret *ServicePrincipalCertificateSecret) SignJwt(spt *ServicePrincipalToken) (string, error) { func (secret *ServicePrincipalCertificateSecret) SignJwt(spt *ServicePrincipalToken) (string, error) {
hasher := sha1.New() hasher := sha1.New()
@ -228,9 +219,9 @@ func (secret *ServicePrincipalCertificateSecret) SignJwt(spt *ServicePrincipalTo
token := jwt.New(jwt.SigningMethodRS256) token := jwt.New(jwt.SigningMethodRS256)
token.Header["x5t"] = thumbprint token.Header["x5t"] = thumbprint
token.Claims = jwt.MapClaims{ token.Claims = jwt.MapClaims{
"aud": spt.oauthConfig.TokenEndpoint.String(), "aud": spt.inner.OauthConfig.TokenEndpoint.String(),
"iss": spt.clientID, "iss": spt.inner.ClientID,
"sub": spt.clientID, "sub": spt.inner.ClientID,
"jti": base64.URLEncoding.EncodeToString(jti), "jti": base64.URLEncoding.EncodeToString(jti),
"nbf": time.Now().Unix(), "nbf": time.Now().Unix(),
"exp": time.Now().Add(time.Hour * 24).Unix(), "exp": time.Now().Add(time.Hour * 24).Unix(),
@ -253,19 +244,151 @@ func (secret *ServicePrincipalCertificateSecret) SetAuthenticationValues(spt *Se
return nil 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. // ServicePrincipalToken encapsulates a Token created for a Service Principal.
type ServicePrincipalToken struct { type ServicePrincipalToken struct {
token Token inner servicePrincipalToken
secret ServicePrincipalSecret
oauthConfig OAuthConfig
clientID string
resource string
autoRefresh bool
refreshLock *sync.RWMutex refreshLock *sync.RWMutex
refreshWithin time.Duration
sender Sender sender Sender
refreshCallbacks []TokenRefreshCallback 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 { 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") return nil, fmt.Errorf("parameter 'secret' cannot be nil")
} }
spt := &ServicePrincipalToken{ spt := &ServicePrincipalToken{
oauthConfig: oauthConfig, inner: servicePrincipalToken{
secret: secret, OauthConfig: oauthConfig,
clientID: id, Secret: secret,
resource: resource, ClientID: id,
autoRefresh: true, Resource: resource,
AutoRefresh: true,
RefreshWithin: defaultRefresh,
},
refreshLock: &sync.RWMutex{}, refreshLock: &sync.RWMutex{},
refreshWithin: defaultRefresh,
sender: &http.Client{}, sender: &http.Client{},
refreshCallbacks: callbacks, refreshCallbacks: callbacks,
} }
@ -327,7 +452,39 @@ func NewServicePrincipalTokenFromManualToken(oauthConfig OAuthConfig, clientID s
return nil, err 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 return spt, nil
} }
@ -495,20 +652,23 @@ func newServicePrincipalTokenFromMSI(msiEndpoint, resource string, userAssignedI
msiEndpointURL.RawQuery = v.Encode() msiEndpointURL.RawQuery = v.Encode()
spt := &ServicePrincipalToken{ spt := &ServicePrincipalToken{
oauthConfig: OAuthConfig{ inner: servicePrincipalToken{
OauthConfig: OAuthConfig{
TokenEndpoint: *msiEndpointURL, TokenEndpoint: *msiEndpointURL,
}, },
secret: &ServicePrincipalMSISecret{}, Secret: &ServicePrincipalMSISecret{},
resource: resource, Resource: resource,
autoRefresh: true, AutoRefresh: true,
RefreshWithin: defaultRefresh,
},
refreshLock: &sync.RWMutex{}, refreshLock: &sync.RWMutex{},
refreshWithin: defaultRefresh,
sender: &http.Client{}, sender: &http.Client{},
refreshCallbacks: callbacks, refreshCallbacks: callbacks,
MaxMSIRefreshAttempts: defaultMaxMSIRefreshAttempts,
} }
if userAssignedID != nil { if userAssignedID != nil {
spt.clientID = *userAssignedID spt.inner.ClientID = *userAssignedID
} }
return spt, nil 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 // 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. // RefreshWithin) and autoRefresh flag is on. This method is safe for concurrent use.
func (spt *ServicePrincipalToken) EnsureFreshWithContext(ctx context.Context) error { 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 // take the write lock then check to see if the token was already refreshed
spt.refreshLock.Lock() spt.refreshLock.Lock()
defer spt.refreshLock.Unlock() defer spt.refreshLock.Unlock()
if spt.token.WillExpireIn(spt.refreshWithin) { if spt.inner.Token.WillExpireIn(spt.inner.RefreshWithin) {
return spt.refreshInternal(ctx, spt.resource) return spt.refreshInternal(ctx, spt.inner.Resource)
} }
} }
return nil return nil
@ -558,7 +718,7 @@ func (spt *ServicePrincipalToken) EnsureFreshWithContext(ctx context.Context) er
func (spt *ServicePrincipalToken) InvokeRefreshCallbacks(token Token) error { func (spt *ServicePrincipalToken) InvokeRefreshCallbacks(token Token) error {
if spt.refreshCallbacks != nil { if spt.refreshCallbacks != nil {
for _, callback := range spt.refreshCallbacks { for _, callback := range spt.refreshCallbacks {
err := callback(spt.token) err := callback(spt.inner.Token)
if err != nil { if err != nil {
return fmt.Errorf("adal: TokenRefreshCallback handler failed. Error = '%v'", err) 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 { func (spt *ServicePrincipalToken) RefreshWithContext(ctx context.Context) error {
spt.refreshLock.Lock() spt.refreshLock.Lock()
defer spt.refreshLock.Unlock() 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. // 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 { func (spt *ServicePrincipalToken) getGrantType() string {
switch spt.secret.(type) { switch spt.inner.Secret.(type) {
case *ServicePrincipalUsernamePasswordSecret: case *ServicePrincipalUsernamePasswordSecret:
return OAuthGrantTypeUserPass return OAuthGrantTypeUserPass
case *ServicePrincipalAuthorizationCodeSecret: case *ServicePrincipalAuthorizationCodeSecret:
@ -615,22 +775,31 @@ func isIMDS(u url.URL) bool {
} }
func (spt *ServicePrincipalToken) refreshInternal(ctx context.Context, resource string) error { 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 { if err != nil {
return fmt.Errorf("adal: Failed to build the refresh request. Error = '%v'", err) return fmt.Errorf("adal: Failed to build the refresh request. Error = '%v'", err)
} }
req.Header.Add("User-Agent", version.UserAgent())
req = req.WithContext(ctx) req = req.WithContext(ctx)
if !isIMDS(spt.oauthConfig.TokenEndpoint) { if !isIMDS(spt.inner.OauthConfig.TokenEndpoint) {
v := url.Values{} v := url.Values{}
v.Set("client_id", spt.clientID) v.Set("client_id", spt.inner.ClientID)
v.Set("resource", resource) v.Set("resource", resource)
if spt.token.RefreshToken != "" { if spt.inner.Token.RefreshToken != "" {
v.Set("grant_type", OAuthGrantTypeRefreshToken) 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 { } else {
v.Set("grant_type", spt.getGrantType()) v.Set("grant_type", spt.getGrantType())
err := spt.secret.SetAuthenticationValues(spt, &v) err := spt.inner.Secret.SetAuthenticationValues(spt, &v)
if err != nil { if err != nil {
return err return err
} }
@ -643,14 +812,14 @@ func (spt *ServicePrincipalToken) refreshInternal(ctx context.Context, resource
req.Body = body req.Body = body
} }
if _, ok := spt.secret.(*ServicePrincipalMSISecret); ok { if _, ok := spt.inner.Secret.(*ServicePrincipalMSISecret); ok {
req.Method = http.MethodGet req.Method = http.MethodGet
req.Header.Set(metadataHeader, "true") req.Header.Set(metadataHeader, "true")
} }
var resp *http.Response var resp *http.Response
if isIMDS(spt.oauthConfig.TokenEndpoint) { if isIMDS(spt.inner.OauthConfig.TokenEndpoint) {
resp, err = retry(spt.sender, req) resp, err = retryForIMDS(spt.sender, req, spt.MaxMSIRefreshAttempts)
} else { } else {
resp, err = spt.sender.Do(req) 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)) 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) 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{ retries := []int{
http.StatusRequestTimeout, // 408 http.StatusRequestTimeout, // 408
http.StatusTooManyRequests, // 429 http.StatusTooManyRequests, // 429
@ -698,8 +869,10 @@ func retry(sender Sender, req *http.Request) (resp *http.Response, err error) {
http.StatusServiceUnavailable, // 503 http.StatusServiceUnavailable, // 503
http.StatusGatewayTimeout, // 504 http.StatusGatewayTimeout, // 504
} }
// Extra retry status codes requered // extra retry status codes specific to IMDS
retries = append(retries, http.StatusNotFound, retries = append(retries,
http.StatusNotFound,
http.StatusGone,
// all remaining 5xx // all remaining 5xx
http.StatusNotImplemented, http.StatusNotImplemented,
http.StatusHTTPVersionNotSupported, http.StatusHTTPVersionNotSupported,
@ -709,36 +882,52 @@ func retry(sender Sender, req *http.Request) (resp *http.Response, err error) {
http.StatusNotExtended, http.StatusNotExtended,
http.StatusNetworkAuthenticationRequired) 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 attempt := 0
maxAttempts := 5 delay := time.Duration(0)
for attempt < maxAttempts { for attempt < maxAttempts {
resp, err = sender.Do(req) resp, err = sender.Do(req)
// retry on temporary network errors, e.g. transient network failures. // 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 return
} }
if !delay(resp, req.Context().Done()) { // perform exponential backoff with a cap.
select { // must increment attempt before calculating delay.
case <-time.After(time.Second):
attempt++ 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(): case <-req.Context().Done():
err = req.Context().Err() err = req.Context().Err()
return return
} }
} }
}
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 { 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 true
} }
return false return false
} }
// returns true if slice ints contains the value n
func containsInt(ints []int, n int) bool { func containsInt(ints []int, n int) bool {
for _, i := range ints { for _, i := range ints {
if i == n { if i == n {
@ -748,31 +937,15 @@ func containsInt(ints []int, n int) bool {
return false 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. // SetAutoRefresh enables or disables automatic refreshing of stale tokens.
func (spt *ServicePrincipalToken) SetAutoRefresh(autoRefresh bool) { 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 // SetRefreshWithin sets the interval within which if the token will expire, EnsureFresh will
// refresh the token. // refresh the token.
func (spt *ServicePrincipalToken) SetRefreshWithin(d time.Duration) { func (spt *ServicePrincipalToken) SetRefreshWithin(d time.Duration) {
spt.refreshWithin = d spt.inner.RefreshWithin = d
return return
} }
@ -784,12 +957,12 @@ func (spt *ServicePrincipalToken) SetSender(s Sender) { spt.sender = s }
func (spt *ServicePrincipalToken) OAuthToken() string { func (spt *ServicePrincipalToken) OAuthToken() string {
spt.refreshLock.RLock() spt.refreshLock.RLock()
defer spt.refreshLock.RUnlock() defer spt.refreshLock.RUnlock()
return spt.token.OAuthToken() return spt.inner.Token.OAuthToken()
} }
// Token returns a copy of the current token. // Token returns a copy of the current token.
func (spt *ServicePrincipalToken) Token() Token { func (spt *ServicePrincipalToken) Token() Token {
spt.refreshLock.RLock() spt.refreshLock.RLock()
defer spt.refreshLock.RUnlock() 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. // It makes the final GET call to retrieve the resultant payload.
func (f Future) GetResult(sender autorest.Sender) (*http.Response, error) { func (f Future) GetResult(sender autorest.Sender) (*http.Response, error) {
if f.pt.finalGetURL() == "" { 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) req, err := http.NewRequest(http.MethodGet, f.pt.finalGetURL(), nil)
if err != nil { if err != nil {
@ -342,6 +348,10 @@ func (pt *pollingTrackerBase) initializeState() error {
case http.StatusOK: case http.StatusOK:
if ps := pt.getProvisioningState(); ps != nil { if ps := pt.getProvisioningState(); ps != nil {
pt.State = *ps pt.State = *ps
if pt.hasFailed() {
pt.updateErrorFromResponse()
return pt.pollingError()
}
} else { } else {
pt.State = operationSucceeded pt.State = operationSucceeded
} }
@ -358,6 +368,7 @@ func (pt *pollingTrackerBase) initializeState() error {
default: default:
pt.State = operationFailed pt.State = operationFailed
pt.updateErrorFromResponse() pt.updateErrorFromResponse()
return pt.pollingError()
} }
return nil return nil
} }
@ -416,6 +427,7 @@ func (pt *pollingTrackerBase) pollForStatus(sender autorest.Sender) error {
// attempts to unmarshal a ServiceError type from the response body. // attempts to unmarshal a ServiceError type from the response body.
// if that fails then make a best attempt at creating something meaningful. // 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() { func (pt *pollingTrackerBase) updateErrorFromResponse() {
var err error var err error
if pt.resp.ContentLength != 0 { if pt.resp.ContentLength != 0 {
@ -425,8 +437,7 @@ func (pt *pollingTrackerBase) updateErrorFromResponse() {
re := respErr{} re := respErr{}
defer pt.resp.Body.Close() defer pt.resp.Body.Close()
var b []byte var b []byte
b, err = ioutil.ReadAll(pt.resp.Body) if b, err = ioutil.ReadAll(pt.resp.Body); err != nil {
if err != nil {
goto Default goto Default
} }
if err = json.Unmarshal(b, &re); err != nil { if err = json.Unmarshal(b, &re); err != nil {
@ -439,20 +450,29 @@ func (pt *pollingTrackerBase) updateErrorFromResponse() {
goto Default 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 pt.Err = re.ServiceError
return return
} }
} }
Default: Default:
se := &ServiceError{ se := &ServiceError{
Code: fmt.Sprintf("HTTP status code %v", pt.resp.StatusCode), Code: pt.pollingStatus(),
Message: pt.resp.Status, Message: "The async operation failed.",
} }
if err != nil { if err != nil {
se.InnerError = make(map[string]interface{}) se.InnerError = make(map[string]interface{})
se.InnerError["unmarshalError"] = err.Error() 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 pt.Err = se
} }

View file

@ -49,6 +49,7 @@ type ServiceError struct {
Target *string `json:"target"` Target *string `json:"target"`
Details []map[string]interface{} `json:"details"` Details []map[string]interface{} `json:"details"`
InnerError map[string]interface{} `json:"innererror"` InnerError map[string]interface{} `json:"innererror"`
AdditionalInfo []map[string]interface{} `json:"additionalInfo"`
} }
func (se ServiceError) Error() string { func (se ServiceError) Error() string {
@ -74,6 +75,14 @@ func (se ServiceError) Error() string {
result += fmt.Sprintf(" InnerError=%v", string(d)) 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 return result
} }
@ -91,6 +100,7 @@ func (se *ServiceError) UnmarshalJSON(b []byte) error {
Target *string `json:"target"` Target *string `json:"target"`
Details []map[string]interface{} `json:"details"` Details []map[string]interface{} `json:"details"`
InnerError map[string]interface{} `json:"innererror"` InnerError map[string]interface{} `json:"innererror"`
AdditionalInfo []map[string]interface{} `json:"additionalInfo"`
} }
type serviceError2 struct { type serviceError2 struct {
@ -99,31 +109,33 @@ func (se *ServiceError) UnmarshalJSON(b []byte) error {
Target *string `json:"target"` Target *string `json:"target"`
Details map[string]interface{} `json:"details"` Details map[string]interface{} `json:"details"`
InnerError map[string]interface{} `json:"innererror"` InnerError map[string]interface{} `json:"innererror"`
AdditionalInfo []map[string]interface{} `json:"additionalInfo"`
} }
se1 := serviceError1{} se1 := serviceError1{}
err := json.Unmarshal(b, &se1) err := json.Unmarshal(b, &se1)
if err == nil { 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 return nil
} }
se2 := serviceError2{} se2 := serviceError2{}
err = json.Unmarshal(b, &se2) err = json.Unmarshal(b, &se2)
if err == nil { 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) se.Details = append(se.Details, se2.Details)
return nil return nil
} }
return err 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.Code = code
se.Message = message se.Message = message
se.Target = target se.Target = target
se.Details = details se.Details = details
se.InnerError = inner se.InnerError = inner
se.AdditionalInfo = additional
} }
// RequestError describes an error response returned by Azure service. // RequestError describes an error response returned by Azure service.
@ -279,16 +291,29 @@ func WithErrorUnlessStatusCode(codes ...int) autorest.RespondDecorator {
resp.Body = ioutil.NopCloser(&b) resp.Body = ioutil.NopCloser(&b)
if decodeErr != nil { if decodeErr != nil {
return fmt.Errorf("autorest/azure: error response cannot be parsed: %q error: %v", b.String(), decodeErr) 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 // 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{ e.ServiceError = &ServiceError{
Code: "Unknown", Code: "Unknown",
Message: "Unknown service error", Message: "Unknown service error",
} }
if len(rawBody) > 0 {
e.ServiceError.Details = []map[string]interface{}{rawBody}
} }
} }
e.Response = resp
e.RequestID = ExtractRequestID(resp) e.RequestID = ExtractRequestID(resp)
if e.StatusCode == nil { if e.StatusCode == nil {
e.StatusCode = resp.StatusCode 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" "log"
"net/http" "net/http"
"net/http/cookiejar" "net/http/cookiejar"
"runtime" "strings"
"time" "time"
"github.com/Azure/go-autorest/logger"
"github.com/Azure/go-autorest/version"
) )
const ( const (
@ -41,15 +44,6 @@ const (
) )
var ( 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 are a defined group of status code for which the client will retry
StatusCodesForRetry = []int{ StatusCodesForRetry = []int{
http.StatusRequestTimeout, // 408 http.StatusRequestTimeout, // 408
@ -179,7 +173,7 @@ func NewClientWithUserAgent(ua string) Client {
PollingDuration: DefaultPollingDuration, PollingDuration: DefaultPollingDuration,
RetryAttempts: DefaultRetryAttempts, RetryAttempts: DefaultRetryAttempts,
RetryDuration: DefaultRetryDuration, RetryDuration: DefaultRetryDuration,
UserAgent: defaultUserAgent, UserAgent: version.UserAgent(),
} }
c.Sender = c.sender() c.Sender = c.sender()
c.AddToUserAgent(ua) 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") 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) resp, err := SendWithSender(c.sender(), r)
logger.Instance.WriteResponse(resp, logger.Filter{})
Respond(resp, c.ByInspecting()) Respond(resp, c.ByInspecting())
return resp, err return resp, err
} }

View file

@ -218,9 +218,10 @@ func IsTokenRefreshError(err error) bool {
return false 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 { 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 true
} }
return false 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 package autorest
import "github.com/Azure/go-autorest/version"
// Copyright 2017 Microsoft Corporation // Copyright 2017 Microsoft Corporation
// //
// Licensed under the Apache License, Version 2.0 (the "License"); // 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). // Version returns the semantic version (see http://semver.org).
func Version() string { 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 return
} }
c.Handlers.Send.PushFrontNamed(request.NamedHandler{Name: "awssdk.client.LogRequest", Fn: logRequest}) c.Handlers.Send.PushFrontNamed(LogHTTPRequestHandler)
c.Handlers.Send.PushBackNamed(request.NamedHandler{Name: "awssdk.client.LogResponse", Fn: logResponse}) c.Handlers.Send.PushBackNamed(LogHTTPResponseHandler)
} }

View file

@ -44,12 +44,22 @@ func (reader *teeReaderCloser) Close() error {
return reader.Source.Close() 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) { func logRequest(r *request.Request) {
logBody := r.Config.LogLevel.Matches(aws.LogDebugWithHTTPBody) logBody := r.Config.LogLevel.Matches(aws.LogDebugWithHTTPBody)
bodySeekable := aws.IsReaderSeekable(r.Body) bodySeekable := aws.IsReaderSeekable(r.Body)
dumpedBody, err := httputil.DumpRequestOut(r.HTTPRequest, logBody)
b, err := httputil.DumpRequestOut(r.HTTPRequest, logBody)
if err != nil { 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 return
} }
@ -63,7 +73,28 @@ func logRequest(r *request.Request) {
r.ResetBody() 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: const logRespMsg = `DEBUG: Response %s/%s Details:
@ -76,27 +107,44 @@ const logRespErrMsg = `DEBUG ERROR: Response %s/%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) { func logResponse(r *request.Request) {
lw := &logWriter{r.Config.Logger, bytes.NewBuffer(nil)} lw := &logWriter{r.Config.Logger, bytes.NewBuffer(nil)}
logBody := r.Config.LogLevel.Matches(aws.LogDebugWithHTTPBody)
if logBody {
r.HTTPResponse.Body = &teeReaderCloser{ r.HTTPResponse.Body = &teeReaderCloser{
Reader: io.TeeReader(r.HTTPResponse.Body, lw), Reader: io.TeeReader(r.HTTPResponse.Body, lw),
Source: r.HTTPResponse.Body, Source: r.HTTPResponse.Body,
} }
}
handlerFn := func(req *request.Request) { handlerFn := func(req *request.Request) {
body, err := httputil.DumpResponse(req.HTTPResponse, false) b, err := httputil.DumpResponse(req.HTTPResponse, false)
if err != nil { 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 return
} }
lw.Logger.Log(fmt.Sprintf(logRespMsg,
req.ClientInfo.ServiceName, req.Operation.Name, string(b)))
if logBody {
b, err := ioutil.ReadAll(lw.buf) b, err := ioutil.ReadAll(lw.buf)
if err != nil { 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 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)) lw.Logger.Log(string(b))
} }
} }
@ -110,3 +158,27 @@ func logResponse(r *request.Request) {
Name: handlerName, Fn: handlerFn, 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. // ClientInfo wraps immutable data from the client.Client structure.
type ClientInfo struct { type ClientInfo struct {
ServiceName string ServiceName string
ServiceID string
APIVersion string APIVersion string
Endpoint string Endpoint string
SigningName 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. // IsExpired returns if the credentials are expired.
func (e *Expiry) IsExpired() bool { func (e *Expiry) IsExpired() bool {
if e.CurrentTime == nil { curTime := e.CurrentTime
e.CurrentTime = time.Now 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 // Credentials will cache the credentials value until they expire. Once the value
// expires the next Get will attempt to retrieve valid credentials. // expires the next Get will attempt to retrieve valid credentials.
// //
@ -178,7 +179,8 @@ func (e *Expiry) IsExpired() bool {
type Credentials struct { type Credentials struct {
creds Value creds Value
forceRefresh bool forceRefresh bool
m sync.Mutex
m sync.RWMutex
provider Provider provider Provider
} }
@ -201,6 +203,17 @@ func NewCredentials(provider Provider) *Credentials {
// If Credentials.Expire() was called the credentials Value will be force // If Credentials.Expire() was called the credentials Value will be force
// expired, and the next call to Get() will cause them to be refreshed. // expired, and the next call to Get() will cause them to be refreshed.
func (c *Credentials) Get() (Value, error) { 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() c.m.Lock()
defer c.m.Unlock() defer c.m.Unlock()
@ -234,8 +247,8 @@ func (c *Credentials) Expire() {
// If the Credentials were forced to be expired with Expire() this will // If the Credentials were forced to be expired with Expire() this will
// reflect that override. // reflect that override.
func (c *Credentials) IsExpired() bool { func (c *Credentials) IsExpired() bool {
c.m.Lock() c.m.RLock()
defer c.m.Unlock() defer c.m.RUnlock()
return c.isExpired() return c.isExpired()
} }

View file

@ -4,7 +4,6 @@ import (
"bufio" "bufio"
"encoding/json" "encoding/json"
"fmt" "fmt"
"path"
"strings" "strings"
"time" "time"
@ -12,6 +11,7 @@ import (
"github.com/aws/aws-sdk-go/aws/client" "github.com/aws/aws-sdk-go/aws/client"
"github.com/aws/aws-sdk-go/aws/credentials" "github.com/aws/aws-sdk-go/aws/credentials"
"github.com/aws/aws-sdk-go/aws/ec2metadata" "github.com/aws/aws-sdk-go/aws/ec2metadata"
"github.com/aws/aws-sdk-go/internal/sdkuri"
) )
// ProviderName provides a name of EC2Role provider // ProviderName provides a name of EC2Role provider
@ -125,7 +125,7 @@ type ec2RoleCredRespBody struct {
Message string Message string
} }
const iamSecurityCredsPath = "/iam/security-credentials" const iamSecurityCredsPath = "iam/security-credentials/"
// requestCredList requests a list of credentials from the EC2 service. // 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 // 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 // If the credentials cannot be found, or there is an error reading the response
// and error will be returned. // and error will be returned.
func requestCred(client *ec2metadata.EC2Metadata, credsName string) (ec2RoleCredRespBody, error) { 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 { if err != nil {
return ec2RoleCredRespBody{}, return ec2RoleCredRespBody{},
awserr.New("EC2RoleRequestError", 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 { func CredChain(cfg *aws.Config, handlers request.Handlers) *credentials.Credentials {
return credentials.NewCredentials(&credentials.ChainProvider{ return credentials.NewCredentials(&credentials.ChainProvider{
VerboseErrors: aws.BoolValue(cfg.CredentialsChainVerboseErrors), 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.EnvProvider{},
&credentials.SharedCredentialsProvider{Filename: "", Profile: ""}, &credentials.SharedCredentialsProvider{Filename: "", Profile: ""},
RemoteCredProvider(*cfg, handlers), RemoteCredProvider(*cfg, handlers),
}, }
})
} }
const ( const (

View file

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

View file

@ -84,6 +84,7 @@ func decodeV3Endpoints(modelDef modelDefinition, opts DecodeModelOptions) (Resol
custAddEC2Metadata(p) custAddEC2Metadata(p)
custAddS3DualStack(p) custAddS3DualStack(p)
custRmIotDataService(p) custRmIotDataService(p)
custFixAppAutoscalingChina(p)
} }
return ps, nil return ps, nil
@ -122,6 +123,27 @@ func custRmIotDataService(p *partition) {
delete(p.Services, "data.iot") 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 { type decodeModelError struct {
awsError awsError
} }

View file

@ -48,7 +48,9 @@ const (
A4bServiceID = "a4b" // A4b. A4bServiceID = "a4b" // A4b.
AcmServiceID = "acm" // Acm. AcmServiceID = "acm" // Acm.
AcmPcaServiceID = "acm-pca" // AcmPca. AcmPcaServiceID = "acm-pca" // AcmPca.
ApiMediatailorServiceID = "api.mediatailor" // ApiMediatailor.
ApiPricingServiceID = "api.pricing" // ApiPricing. ApiPricingServiceID = "api.pricing" // ApiPricing.
ApiSagemakerServiceID = "api.sagemaker" // ApiSagemaker.
ApigatewayServiceID = "apigateway" // Apigateway. ApigatewayServiceID = "apigateway" // Apigateway.
ApplicationAutoscalingServiceID = "application-autoscaling" // ApplicationAutoscaling. ApplicationAutoscalingServiceID = "application-autoscaling" // ApplicationAutoscaling.
Appstream2ServiceID = "appstream2" // Appstream2. Appstream2ServiceID = "appstream2" // Appstream2.
@ -111,6 +113,7 @@ const (
ImportexportServiceID = "importexport" // Importexport. ImportexportServiceID = "importexport" // Importexport.
InspectorServiceID = "inspector" // Inspector. InspectorServiceID = "inspector" // Inspector.
IotServiceID = "iot" // Iot. IotServiceID = "iot" // Iot.
IotanalyticsServiceID = "iotanalytics" // Iotanalytics.
KinesisServiceID = "kinesis" // Kinesis. KinesisServiceID = "kinesis" // Kinesis.
KinesisanalyticsServiceID = "kinesisanalytics" // Kinesisanalytics. KinesisanalyticsServiceID = "kinesisanalytics" // Kinesisanalytics.
KinesisvideoServiceID = "kinesisvideo" // Kinesisvideo. KinesisvideoServiceID = "kinesisvideo" // Kinesisvideo.
@ -130,6 +133,7 @@ const (
ModelsLexServiceID = "models.lex" // ModelsLex. ModelsLexServiceID = "models.lex" // ModelsLex.
MonitoringServiceID = "monitoring" // Monitoring. MonitoringServiceID = "monitoring" // Monitoring.
MturkRequesterServiceID = "mturk-requester" // MturkRequester. MturkRequesterServiceID = "mturk-requester" // MturkRequester.
NeptuneServiceID = "neptune" // Neptune.
OpsworksServiceID = "opsworks" // Opsworks. OpsworksServiceID = "opsworks" // Opsworks.
OpsworksCmServiceID = "opsworks-cm" // OpsworksCm. OpsworksCmServiceID = "opsworks-cm" // OpsworksCm.
OrganizationsServiceID = "organizations" // Organizations. OrganizationsServiceID = "organizations" // Organizations.
@ -144,7 +148,6 @@ const (
RuntimeLexServiceID = "runtime.lex" // RuntimeLex. RuntimeLexServiceID = "runtime.lex" // RuntimeLex.
RuntimeSagemakerServiceID = "runtime.sagemaker" // RuntimeSagemaker. RuntimeSagemakerServiceID = "runtime.sagemaker" // RuntimeSagemaker.
S3ServiceID = "s3" // S3. S3ServiceID = "s3" // S3.
SagemakerServiceID = "sagemaker" // Sagemaker.
SdbServiceID = "sdb" // Sdb. SdbServiceID = "sdb" // Sdb.
SecretsmanagerServiceID = "secretsmanager" // Secretsmanager. SecretsmanagerServiceID = "secretsmanager" // Secretsmanager.
ServerlessrepoServiceID = "serverlessrepo" // Serverlessrepo. ServerlessrepoServiceID = "serverlessrepo" // Serverlessrepo.
@ -302,11 +305,22 @@ var awsPartition = partition{
"ca-central-1": endpoint{}, "ca-central-1": endpoint{},
"eu-central-1": endpoint{}, "eu-central-1": endpoint{},
"eu-west-1": endpoint{}, "eu-west-1": endpoint{},
"eu-west-2": endpoint{},
"us-east-1": endpoint{}, "us-east-1": endpoint{},
"us-east-2": endpoint{}, "us-east-2": endpoint{},
"us-west-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{ "api.pricing": service{
Defaults: endpoint{ Defaults: endpoint{
CredentialScope: credentialScope{ CredentialScope: credentialScope{
@ -318,6 +332,19 @@ var awsPartition = partition{
"us-east-1": endpoint{}, "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{ "apigateway": service{
Endpoints: endpoints{ Endpoints: endpoints{
@ -382,10 +409,13 @@ var awsPartition = partition{
Endpoints: endpoints{ Endpoints: endpoints{
"ap-northeast-1": endpoint{}, "ap-northeast-1": endpoint{},
"ap-northeast-2": endpoint{},
"ap-south-1": endpoint{},
"ap-southeast-1": endpoint{}, "ap-southeast-1": endpoint{},
"ap-southeast-2": endpoint{}, "ap-southeast-2": endpoint{},
"eu-central-1": endpoint{}, "eu-central-1": endpoint{},
"eu-west-1": endpoint{}, "eu-west-1": endpoint{},
"eu-west-2": endpoint{},
"us-east-1": endpoint{}, "us-east-1": endpoint{},
"us-east-2": endpoint{}, "us-east-2": endpoint{},
"us-west-2": endpoint{}, "us-west-2": endpoint{},
@ -441,6 +471,7 @@ var awsPartition = partition{
"eu-central-1": endpoint{}, "eu-central-1": endpoint{},
"eu-west-1": endpoint{}, "eu-west-1": endpoint{},
"eu-west-2": endpoint{}, "eu-west-2": endpoint{},
"sa-east-1": endpoint{},
"us-east-1": endpoint{}, "us-east-1": endpoint{},
"us-east-2": endpoint{}, "us-east-2": endpoint{},
"us-west-1": endpoint{}, "us-west-1": endpoint{},
@ -559,6 +590,7 @@ var awsPartition = partition{
"ca-central-1": endpoint{}, "ca-central-1": endpoint{},
"eu-central-1": endpoint{}, "eu-central-1": endpoint{},
"eu-west-1": endpoint{}, "eu-west-1": endpoint{},
"eu-west-2": endpoint{},
"us-east-1": endpoint{}, "us-east-1": endpoint{},
"us-east-2": endpoint{}, "us-east-2": endpoint{},
"us-west-1": endpoint{}, "us-west-1": endpoint{},
@ -774,6 +806,7 @@ var awsPartition = partition{
Protocols: []string{"https"}, Protocols: []string{"https"},
}, },
Endpoints: endpoints{ Endpoints: endpoints{
"ap-southeast-2": endpoint{},
"eu-west-1": endpoint{}, "eu-west-1": endpoint{},
"us-east-1": endpoint{}, "us-east-1": endpoint{},
"us-east-2": endpoint{}, "us-east-2": endpoint{},
@ -1017,6 +1050,12 @@ var awsPartition = partition{
"eu-west-1": endpoint{}, "eu-west-1": endpoint{},
"eu-west-2": endpoint{}, "eu-west-2": endpoint{},
"eu-west-3": 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{}, "sa-east-1": endpoint{},
"us-east-1": endpoint{}, "us-east-1": endpoint{},
"us-east-2": endpoint{}, "us-east-2": endpoint{},
@ -1047,6 +1086,8 @@ var awsPartition = partition{
"elasticfilesystem": service{ "elasticfilesystem": service{
Endpoints: endpoints{ Endpoints: endpoints{
"ap-northeast-1": endpoint{},
"ap-northeast-2": endpoint{},
"ap-southeast-2": endpoint{}, "ap-southeast-2": endpoint{},
"eu-central-1": endpoint{}, "eu-central-1": endpoint{},
"eu-west-1": endpoint{}, "eu-west-1": endpoint{},
@ -1081,7 +1122,7 @@ var awsPartition = partition{
"elasticmapreduce": service{ "elasticmapreduce": service{
Defaults: endpoint{ Defaults: endpoint{
SSLCommonName: "{region}.{service}.{dnsSuffix}", SSLCommonName: "{region}.{service}.{dnsSuffix}",
Protocols: []string{"http", "https"}, Protocols: []string{"https"},
}, },
Endpoints: endpoints{ Endpoints: endpoints{
"ap-northeast-1": endpoint{}, "ap-northeast-1": endpoint{},
@ -1180,10 +1221,16 @@ var awsPartition = partition{
Endpoints: endpoints{ Endpoints: endpoints{
"ap-northeast-1": endpoint{}, "ap-northeast-1": endpoint{},
"ap-northeast-2": endpoint{},
"ap-south-1": endpoint{},
"ap-southeast-1": endpoint{}, "ap-southeast-1": endpoint{},
"ap-southeast-2": endpoint{}, "ap-southeast-2": endpoint{},
"ca-central-1": endpoint{},
"eu-central-1": endpoint{}, "eu-central-1": endpoint{},
"eu-west-1": endpoint{}, "eu-west-1": endpoint{},
"eu-west-2": endpoint{},
"eu-west-3": endpoint{},
"sa-east-1": endpoint{},
"us-east-1": endpoint{}, "us-east-1": endpoint{},
"us-east-2": endpoint{}, "us-east-2": endpoint{},
"us-west-1": endpoint{}, "us-west-1": endpoint{},
@ -1195,6 +1242,7 @@ var awsPartition = partition{
Protocols: []string{"https"}, Protocols: []string{"https"},
}, },
Endpoints: endpoints{ Endpoints: endpoints{
"eu-west-1": endpoint{},
"us-east-1": endpoint{}, "us-east-1": endpoint{},
"us-west-2": endpoint{}, "us-west-2": endpoint{},
}, },
@ -1247,6 +1295,7 @@ var awsPartition = partition{
"ap-south-1": endpoint{}, "ap-south-1": endpoint{},
"ap-southeast-1": endpoint{}, "ap-southeast-1": endpoint{},
"ap-southeast-2": endpoint{}, "ap-southeast-2": endpoint{},
"ca-central-1": endpoint{},
"eu-central-1": endpoint{}, "eu-central-1": endpoint{},
"eu-west-1": endpoint{}, "eu-west-1": endpoint{},
"eu-west-2": endpoint{}, "eu-west-2": endpoint{},
@ -1264,6 +1313,7 @@ var awsPartition = partition{
"ap-northeast-1": endpoint{}, "ap-northeast-1": endpoint{},
"ap-southeast-2": endpoint{}, "ap-southeast-2": endpoint{},
"eu-central-1": endpoint{}, "eu-central-1": endpoint{},
"eu-west-1": endpoint{},
"us-east-1": endpoint{}, "us-east-1": endpoint{},
"us-west-2": endpoint{}, "us-west-2": endpoint{},
}, },
@ -1360,6 +1410,15 @@ var awsPartition = partition{
"us-west-2": endpoint{}, "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{ "kinesis": service{
Endpoints: endpoints{ Endpoints: endpoints{
@ -1383,6 +1442,7 @@ var awsPartition = partition{
"kinesisanalytics": service{ "kinesisanalytics": service{
Endpoints: endpoints{ Endpoints: endpoints{
"eu-central-1": endpoint{},
"eu-west-1": endpoint{}, "eu-west-1": endpoint{},
"us-east-1": endpoint{}, "us-east-1": endpoint{},
"us-west-2": endpoint{}, "us-west-2": endpoint{},
@ -1513,9 +1573,12 @@ var awsPartition = partition{
Endpoints: endpoints{ Endpoints: endpoints{
"ap-northeast-1": endpoint{}, "ap-northeast-1": endpoint{},
"ap-northeast-2": endpoint{}, "ap-northeast-2": endpoint{},
"ap-south-1": endpoint{},
"ap-southeast-1": endpoint{}, "ap-southeast-1": endpoint{},
"ap-southeast-2": endpoint{}, "ap-southeast-2": endpoint{},
"eu-central-1": endpoint{},
"eu-west-1": endpoint{}, "eu-west-1": endpoint{},
"sa-east-1": endpoint{},
"us-east-1": endpoint{}, "us-east-1": endpoint{},
"us-west-2": endpoint{}, "us-west-2": endpoint{},
}, },
@ -1539,6 +1602,7 @@ var awsPartition = partition{
Endpoints: endpoints{ Endpoints: endpoints{
"ap-northeast-1": endpoint{}, "ap-northeast-1": endpoint{},
"ap-northeast-2": endpoint{},
"ap-southeast-2": endpoint{}, "ap-southeast-2": endpoint{},
"eu-central-1": endpoint{}, "eu-central-1": endpoint{},
"eu-west-1": endpoint{}, "eu-west-1": endpoint{},
@ -1626,6 +1690,35 @@ var awsPartition = partition{
"us-east-1": endpoint{}, "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{ "opsworks": service{
Endpoints: endpoints{ Endpoints: endpoints{
@ -1768,6 +1861,7 @@ var awsPartition = partition{
"eu-central-1": endpoint{}, "eu-central-1": endpoint{},
"eu-west-1": endpoint{}, "eu-west-1": endpoint{},
"eu-west-2": endpoint{}, "eu-west-2": endpoint{},
"eu-west-3": endpoint{},
"sa-east-1": endpoint{}, "sa-east-1": endpoint{},
"us-east-1": endpoint{}, "us-east-1": endpoint{},
"us-east-2": endpoint{}, "us-east-2": endpoint{},
@ -1809,6 +1903,10 @@ var awsPartition = partition{
"runtime.sagemaker": service{ "runtime.sagemaker": service{
Endpoints: endpoints{ Endpoints: endpoints{
"ap-northeast-1": endpoint{},
"ap-northeast-2": endpoint{},
"ap-southeast-2": endpoint{},
"eu-central-1": endpoint{},
"eu-west-1": endpoint{}, "eu-west-1": endpoint{},
"us-east-1": endpoint{}, "us-east-1": endpoint{},
"us-east-2": 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{ "sdb": service{
Defaults: endpoint{ Defaults: endpoint{
Protocols: []string{"http", "https"}, Protocols: []string{"http", "https"},
@ -1995,6 +2084,7 @@ var awsPartition = partition{
"eu-west-1": endpoint{}, "eu-west-1": endpoint{},
"us-east-1": endpoint{}, "us-east-1": endpoint{},
"us-east-2": endpoint{}, "us-east-2": endpoint{},
"us-west-1": endpoint{},
"us-west-2": endpoint{}, "us-west-2": endpoint{},
}, },
}, },
@ -2085,10 +2175,30 @@ var awsPartition = partition{
"eu-west-1": endpoint{}, "eu-west-1": endpoint{},
"eu-west-2": endpoint{}, "eu-west-2": endpoint{},
"eu-west-3": endpoint{}, "eu-west-3": endpoint{},
"fips-us-east-1": endpoint{}, "fips-us-east-1": endpoint{
"fips-us-east-2": endpoint{}, Hostname: "sqs-fips.us-east-1.amazonaws.com",
"fips-us-west-1": endpoint{}, CredentialScope: credentialScope{
"fips-us-west-2": endpoint{}, 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{}, "sa-east-1": endpoint{},
"us-east-1": endpoint{ "us-east-1": endpoint{
SSLCommonName: "queue.{dnsSuffix}", SSLCommonName: "queue.{dnsSuffix}",
@ -2123,6 +2233,7 @@ var awsPartition = partition{
Endpoints: endpoints{ Endpoints: endpoints{
"ap-northeast-1": endpoint{}, "ap-northeast-1": endpoint{},
"ap-northeast-2": endpoint{}, "ap-northeast-2": endpoint{},
"ap-south-1": endpoint{},
"ap-southeast-1": endpoint{}, "ap-southeast-1": endpoint{},
"ap-southeast-2": endpoint{}, "ap-southeast-2": endpoint{},
"ca-central-1": endpoint{}, "ca-central-1": endpoint{},
@ -2418,11 +2529,12 @@ var awscnPartition = partition{
Endpoints: endpoints{ Endpoints: endpoints{
"cn-north-1": endpoint{}, "cn-north-1": endpoint{},
"cn-northwest-1": endpoint{},
}, },
}, },
"application-autoscaling": service{ "application-autoscaling": service{
Defaults: endpoint{ Defaults: endpoint{
Hostname: "autoscaling.{region}.amazonaws.com", Hostname: "autoscaling.{region}.amazonaws.com.cn",
Protocols: []string{"http", "https"}, Protocols: []string{"http", "https"},
CredentialScope: credentialScope{ CredentialScope: credentialScope{
Service: "application-autoscaling", Service: "application-autoscaling",
@ -2483,6 +2595,13 @@ var awscnPartition = partition{
"cn-northwest-1": endpoint{}, "cn-northwest-1": endpoint{},
}, },
}, },
"ds": service{
Endpoints: endpoints{
"cn-north-1": endpoint{},
"cn-northwest-1": endpoint{},
},
},
"dynamodb": service{ "dynamodb": service{
Defaults: endpoint{ Defaults: endpoint{
Protocols: []string{"http", "https"}, Protocols: []string{"http", "https"},
@ -2516,12 +2635,14 @@ var awscnPartition = partition{
Endpoints: endpoints{ Endpoints: endpoints{
"cn-north-1": endpoint{}, "cn-north-1": endpoint{},
"cn-northwest-1": endpoint{},
}, },
}, },
"ecs": service{ "ecs": service{
Endpoints: endpoints{ Endpoints: endpoints{
"cn-north-1": endpoint{}, "cn-north-1": endpoint{},
"cn-northwest-1": endpoint{},
}, },
}, },
"elasticache": service{ "elasticache": service{
@ -2549,7 +2670,7 @@ var awscnPartition = partition{
}, },
"elasticmapreduce": service{ "elasticmapreduce": service{
Defaults: endpoint{ Defaults: endpoint{
Protocols: []string{"http", "https"}, Protocols: []string{"https"},
}, },
Endpoints: endpoints{ Endpoints: endpoints{
"cn-north-1": endpoint{}, "cn-north-1": endpoint{},
@ -2612,6 +2733,7 @@ var awscnPartition = partition{
Endpoints: endpoints{ Endpoints: endpoints{
"cn-north-1": endpoint{}, "cn-north-1": endpoint{},
"cn-northwest-1": endpoint{},
}, },
}, },
"logs": service{ "logs": service{
@ -2877,6 +2999,12 @@ var awsusgovPartition = partition{
"elasticache": service{ "elasticache": service{
Endpoints: endpoints{ 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{}, "us-gov-west-1": endpoint{},
}, },
}, },
@ -2898,7 +3026,7 @@ var awsusgovPartition = partition{
Endpoints: endpoints{ Endpoints: endpoints{
"us-gov-west-1": endpoint{ "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{ "kinesis": service{
Endpoints: endpoints{ Endpoints: endpoints{
@ -3051,6 +3195,12 @@ var awsusgovPartition = partition{
"us-gov-west-1": endpoint{}, "us-gov-west-1": endpoint{},
}, },
}, },
"states": service{
Endpoints: endpoints{
"us-gov-west-1": endpoint{},
},
},
"storagegateway": service{ "storagegateway": service{
Endpoints: endpoints{ Endpoints: endpoints{
@ -3091,5 +3241,13 @@ var awsusgovPartition = partition{
"us-gov-west-1": endpoint{}, "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 // LogDebugWithRequestErrors states the SDK should log when service requests fail
// to build, send, validate, or unmarshal. // to build, send, validate, or unmarshal.
LogDebugWithRequestErrors 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 // 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 Send HandlerList
ValidateResponse HandlerList ValidateResponse HandlerList
Unmarshal HandlerList Unmarshal HandlerList
UnmarshalStream HandlerList
UnmarshalMeta HandlerList UnmarshalMeta HandlerList
UnmarshalError HandlerList UnmarshalError HandlerList
Retry HandlerList Retry HandlerList
@ -30,6 +31,7 @@ func (h *Handlers) Copy() Handlers {
Send: h.Send.copy(), Send: h.Send.copy(),
ValidateResponse: h.ValidateResponse.copy(), ValidateResponse: h.ValidateResponse.copy(),
Unmarshal: h.Unmarshal.copy(), Unmarshal: h.Unmarshal.copy(),
UnmarshalStream: h.UnmarshalStream.copy(),
UnmarshalError: h.UnmarshalError.copy(), UnmarshalError: h.UnmarshalError.copy(),
UnmarshalMeta: h.UnmarshalMeta.copy(), UnmarshalMeta: h.UnmarshalMeta.copy(),
Retry: h.Retry.copy(), Retry: h.Retry.copy(),
@ -45,6 +47,7 @@ func (h *Handlers) Clear() {
h.Send.Clear() h.Send.Clear()
h.Sign.Clear() h.Sign.Clear()
h.Unmarshal.Clear() h.Unmarshal.Clear()
h.UnmarshalStream.Clear()
h.UnmarshalMeta.Clear() h.UnmarshalMeta.Clear()
h.UnmarshalError.Clear() h.UnmarshalError.Clear()
h.ValidateResponse.Clear() h.ValidateResponse.Clear()
@ -172,6 +175,21 @@ func (l *HandlerList) SwapNamed(n NamedHandler) (swapped bool) {
return swapped 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. // 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. // If the handler does not exist the handler will be added to the end of the list.
func (l *HandlerList) SetBackNamed(n NamedHandler) { func (l *HandlerList) SetBackNamed(n NamedHandler) {

View file

@ -46,6 +46,7 @@ type Request struct {
Handlers Handlers Handlers Handlers
Retryer Retryer
AttemptTime time.Time
Time time.Time Time time.Time
Operation *Operation Operation *Operation
HTTPRequest *http.Request HTTPRequest *http.Request
@ -121,6 +122,7 @@ func New(cfg aws.Config, clientInfo metadata.ClientInfo, handlers Handlers,
Handlers: handlers.Copy(), Handlers: handlers.Copy(),
Retryer: retryer, Retryer: retryer,
AttemptTime: time.Now(),
Time: time.Now(), Time: time.Now(),
ExpireTime: 0, ExpireTime: 0,
Operation: operation, Operation: operation,
@ -368,9 +370,9 @@ func (r *Request) Build() error {
return r.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. // be executed in the order they were set.
func (r *Request) Sign() error { func (r *Request) Sign() error {
r.Build() r.Build()
@ -440,7 +442,7 @@ func (r *Request) GetBody() io.ReadSeeker {
return r.safeBody 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 // Send will sign the request prior to sending. All Send Handlers will
// be executed in the order they were set. // be executed in the order they were set.
@ -461,6 +463,7 @@ func (r *Request) Send() error {
}() }()
for { for {
r.AttemptTime = time.Now()
if aws.BoolValue(r.Retryable) { if aws.BoolValue(r.Retryable) {
if r.Config.LogLevel.Matches(aws.LogDebugWithRequestRetries) { if r.Config.LogLevel.Matches(aws.LogDebugWithRequestRetries) {
r.Config.Logger.Log(fmt.Sprintf("DEBUG: Retrying Request %s/%s, attempt %d", 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{} var NoBody = noBody{}
// ResetBody rewinds the request body back to its starting position, and // 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. // 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 // ResetBody will automatically be called by the SDK's build handler, but if

View file

@ -11,7 +11,7 @@ import (
var NoBody = http.NoBody var NoBody = http.NoBody
// ResetBody rewinds the request body back to its starting position, and // 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. // 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 // 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 { if t, ok := err.(temporaryError); ok {
return t.Temporary() return t.Temporary() || isErrConnectionReset(err)
} }
return 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 file (~/.aws/credentials) over those in the shared config file (~/.aws/config).
Credentials are the values the SDK should use for authenticating requests with 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 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 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 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 // AWS_CA_BUNDLE=$HOME/my_custom_ca_bundle
CustomCABundle string CustomCABundle string
csmEnabled string
CSMEnabled bool
CSMPort string
CSMClientID string
} }
var ( var (
csmEnabledEnvKey = []string{
"AWS_CSM_ENABLED",
}
csmPortEnvKey = []string{
"AWS_CSM_PORT",
}
csmClientIDEnvKey = []string{
"AWS_CSM_CLIENT_ID",
}
credAccessEnvKey = []string{ credAccessEnvKey = []string{
"AWS_ACCESS_KEY_ID", "AWS_ACCESS_KEY_ID",
"AWS_ACCESS_KEY", "AWS_ACCESS_KEY",
@ -157,6 +171,12 @@ func envConfigLoad(enableSharedConfig bool) envConfig {
setFromEnvVal(&cfg.Creds.SecretAccessKey, credSecretEnvKey) setFromEnvVal(&cfg.Creds.SecretAccessKey, credSecretEnvKey)
setFromEnvVal(&cfg.Creds.SessionToken, credSessionEnvKey) 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 // Require logical grouping of credentials
if len(cfg.Creds.AccessKeyID) == 0 || len(cfg.Creds.SecretAccessKey) == 0 { if len(cfg.Creds.AccessKeyID) == 0 || len(cfg.Creds.SecretAccessKey) == 0 {
cfg.Creds = credentials.Value{} 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/corehandlers"
"github.com/aws/aws-sdk-go/aws/credentials" "github.com/aws/aws-sdk-go/aws/credentials"
"github.com/aws/aws-sdk-go/aws/credentials/stscreds" "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/defaults"
"github.com/aws/aws-sdk-go/aws/endpoints" "github.com/aws/aws-sdk-go/aws/endpoints"
"github.com/aws/aws-sdk-go/aws/request" "github.com/aws/aws-sdk-go/aws/request"
@ -81,10 +82,16 @@ func New(cfgs ...*aws.Config) *Session {
r.Error = err r.Error = err
}) })
} }
return s 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, // NewSession returns a new Session created from SDK defaults, config files,
@ -300,10 +307,22 @@ func deprecatedNewSession(cfgs ...*aws.Config) *Session {
} }
initHandlers(s) initHandlers(s)
return 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) { func newSession(opts Options, envCfg envConfig, cfgs ...*aws.Config) (*Session, error) {
cfg := defaults.Config() cfg := defaults.Config()
handlers := defaults.Handlers() handlers := defaults.Handlers()
@ -343,6 +362,9 @@ func newSession(opts Options, envCfg envConfig, cfgs ...*aws.Config) (*Session,
} }
initHandlers(s) initHandlers(s)
if envCfg.CSMEnabled {
enableCSM(&s.Handlers, envCfg.CSMClientID, envCfg.CSMPort, s.Config.Logger)
}
// Setup HTTP client with custom cert bundle if enabled // Setup HTTP client with custom cert bundle if enabled
if opts.CustomCABundle != nil { if opts.CustomCABundle != nil {

View file

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

View file

@ -5,4 +5,4 @@ package aws
const SDKName = "aws-sdk-go" const SDKName = "aws-sdk-go"
// SDKVersion is the version of this SDK // 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: default:
switch converted := value.Interface().(type) { switch converted := value.Interface().(type) {
case time.Time: 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: case []byte:
if !value.IsNil() { if !value.IsNil() {
buf.WriteByte('"') 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 { 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) { switch d := data.(type) {
case nil: case nil:
@ -189,6 +186,17 @@ func unmarshalScalar(value reflect.Value, data interface{}, tag reflect.StructTa
return err return err
} }
value.Set(reflect.ValueOf(b)) 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: case aws.JSONValue:
// No need to use escaping as the value is a non-quoted string. // No need to use escaping as the value is a non-quoted string.
v, err := protocol.DecodeJSONValue(d, protocol.NoEscape) 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)) value.Set(reflect.ValueOf(v))
default: default:
return errf() return fmt.Errorf("unsupported value: %v (%s)", value.Interface(), value.Type())
} }
case float64: case float64:
switch value.Interface().(type) { switch value.Interface().(type) {
@ -207,17 +215,18 @@ func unmarshalScalar(value reflect.Value, data interface{}, tag reflect.StructTa
case *float64: case *float64:
value.Set(reflect.ValueOf(&d)) value.Set(reflect.ValueOf(&d))
case *time.Time: case *time.Time:
// Time unmarshaled from a float64 can only be epoch seconds
t := time.Unix(int64(d), 0).UTC() t := time.Unix(int64(d), 0).UTC()
value.Set(reflect.ValueOf(&t)) value.Set(reflect.ValueOf(&t))
default: default:
return errf() return fmt.Errorf("unsupported value: %v (%s)", value.Interface(), value.Type())
} }
case bool: case bool:
switch value.Interface().(type) { switch value.Interface().(type) {
case *bool: case *bool:
value.Set(reflect.ValueOf(&d)) value.Set(reflect.ValueOf(&d))
default: default:
return errf() return fmt.Errorf("unsupported value: %v (%s)", value.Interface(), value.Type())
} }
default: default:
return fmt.Errorf("unsupported JSON value (%v)", data) 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)) v.Set(name, strconv.FormatFloat(float64(value), 'f', -1, 32))
case time.Time: case time.Time:
const ISO8601UTC = "2006-01-02T15:04:05Z" 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: default:
return fmt.Errorf("unsupported value for param %s: %v (%s)", name, r.Interface(), r.Type().Name()) 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" "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 // Whether the byte value can be sent without escaping in AWS URLs
var noEscape [256]bool var noEscape [256]bool
@ -272,7 +267,14 @@ func convertType(v reflect.Value, tag reflect.StructTag) (str string, err error)
case float64: case float64:
str = strconv.FormatFloat(value, 'f', -1, 64) str = strconv.FormatFloat(value, 'f', -1, 64)
case time.Time: 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: case aws.JSONValue:
if len(value) == 0 { if len(value) == 0 {
return "", errValueNotSet return "", errValueNotSet

View file

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

View file

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

View file

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

View file

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

View file

@ -29,8 +29,9 @@ var initRequest func(*request.Request)
// Service information constants // Service information constants
const ( const (
ServiceName = "lightsail" // Service endpoint prefix API calls made to. ServiceName = "lightsail" // Name of service.
EndpointsID = ServiceName // Service ID for Regions and Endpoints metadata. 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. // 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, cfg,
metadata.ClientInfo{ metadata.ClientInfo{
ServiceName: ServiceName, ServiceName: ServiceName,
ServiceID: ServiceID,
SigningName: signingName, SigningName: signingName,
SigningRegion: signingRegion, SigningRegion: signingRegion,
Endpoint: endpoint, Endpoint: endpoint,

View file

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

View file

@ -29,8 +29,9 @@ var initRequest func(*request.Request)
// Service information constants // Service information constants
const ( const (
ServiceName = "route53" // Service endpoint prefix API calls made to. ServiceName = "route53" // Name of service.
EndpointsID = ServiceName // Service ID for Regions and Endpoints metadata. 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. // 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, cfg,
metadata.ClientInfo{ metadata.ClientInfo{
ServiceName: ServiceName, ServiceName: ServiceName,
ServiceID: ServiceID,
SigningName: signingName, SigningName: signingName,
SigningRegion: signingRegion, SigningRegion: signingRegion,
Endpoint: endpoint, Endpoint: endpoint,

View file

@ -1908,7 +1908,7 @@ type Credentials struct {
// The date on which the current credentials expire. // The date on which the current credentials expire.
// //
// Expiration is a required field // 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. // 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 // Service information constants
const ( const (
ServiceName = "sts" // Service endpoint prefix API calls made to. ServiceName = "sts" // Name of service.
EndpointsID = ServiceName // Service ID for Regions and Endpoints metadata. 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. // 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, cfg,
metadata.ClientInfo{ metadata.ClientInfo{
ServiceName: ServiceName, ServiceName: ServiceName,
ServiceID: ServiceID,
SigningName: signingName, SigningName: signingName,
SigningRegion: signingRegion, SigningRegion: signingRegion,
Endpoint: endpoint, Endpoint: endpoint,

View file

@ -2,7 +2,7 @@ ISC License
Copyright (c) 2012-2016 Dave Collins <dave@davec.name> 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 purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies. 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 // 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" // "-tags safe" is not added to the go build command line. The "disableunsafe"
// tag is deprecated and thus should not be used. // 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 package spew
@ -34,80 +36,49 @@ const (
ptrSize = unsafe.Sizeof((*byte)(nil)) ptrSize = unsafe.Sizeof((*byte)(nil))
) )
var ( type flag uintptr
// 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)
// flagKindWidth and flagKindShift indicate various bits that the var (
// reflect package uses internally to track kind information. // flagRO indicates whether the value field of a reflect.Value
// // is read-only.
// flagRO indicates whether or not the value field of a reflect.Value is flagRO flag
// read-only.
// // flagAddr indicates whether the address of the reflect.Value's
// flagIndir indicates whether the value field of a reflect.Value is // value may be taken.
// the actual data or a pointer to the data. flagAddr flag
//
// 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)
) )
func init() { // flagKindMask holds the bits that make up the kind
// Older versions of reflect.Value stored small integers directly in the // part of the flags field. In all the supported versions,
// ptr field (which is named val in the older versions). Versions // it is in the lower 5 bits.
// between commits ecccf07e7f9d and 82f48826c6c7 added a new field named const flagKindMask = flag(0x1f)
// 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
}
// Commit 90a7c3c86944 changed the flag positions such that the low // Different versions of Go have used different
// order bits are the kind. This code extracts the kind from the flags // bit layouts for the flags type. This table
// field and ensures it's the correct type. When it's not, the flag // records the known combinations.
// order has been changed to the newer format, so the flags are updated var okFlags = []struct {
// accordingly. ro, addr flag
upf := unsafe.Pointer(uintptr(unsafe.Pointer(&vv)) + offsetFlag) }{{
upfv := *(*uintptr)(upf) // From Go 1.4 to 1.5
flagKindMask := uintptr((1<<flagKindWidth - 1) << flagKindShift) ro: 1 << 5,
if (upfv&flagKindMask)>>flagKindShift != uintptr(reflect.Int) { addr: 1 << 7,
flagKindShift = 0 }, {
flagRO = 1 << 5 // Up to Go tip.
flagIndir = 1 << 6 ro: 1<<5 | 1<<6,
addr: 1 << 8,
}}
// Commit adf9b30e5594 modified the flags to separate the var flagValOffset = func() uintptr {
// flagRO flag into two bits which specifies whether or not the field, ok := reflect.TypeOf(reflect.Value{}).FieldByName("flag")
// field is embedded. This causes flagIndir to move over a bit if !ok {
// and means that flagRO is the combination of either of the panic("reflect.Value has no flag field")
// 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
}
} }
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 // 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 // This allows us to check for implementations of the Stringer and error
// interfaces to be used for pretty printing ordinarily unaddressable and // interfaces to be used for pretty printing ordinarily unaddressable and
// inaccessible values such as unexported struct fields. // inaccessible values such as unexported struct fields.
func unsafeReflectValue(v reflect.Value) (rv reflect.Value) { func unsafeReflectValue(v reflect.Value) reflect.Value {
indirects := 1 if !v.IsValid() || (v.CanInterface() && v.CanAddr()) {
vt := v.Type() return v
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)
} }
flagFieldPtr := flagField(&v)
*flagFieldPtr &^= flagRO
*flagFieldPtr |= flagAddr
return v
} }
pv := reflect.NewAt(vt, upv) // Sanity checks against future reflect package changes
rv = pv // to the type or semantics of the Value.flag field.
for i := 0; i < indirects; i++ { func init() {
rv = rv.Elem() field, ok := reflect.TypeOf(reflect.Value{}).FieldByName("flag")
if !ok {
panic("reflect.Value has no flag field")
} }
return rv 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 // 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" // "-tags safe" is added to the go build command line. The "disableunsafe"
// tag is deprecated and thus should not be used. // tag is deprecated and thus should not be used.
// +build js appengine safe disableunsafe // +build js appengine safe disableunsafe !go1.4
package spew package spew

View file

@ -180,7 +180,7 @@ func printComplex(w io.Writer, c complex128, floatPrecision int) {
w.Write(closeParenBytes) 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. // prefix to Writer w.
func printHexPtr(w io.Writer, p uintptr) { func printHexPtr(w io.Writer, p uintptr) {
// Null pointer. // Null pointer.

View file

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

View file

@ -182,10 +182,10 @@ func (f *formatState) formatPtr(v reflect.Value) {
// Display dereferenced value. // Display dereferenced value.
switch { switch {
case nilFound == true: case nilFound:
f.fs.Write(nilAngleBytes) f.fs.Write(nilAngleBytes)
case cycleFound == true: case cycleFound:
f.fs.Write(circularShortBytes) f.fs.Write(circularShortBytes)
default: 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. // the response body is decoded into v.
if obj != nil { if obj != nil {
if w, ok := obj.(io.Writer); ok { if w, ok := obj.(io.Writer); ok {
io.Copy(w, resp.Body) _, err = io.Copy(w, resp.Body)
} else { } else {
err = json.NewDecoder(resp.Body).Decode(obj) err = json.NewDecoder(resp.Body).Decode(obj)
} }

View file

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

View file

@ -1,25 +1,161 @@
package egoscale package egoscale
func (*ListAccounts) name() string { import "fmt"
return "listAccounts"
// 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) return new(ListAccountsResponse)
} }
func (*EnableAccount) name() string { // SetPage sets the current page
return "enableAccount" func (ls *ListAccounts) SetPage(page int) {
ls.Page = page
} }
func (*EnableAccount) response() interface{} { // SetPageSize sets the page size
return new(EnableAccountResponse) func (ls *ListAccounts) SetPageSize(pageSize int) {
ls.PageSize = pageSize
} }
func (*DisableAccount) name() string { func (ListAccounts) each(resp interface{}, callback IterateItemFunc) {
return "disableAccount" vms, ok := resp.(*ListAccountsResponse)
if !ok {
callback(nil, fmt.Errorf("wrong type. ListAccountsResponse expected, got %T", resp))
return
} }
func (*DisableAccount) asyncResponse() interface{} { for i := range vms.Account {
return new(DisableAccountResponse) if !callback(&vms.Account[i], nil) {
break
}
}
}
// 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 ( import (
"context" "context"
"fmt" "fmt"
"net"
"github.com/jinzhu/copier"
) )
// Get fetches the resource // IPAddress represents an IP Address
func (ipaddress *IPAddress) Get(ctx context.Context, client *Client) error { type IPAddress struct {
if ipaddress.ID == "" && ipaddress.IPAddress == nil { Account string `json:"account,omitempty" doc:"the account the public IP address is associated with"`
return fmt.Errorf("An IPAddress may only be searched using ID or IPAddress") 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{ req := &ListPublicIPAddresses{
Account: ipaddress.Account,
AssociatedNetworkID: ipaddress.AssociatedNetworkID,
DomainID: ipaddress.DomainID,
ID: ipaddress.ID, ID: ipaddress.ID,
IPAddress: ipaddress.IPAddress, IPAddress: ipaddress.IPAddress,
Account: ipaddress.Account, PhysicalNetworkID: ipaddress.PhysicalNetworkID,
DomainID: ipaddress.DomainID, VlanID: ipaddress.VlanID,
ZoneID: ipaddress.ZoneID, ZoneID: ipaddress.ZoneID,
} }
if ipaddress.IsElastic { 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) return req, nil
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])
} }
// Delete removes the resource // Delete removes the resource
func (ipaddress *IPAddress) Delete(ctx context.Context, client *Client) error { func (ipaddress IPAddress) Delete(ctx context.Context, client *Client) error {
if ipaddress.ID == "" { if ipaddress.ID == nil {
return fmt.Errorf("An IPAddress may only be deleted using ID") return fmt.Errorf("an IPAddress may only be deleted using ID")
} }
return client.BooleanRequestWithContext(ctx, &DisassociateIPAddress{ 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 // AssociateIPAddress (Async) represents the IP creation
func (*IPAddress) ResourceType() string { type AssociateIPAddress struct {
return "PublicIpAddress" 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) response() interface{} {
func (*AssociateIPAddress) name() string { return new(AsyncJobResult)
return "associateIpAddress"
} }
func (*AssociateIPAddress) asyncResponse() interface{} { func (AssociateIPAddress) asyncResponse() interface{} {
return new(AssociateIPAddressResponse) return new(IPAddress)
} }
// name returns the CloudStack API command name // DisassociateIPAddress (Async) represents the IP deletion
func (*DisassociateIPAddress) name() string { type DisassociateIPAddress struct {
return "disassociateIpAddress" 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) return new(booleanResponse)
} }
// name returns the CloudStack API command name // UpdateIPAddress (Async) represents the IP modification
func (*UpdateIPAddress) name() string { type UpdateIPAddress struct {
return "updateIpAddress" 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"`
func (*UpdateIPAddress) asyncResponse() interface{} { ForDisplay *bool `json:"fordisplay,omitempty" doc:"an optional field, whether to the display the ip to the end user or not"`
return new(UpdateIPAddressResponse) _ bool `name:"updateIpAddress" description:"Updates an ip address"`
} }
// name returns the CloudStack API command name func (UpdateIPAddress) response() interface{} {
func (*ListPublicIPAddresses) name() string { return new(AsyncJobResult)
return "listPublicIpAddresses"
} }
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) 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 // SetPage sets the current page
func (ls *ListPublicIPAddresses) SetPage(page int) { func (ls *ListPublicIPAddresses) SetPage(page int) {
ls.Page = page ls.Page = page
@ -124,10 +180,10 @@ func (ls *ListPublicIPAddresses) SetPageSize(pageSize int) {
ls.PageSize = pageSize ls.PageSize = pageSize
} }
func (*ListPublicIPAddresses) each(resp interface{}, callback IterateItemFunc) { func (ListPublicIPAddresses) each(resp interface{}, callback IterateItemFunc) {
ips, ok := resp.(*ListPublicIPAddressesResponse) ips, ok := resp.(*ListPublicIPAddressesResponse)
if !ok { if !ok {
callback(nil, fmt.Errorf("ListPublicIPAddressesResponse expected, got %t", resp)) callback(nil, fmt.Errorf("wrong type. ListPublicIPAddressesResponse expected, got %T", resp))
return 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" "context"
"fmt" "fmt"
"net/url" "net/url"
"github.com/jinzhu/copier"
) )
// Get loads the given Affinity Group // AffinityGroup represents an (anti-)affinity group
func (ag *AffinityGroup) Get(ctx context.Context, client *Client) error { //
if ag.ID == "" && ag.Name == "" { // Affinity and Anti-Affinity groups provide a way to influence where VMs should run.
return fmt.Errorf("An Affinity Group may only be searched using ID or Name") // 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, ID: ag.ID,
Name: ag.Name, Name: ag.Name,
}) }, nil
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])
} }
// Delete removes the given Affinity Group // Delete removes the given Affinity Group
func (ag *AffinityGroup) Delete(ctx context.Context, client *Client) error { func (ag AffinityGroup) Delete(ctx context.Context, client *Client) error {
if ag.ID == "" && ag.Name == "" { if ag.ID == nil && ag.Name == "" {
return fmt.Errorf("An Affinity Group may only be deleted using ID or Name") return fmt.Errorf("an Affinity Group may only be deleted using ID or Name")
} }
req := &DeleteAffinityGroup{ req := &DeleteAffinityGroup{
@ -48,7 +40,7 @@ func (ag *AffinityGroup) Delete(ctx context.Context, client *Client) error {
DomainID: ag.DomainID, DomainID: ag.DomainID,
} }
if ag.ID != "" { if ag.ID != nil {
req.ID = ag.ID req.ID = ag.ID
} else { } else {
req.Name = ag.Name req.Name = ag.Name
@ -57,25 +49,38 @@ func (ag *AffinityGroup) Delete(ctx context.Context, client *Client) error {
return client.BooleanRequestWithContext(ctx, req) return client.BooleanRequestWithContext(ctx, req)
} }
// name returns the CloudStack API command name // AffinityGroupType represent an affinity group type
func (*CreateAffinityGroup) name() string { type AffinityGroupType struct {
return "createAffinityGroup" Type string `json:"type,omitempty" doc:"the type of the affinity group"`
} }
func (*CreateAffinityGroup) asyncResponse() interface{} { // CreateAffinityGroup (Async) represents a new (anti-)affinity group
return new(CreateAffinityGroupResponse) 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 (CreateAffinityGroup) response() interface{} {
func (*UpdateVMAffinityGroup) name() string { return new(AsyncJobResult)
return "updateVMAffinityGroup"
} }
func (*UpdateVMAffinityGroup) asyncResponse() interface{} { func (CreateAffinityGroup) asyncResponse() interface{} {
return new(UpdateVMAffinityGroupResponse) 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 // Either AffinityGroupIDs or AffinityGroupNames must be set
if len(req.AffinityGroupIDs) == 0 && len(req.AffinityGroupNames) == 0 { if len(req.AffinityGroupIDs) == 0 && len(req.AffinityGroupNames) == 0 {
params.Set("affinitygroupids", "") params.Set("affinitygroupids", "")
@ -83,29 +88,95 @@ func (req *UpdateVMAffinityGroup) onBeforeSend(params *url.Values) error {
return nil return nil
} }
// name returns the CloudStack API command name func (UpdateVMAffinityGroup) response() interface{} {
func (*DeleteAffinityGroup) name() string { return new(AsyncJobResult)
return "deleteAffinityGroup"
} }
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) return new(booleanResponse)
} }
// name returns the CloudStack API command name // ListAffinityGroups represents an (anti-)affinity groups search
func (*ListAffinityGroups) name() string { type ListAffinityGroups struct {
return "listAffinityGroups" 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) return new(ListAffinityGroupsResponse)
} }
// name returns the CloudStack API command name // SetPage sets the current page
func (*ListAffinityGroupTypes) name() string { func (ls *ListAffinityGroups) SetPage(page int) {
return "listAffinityGroupTypes" 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) 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 package egoscale
func (*ListAPIs) name() string { // API represents an API service
return "listApis" 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{} { 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 package egoscale
import "encoding/json" import (
"encoding/json"
"errors"
)
// QueryAsyncJobResult represents a query to fetch the status of async job // AsyncJobResult represents an asynchronous job result
// type AsyncJobResult struct {
// CloudStack API: https://cloudstack.apache.org/api/apidocs-4.10/apis/queryAsyncJobResult.html AccountID *UUID `json:"accountid,omitempty" doc:"the account that executed the async command"`
type QueryAsyncJobResult struct { Cmd string `json:"cmd,omitempty" doc:"the async command executed"`
JobID string `json:"jobid" doc:"the ID of the asychronous job"` 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 (a AsyncJobResult) Error() error {
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 {
r := new(ErrorResponse) r := new(ErrorResponse)
if e := json.Unmarshal(*a.JobResult, r); e != nil { if e := json.Unmarshal(*a.JobResult, r); e != nil {
return e return e
} }
return r 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 ( import (
"context" "context"
"crypto/tls"
"fmt" "fmt"
"io/ioutil"
"log"
"net/http" "net/http"
"net/http/httputil"
"os"
"reflect"
"strings"
"time" "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 // Get populates the given resource or fails
func (client *Client) Get(g Gettable) error { func (client *Client) Get(g Gettable) error {
ctx, cancel := context.WithTimeout(context.Background(), client.Timeout) 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 // GetWithContext populates the given resource or fails
func (client *Client) GetWithContext(ctx context.Context, g Gettable) error { 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 // 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) { func (client *Client) ListWithContext(ctx context.Context, g Listable) ([]interface{}, error) {
s := make([]interface{}, 0) 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() req, err := g.ListRequest()
if err != nil { if err != nil {
return s, err 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 // APIName returns the CloudStack name of the given command
func (client *Client) APIName(request Command) string { func (client *Client) APIName(command Command) string {
return request.name() // 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 // Response returns the response structure of the given command
func (client *Client) Response(request Command) interface{} { func (client *Client) Response(command Command) interface{} {
switch request.(type) { switch command.(type) {
case syncCommand:
return (request.(syncCommand)).response()
case AsyncCommand: case AsyncCommand:
return (request.(AsyncCommand)).asyncResponse() return (command.(AsyncCommand)).asyncResponse()
default: 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 // TraceOn activates the HTTP tracer
// func (client *Client) TraceOn() {
// Timeout is set to both the HTTP client and the client itself. if _, ok := client.HTTPClient.Transport.(*traceTransport); !ok {
func NewClientWithTimeout(endpoint, apiKey, apiSecret string, timeout time.Duration) *Client { client.HTTPClient.Transport = &traceTransport{
client := &http.Client{ transport: client.HTTPClient.Transport,
Timeout: timeout, logger: client.Logger,
Transport: &http.Transport{ }
Proxy: http.ProxyFromEnvironment, }
TLSClientConfig: &tls.Config{
InsecureSkipVerify: false,
},
},
} }
cs := &Client{ // TraceOff deactivates the HTTP tracer
HTTPClient: client, func (client *Client) TraceOff() {
Endpoint: endpoint, if rt, ok := client.HTTPClient.Transport.(*traceTransport); ok {
APIKey: apiKey, client.HTTPClient.Transport = rt.transport
apiSecret: apiSecret, }
PageSize: 50,
Timeout: timeout,
RetryStrategy: MonotonicRetryStrategyFunc(2),
} }
return cs // traceTransport contains the original HTTP transport to enable it to be reverted
type traceTransport struct {
transport http.RoundTripper
logger *log.Logger
} }
// NewClient creates a CloudStack API client with default timeout (60) // RoundTrip executes a single HTTP transaction
func NewClient(endpoint, apiKey, apiSecret string) *Client { func (t *traceTransport) RoundTrip(req *http.Request) (*http.Response, error) {
timeout := 60 * time.Second if dump, err := httputil.DumpRequest(req, true); err == nil {
return NewClientWithTimeout(endpoint, apiKey, apiSecret, timeout) 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 // 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" import "strconv"
const _CSErrorCode_name = "CloudRuntimeExceptionExecutionExceptionHypervisorVersionChangedExceptionCloudExceptionAccountLimitExceptionAgentUnavailableExceptionCloudAuthenticationExceptionConcurrentOperationExceptionConflictingNetworkSettingsExceptionDiscoveredWithErrorExceptionHAStateExceptionInsufficientAddressCapacityExceptionInsufficientCapacityExceptionInsufficientNetworkCapacityExceptionInsufficientServerCapacityExceptionInsufficientStorageCapacityExceptionInternalErrorExceptionInvalidParameterValueExceptionManagementServerExceptionNetworkRuleConflictExceptionPermissionDeniedExceptionResourceAllocationExceptionResourceInUseExceptionResourceUnavailableExceptionStorageUnavailableExceptionUnsupportedServiceExceptionVirtualMachineMigrationExceptionAsyncCommandQueuedRequestLimitExceptionServerApiException" const _CSErrorCode_name = "CloudRuntimeExceptionExecutionExceptionHypervisorVersionChangedExceptionCloudExceptionAccountLimitExceptionAgentUnavailableExceptionCloudAuthenticationExceptionConcurrentOperationExceptionConflictingNetworkSettingsExceptionDiscoveredWithErrorExceptionHAStateExceptionInsufficientAddressCapacityExceptionInsufficientCapacityExceptionInsufficientNetworkCapacityExceptionInsufficientServerCapacityExceptionInsufficientStorageCapacityExceptionInternalErrorExceptionInvalidParameterValueExceptionManagementServerExceptionNetworkRuleConflictExceptionPermissionDeniedExceptionResourceAllocationExceptionResourceInUseExceptionResourceUnavailableExceptionStorageUnavailableExceptionUnsupportedServiceExceptionVirtualMachineMigrationExceptionAsyncCommandQueuedRequestLimitExceptionServerAPIException"
var _CSErrorCode_map = map[CSErrorCode]string{ var _CSErrorCode_map = map[CSErrorCode]string{
4250: _CSErrorCode_name[0:21], 4250: _CSErrorCode_name[0:21],

View file

@ -5,6 +5,7 @@ import (
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"net/http" "net/http"
"net/url"
"strconv" "strconv"
"strings" "strings"
) )
@ -53,27 +54,81 @@ type DNSRecordResponse struct {
Record DNSRecord `json:"record"` 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 // DNSErrorResponse represents an error in the API
type DNSErrorResponse struct { type DNSErrorResponse struct {
Message string `json:"message,omitempty"` Message string `json:"message,omitempty"`
Errors *DNSError `json:"errors"` Errors map[string][]string `json:"errors"`
} }
// DNSError represents an error // Record represent record type
type DNSError struct { type Record int
Name []string `json:"name"`
} //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 // Error formats the DNSerror into a string
func (req *DNSErrorResponse) Error() error { func (req *DNSErrorResponse) Error() string {
if req.Errors != nil { if len(req.Errors) > 0 {
return fmt.Errorf("DNS error: %s", strings.Join(req.Errors.Name, ", ")) 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 // 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{ m, err := json.Marshal(DNSDomainResponse{
Domain: &DNSDomain{ Domain: &DNSDomain{
Name: name, Name: name,
@ -83,7 +138,7 @@ func (exo *Client) CreateDomain(name string) (*DNSDomain, error) {
return nil, err 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 { if err != nil {
return nil, err return nil, err
} }
@ -97,8 +152,8 @@ func (exo *Client) CreateDomain(name string) (*DNSDomain, error) {
} }
// GetDomain gets a DNS domain // GetDomain gets a DNS domain
func (exo *Client) GetDomain(name string) (*DNSDomain, error) { func (client *Client) GetDomain(name string) (*DNSDomain, error) {
resp, err := exo.dnsRequest("/v1/domains/"+name, "", "GET") resp, err := client.dnsRequest("/v1/domains/"+name, nil, "", "GET")
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -111,20 +166,35 @@ func (exo *Client) GetDomain(name string) (*DNSDomain, error) {
return d.Domain, nil return d.Domain, nil
} }
// DeleteDomain delets a DNS domain // GetDomains gets DNS domains
func (exo *Client) DeleteDomain(name string) error { func (client *Client) GetDomains() ([]DNSDomain, error) {
_, err := exo.dnsRequest("/v1/domains/"+name, "", "DELETE") resp, err := client.dnsRequest("/v1/domains", nil, "", "GET")
if err != nil { if err != nil {
return nil, err
}
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 return err
} }
return nil
}
// GetRecord returns a DNS record // 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) 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 { if err != nil {
return nil, err return nil, err
} }
@ -138,8 +208,37 @@ func (exo *Client) GetRecord(domain string, recordID int64) (*DNSRecord, error)
} }
// GetRecords returns the DNS records // GetRecords returns the DNS records
func (exo *Client) GetRecords(name string) ([]DNSRecord, error) { func (client *Client) GetRecords(domain string) ([]DNSRecord, error) {
resp, err := exo.dnsRequest("/v1/domains/"+name+"/records", "", "GET") 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 { if err != nil {
return nil, err return nil, err
} }
@ -158,7 +257,7 @@ func (exo *Client) GetRecords(name string) ([]DNSRecord, error) {
} }
// CreateRecord creates a DNS record // 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{ body, err := json.Marshal(DNSRecordResponse{
Record: rec, Record: rec,
}) })
@ -166,7 +265,7 @@ func (exo *Client) CreateRecord(name string, rec DNSRecord) (*DNSRecord, error)
return nil, err 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 { if err != nil {
return nil, err return nil, err
} }
@ -180,8 +279,8 @@ func (exo *Client) CreateRecord(name string, rec DNSRecord) (*DNSRecord, error)
} }
// UpdateRecord updates a DNS record // UpdateRecord updates a DNS record
func (exo *Client) UpdateRecord(name string, rec DNSRecord) (*DNSRecord, error) { func (client *Client) UpdateRecord(name string, rec UpdateDNSRecord) (*DNSRecord, error) {
body, err := json.Marshal(DNSRecordResponse{ body, err := json.Marshal(UpdateDNSRecordResponse{
Record: rec, Record: rec,
}) })
if err != nil { if err != nil {
@ -189,7 +288,7 @@ func (exo *Client) UpdateRecord(name string, rec DNSRecord) (*DNSRecord, error)
} }
id := strconv.FormatInt(rec.ID, 10) 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 { if err != nil {
return nil, err return nil, err
} }
@ -203,22 +302,35 @@ func (exo *Client) UpdateRecord(name string, rec DNSRecord) (*DNSRecord, error)
} }
// DeleteRecord deletes a record // 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) 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 return err
} }
func (exo *Client) dnsRequest(uri string, params string, method string) (json.RawMessage, error) { func (client *Client) dnsRequest(uri string, urlValues url.Values, params, method string) (json.RawMessage, error) {
url := exo.Endpoint + uri rawURL := client.Endpoint + uri
req, err := http.NewRequest(method, url, strings.NewReader(params)) 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 { if err != nil {
return nil, err return nil, err
} }
var hdr = make(http.Header) 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("User-Agent", fmt.Sprintf("exoscale/egoscale (%v)", Version))
hdr.Add("Accept", "application/json") hdr.Add("Accept", "application/json")
if params != "" { if params != "" {
@ -226,23 +338,28 @@ func (exo *Client) dnsRequest(uri string, params string, method string) (json.Ra
} }
req.Header = hdr 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 { if err != nil {
return nil, err return nil, err
} }
defer response.Body.Close() // nolint: errcheck if resp.StatusCode >= 400 {
b, err := ioutil.ReadAll(response.Body) e := new(DNSErrorResponse)
if err != nil { if err := json.Unmarshal(b, e); err != nil {
return nil, err return nil, err
} }
return nil, e
if response.StatusCode >= 400 {
var e DNSErrorResponse
if err := json.Unmarshal(b, &e); err != nil {
return nil, err
}
return nil, e.Error()
} }
return b, nil 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 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 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) panic(err)
} }
vm := resp.(*egoscale.DeployVirtualMachineResponse).VirtualMachine vm := resp.(*egoscale.VirtualMachine)
fmt.Printf("Virtual Machine ID: %s\n", vm.ID) 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{ req := &egoscale.DeployVirtualMachine{
Size: 10, Size: 10,
@ -32,7 +32,7 @@ This exemple deploys a virtual machine while controlling the job status as it go
TemplateID: "...", TemplateID: "...",
ZoneID: "...", ZoneID: "...",
} }
resp := &egoscale.DeployVirtualMachineResponse{} vm := &egoscale.VirtualMachine{}
fmt.Println("Deployment started") fmt.Println("Deployment started")
cs.AsyncRequest(req, func(jobResult *egoscale.AsyncJobResult, err error) bool { 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 // Unmarshal the response into the response struct
if err := jobResult.Response(resp); err != nil { if err := jobResult.Response(vm); err != nil {
// JSON unmarshaling error // JSON unmarshaling error
panic(err) panic(err)
} }
@ -57,7 +57,19 @@ This exemple deploys a virtual machine while controlling the job status as it go
return false 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 APIs
@ -86,7 +98,7 @@ Security Groups provide a way to isolate traffic to VMs. Rules are added via the
Name: "Load balancer", Name: "Load balancer",
Description: "Opens HTTP/HTTPS ports from the outside world", Description: "Opens HTTP/HTTPS ports from the outside world",
}) })
securityGroup := resp.(*egoscale.CreateSecurityGroupResponse).SecurityGroup securityGroup := resp.(*egoscale.SecurityGroup)
resp, err = cs.Request(&egoscale.AuthorizeSecurityGroupIngress{ resp, err = cs.Request(&egoscale.AuthorizeSecurityGroupIngress{
Description: "SSH traffic", Description: "SSH traffic",
@ -97,7 +109,7 @@ Security Groups provide a way to isolate traffic to VMs. Rules are added via the
EndPort: 22, EndPort: 22,
}) })
// The modified SecurityGroup is returned // The modified SecurityGroup is returned
securityGroup := resp.(*egoscale.AuthorizeSecurityGroupResponse).SecurityGroup securityGroup := resp.(*egoscale.SecurityGroup)
// ... // ...
err = client.BooleanRequest(&egoscale.DeleteSecurityGroup{ 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"` Created string `json:"created,omitempty" doc:"the date the event was created"`
Description string `json:"description,omitempty" doc:"a brief description of the event"` 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"` 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"` DomainID *UUID `json:"domainid,omitempty" doc:"the id of the account's domain"`
ID string `json:"id,omitempty" doc:"the ID of the event"` ID *UUID `json:"id,omitempty" doc:"the ID of the event"`
Level string `json:"level,omitempty" doc:"the event level (INFO, WARN, ERROR)"` 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"` 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)"` 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)"` 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 // ListEvents list the events
//
// CloudStack API: http://cloudstack.apache.org/api/apidocs-4.10/apis/listEvents.html
type ListEvents struct { type ListEvents struct {
Account string `json:"account,omitempty" doc:"list resources by account. Must be used with the domainId parameter."` 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"` 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\")"` 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"` 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."` 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"` Keyword string `json:"keyword,omitempty" doc:"List by keyword"`
Level string `json:"level,omitempty" doc:"the event level (INFO, WARN, ERROR)"` 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" ` 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\")"` 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)"` Type string `json:"type,omitempty" doc:"the event type (see event types)"`
} _ bool `name:"listEvents" description:"A command to list events."`
// name returns the CloudStack API command name
func (*ListEvents) name() string {
return "listEvents"
}
func (*ListEvents) response() interface{} {
return new(ListEventsResponse)
} }
// ListEventsResponse represents a response of a list query // ListEventsResponse represents a response of a list query
@ -55,22 +45,22 @@ type ListEventsResponse struct {
Event []Event `json:"event"` Event []Event `json:"event"`
} }
// ListEventTypes list the event types func (ListEvents) response() interface{} {
// return new(ListEventsResponse)
// 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 (*ListEventTypes) response() interface{} { // ListEventTypes list the event types
return new(ListEventTypesResponse) type ListEventTypes struct {
_ bool `name:"listEventTypes" description:"List Event Types"`
} }
// ListEventTypesResponse represents a response of a list query // ListEventTypesResponse represents a response of a list query
type ListEventTypesResponse struct { type ListEventTypesResponse struct {
Count int `json:"count"` Count int `json:"count"`
EventType []EventType `json:"eventtype"` 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 package egoscale
func (*ListResourceLimits) name() string { // https://github.com/apache/cloudstack/blob/master/api/src/main/java/com/cloud/configuration/Resource.java
return "listResourceLimits"
// 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) return new(ListResourceLimitsResponse)
} }
func (*UpdateResourceLimit) name() string { // UpdateResourceLimit updates the resource limit
return "updateResourceLimit" 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) return new(UpdateResourceLimitResponse)
} }
func (*GetAPILimit) name() string { // GetAPILimit gets API limit count for the caller
return "getAPILimit" 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) 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 package egoscale
func (*ListNetworkOfferings) name() string { // NetworkOffering corresponds to the Compute Offerings
return "listNetworkOfferings" 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) return new(ListNetworkOfferingsResponse)
} }
func (*UpdateNetworkOffering) name() string { // UpdateNetworkOffering represents a modification of a network offering
return "updateNetworkOffering" 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{} { func (UpdateNetworkOffering) response() interface{} {
return new(UpdateNetworkOfferingResponse) 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 ( import (
"fmt" "fmt"
"net"
"net/url" "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 // ListRequest builds the ListNetworks request
func (network *Network) ListRequest() (ListCommand, error) { func (network Network) ListRequest() (ListCommand, error) {
//TODO add tags support //TODO add tags support
req := &ListNetworks{ req := &ListNetworks{
Account: network.Account, Account: network.Account,
ACLType: network.ACLType,
CanUseForDeploy: &network.CanUseForDeploy,
DomainID: network.DomainID, DomainID: network.DomainID,
ID: network.ID, ID: network.ID,
PhysicalNetworkID: network.PhysicalNetworkID, PhysicalNetworkID: network.PhysicalNetworkID,
RestartRequired: &network.RestartRequired,
TrafficType: network.TrafficType, TrafficType: network.TrafficType,
Type: network.Type, Type: network.Type,
ZoneID: network.ZoneID, ZoneID: network.ZoneID,
} }
if network.CanUseForDeploy {
req.CanUseForDeploy = &network.CanUseForDeploy
}
if network.RestartRequired {
req.RestartRequired = &network.RestartRequired
}
return req, nil return req, nil
} }
// ResourceType returns the type of the resource // ResourceType returns the type of the resource
func (*Network) ResourceType() string { func (Network) ResourceType() string {
return "Network" return "Network"
} }
// name returns the CloudStack API command name // Service is a feature of a network
func (*CreateNetwork) name() string { type Service struct {
return "createNetwork" Capability []ServiceCapability `json:"capability,omitempty"`
Name string `json:"name"`
Provider []ServiceProvider `json:"provider,omitempty"`
} }
func (*CreateNetwork) response() interface{} { // ServiceCapability represents optional capability of a service
return new(CreateNetworkResponse) 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 // Those fields are required but might be empty
if req.Name == "" { if req.Name == "" {
params.Set("name", "") params.Set("name", "")
@ -49,39 +148,89 @@ func (req *CreateNetwork) onBeforeSend(params *url.Values) error {
return nil return nil
} }
// name returns the CloudStack API command name // UpdateNetwork (Async) updates a network
func (*UpdateNetwork) name() string { type UpdateNetwork struct {
return "updateNetwork" 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{} { func (UpdateNetwork) response() interface{} {
return new(UpdateNetworkResponse) return new(AsyncJobResult)
} }
// name returns the CloudStack API command name func (UpdateNetwork) asyncResponse() interface{} {
func (*RestartNetwork) name() string { return new(Network)
return "restartNetwork"
} }
func (*RestartNetwork) asyncResponse() interface{} { // RestartNetwork (Async) updates a network
return new(RestartNetworkResponse) 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 (RestartNetwork) response() interface{} {
func (*DeleteNetwork) name() string { return new(AsyncJobResult)
return "deleteNetwork"
} }
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) return new(booleanResponse)
} }
// name returns the CloudStack API command name // ListNetworks represents a query to a network
func (*ListNetworks) name() string { type ListNetworks struct {
return "listNetworks" 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) return new(ListNetworksResponse)
} }
@ -95,10 +244,10 @@ func (listNetwork *ListNetworks) SetPageSize(pageSize int) {
listNetwork.PageSize = pageSize listNetwork.PageSize = pageSize
} }
func (*ListNetworks) each(resp interface{}, callback IterateItemFunc) { func (ListNetworks) each(resp interface{}, callback IterateItemFunc) {
networks, ok := resp.(*ListNetworksResponse) networks, ok := resp.(*ListNetworksResponse)
if !ok { if !ok {
callback(nil, fmt.Errorf("ListNetworksResponse expected, got %t", resp)) callback(nil, fmt.Errorf("type error: ListNetworksResponse expected, got %T", resp))
return 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 package egoscale
import ( 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 // ListRequest build a ListNics request from the given Nic
func (nic *Nic) ListRequest() (ListCommand, error) { func (nic Nic) ListRequest() (ListCommand, error) {
if nic.VirtualMachineID == "" { if nic.VirtualMachineID == nil {
return nil, fmt.Errorf("ListNics command requires the VirtualMachineID field to be set") return nil, errors.New("command ListNics requires the VirtualMachineID field to be set")
} }
req := &ListNics{ req := &ListNics{
@ -19,12 +45,34 @@ func (nic *Nic) ListRequest() (ListCommand, error) {
return req, nil return req, nil
} }
// name returns the CloudStack API command name // NicSecondaryIP represents a link between NicID and IPAddress
func (*ListNics) name() string { type NicSecondaryIP struct {
return "listNics" 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) return new(ListNicsResponse)
} }
@ -38,7 +86,7 @@ func (ls *ListNics) SetPageSize(pageSize int) {
ls.PageSize = pageSize ls.PageSize = pageSize
} }
func (*ListNics) each(resp interface{}, callback IterateItemFunc) { func (ListNics) each(resp interface{}, callback IterateItemFunc) {
nics := resp.(*ListNicsResponse) nics := resp.(*ListNicsResponse)
for i := range nics.Nic { for i := range nics.Nic {
if !callback(&(nics.Nic[i]), nil) { 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 // AddIPToNic (Async) represents the assignation of a secondary IP
func (*AddIPToNic) name() string { type AddIPToNic struct {
return "addIpToNic" 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"`
func (*AddIPToNic) asyncResponse() interface{} { _ bool `name:"addIpToNic" description:"Assigns secondary IP to NIC"`
return new(AddIPToNicResponse)
} }
// name returns the CloudStack API command name: removeIpFromNic func (AddIPToNic) response() interface{} {
func (*RemoveIPFromNic) name() string { return new(AsyncJobResult)
return "removeIpFromNic"
} }
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) return new(booleanResponse)
} }
// name returns the CloudStack API command name: activateIp6 // ActivateIP6 (Async) activates the IP6 on the given NIC
func (*ActivateIP6) name() string { //
return "activateIp6" // 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{} { func (ActivateIP6) response() interface{} {
return new(ActivateIP6Response) 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