From 8c14945ecdabd42c5662860b795be2f3a3f00ed3 Mon Sep 17 00:00:00 2001 From: Mathieu Tortuyaux Date: Mon, 12 Feb 2024 15:32:44 +0100 Subject: [PATCH 1/8] plaform/aws: make S3 variable public Doing this, we prepare the landing of Scaleway provider and leverage the existing S3 AWS methods. Signed-off-by: Mathieu Tortuyaux --- platform/api/aws/api.go | 4 ++-- platform/api/aws/s3.go | 16 ++++++++-------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/platform/api/aws/api.go b/platform/api/aws/api.go index 4718bf100..3441d3c97 100644 --- a/platform/api/aws/api.go +++ b/platform/api/aws/api.go @@ -62,7 +62,7 @@ type API struct { ec2 *ec2.EC2 iam *iam.IAM marketplace *marketplacecatalog.MarketplaceCatalog - s3 *s3.S3 + S3 *s3.S3 opts *Options } @@ -98,7 +98,7 @@ func New(opts *Options) (*API, error) { ec2: ec2.New(sess), marketplace: marketplacecatalog.New(sess), iam: iam.New(sess), - s3: s3.New(sess), + S3: s3.New(sess), opts: opts, } diff --git a/platform/api/aws/s3.go b/platform/api/aws/s3.go index 071ca275c..c2aeafce1 100644 --- a/platform/api/aws/s3.go +++ b/platform/api/aws/s3.go @@ -55,10 +55,10 @@ func (a *API) UploadObject(r io.Reader, bucket, path string, force bool) error { // UploadObjectExt uploads an object to S3 with more control over options. func (a *API) UploadObjectExt(r io.Reader, bucket, path string, force bool, policy string, contentType string, max_age int) error { - s3uploader := s3manager.NewUploaderWithClient(a.s3) + s3uploader := s3manager.NewUploaderWithClient(a.S3) if !force { - _, err := a.s3.HeadObject(&s3.HeadObjectInput{ + _, err := a.S3.HeadObject(&s3.HeadObjectInput{ Bucket: &bucket, Key: &path, }) @@ -95,7 +95,7 @@ func (a *API) UploadObjectExt(r io.Reader, bucket, path string, force bool, poli func (a *API) DeleteObject(bucket, path string) error { plog.Infof("Deleting s3://%v/%v", bucket, path) - _, err := a.s3.DeleteObject(&s3.DeleteObjectInput{ + _, err := a.S3.DeleteObject(&s3.DeleteObjectInput{ Bucket: aws.String(bucket), Key: aws.String(path), }) @@ -106,7 +106,7 @@ func (a *API) DeleteObject(bucket, path string) error { } func (a *API) InitializeBucket(bucket string) error { - _, err := a.s3.CreateBucket(&s3.CreateBucketInput{ + _, err := a.S3.CreateBucket(&s3.CreateBucketInput{ Bucket: &bucket, }) if err != nil { @@ -121,7 +121,7 @@ func (a *API) InitializeBucket(bucket string) error { // This will modify the ACL on Objects to one of the canned ACL policies func (a *API) PutObjectAcl(bucket, path, policy string) error { - _, err := a.s3.PutObjectAcl(&s3.PutObjectAclInput{ + _, err := a.S3.PutObjectAcl(&s3.PutObjectAclInput{ ACL: aws.String(policy), Bucket: aws.String(bucket), Key: aws.String(path), @@ -138,7 +138,7 @@ func (a *API) CopyObject(srcBucket, srcPath, destBucket, destPath, policy string if err != nil { return fmt.Errorf("creating destination bucket: %v", err) } - _, err = a.s3.CopyObject(&s3.CopyObjectInput{ + _, err = a.S3.CopyObject(&s3.CopyObjectInput{ ACL: aws.String(policy), CopySource: aws.String(url.QueryEscape(fmt.Sprintf("%s/%s", srcBucket, srcPath))), Bucket: aws.String(destBucket), @@ -156,7 +156,7 @@ func (a *API) CopyObject(srcBucket, srcPath, destBucket, destPath, policy string // Copies all objects in srcBucket to destBucket with a given canned ACL policy func (a *API) CopyBucket(srcBucket, prefix, destBucket, policy string) error { - objects, err := a.s3.ListObjects(&s3.ListObjectsInput{ + objects, err := a.S3.ListObjects(&s3.ListObjectsInput{ Bucket: aws.String(srcBucket), Prefix: aws.String(prefix), }) @@ -183,7 +183,7 @@ func (a *API) CopyBucket(srcBucket, prefix, destBucket, policy string) error { // TODO: bikeshed this name // modifies the ACL of all objects of a given prefix in srcBucket to a given canned ACL policy func (a *API) UpdateBucketObjectsACL(srcBucket, prefix, policy string) error { - objects, err := a.s3.ListObjects(&s3.ListObjectsInput{ + objects, err := a.S3.ListObjects(&s3.ListObjectsInput{ Bucket: aws.String(srcBucket), Prefix: aws.String(prefix), }) From 92d87a2942fe45bcbc231389cd0f3a22cc090baa Mon Sep 17 00:00:00 2001 From: Mathieu Tortuyaux Date: Mon, 12 Feb 2024 15:57:34 +0100 Subject: [PATCH 2/8] platform/aws: conditionally bind ACL to bucket It changes nothing except for Scaleway: we get an error 400 if we try to set even an empty ACL. Signed-off-by: Mathieu Tortuyaux --- platform/api/aws/s3.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/platform/api/aws/s3.go b/platform/api/aws/s3.go index c2aeafce1..3b6a6287f 100644 --- a/platform/api/aws/s3.go +++ b/platform/api/aws/s3.go @@ -76,8 +76,12 @@ func (a *API) UploadObjectExt(r io.Reader, bucket, path string, force bool, poli Body: r, Bucket: aws.String(bucket), Key: aws.String(path), - ACL: aws.String(policy), } + + if policy != "" { + input.ACL = aws.String(policy) + } + if max_age >= 0 { input.CacheControl = aws.String(fmt.Sprintf("max-age=%d", max_age)) } From 8869e65bd90e449931036184a527b29fc78f1b9a Mon Sep 17 00:00:00 2001 From: Mathieu Tortuyaux Date: Mon, 12 Feb 2024 17:13:51 +0100 Subject: [PATCH 3/8] mod: add scaleway go SDK Signed-off-by: Mathieu Tortuyaux --- go.mod | 2 ++ go.sum | 2 ++ 2 files changed, 4 insertions(+) diff --git a/go.mod b/go.mod index 3349dbb08..4318fb431 100644 --- a/go.mod +++ b/go.mod @@ -31,6 +31,7 @@ require ( github.com/packethost/packngo v0.21.0 github.com/pborman/uuid v1.2.0 github.com/pin/tftp v2.1.0+incompatible + github.com/scaleway/scaleway-sdk-go v1.0.0-beta.23 github.com/spf13/cobra v1.1.3 github.com/spf13/pflag v1.0.6-0.20210604193023-d5e0c0615ace github.com/stretchr/testify v1.9.0 @@ -135,6 +136,7 @@ require ( google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d // indirect google.golang.org/grpc v1.59.0 // indirect google.golang.org/protobuf v1.33.0 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect ) replace github.com/Microsoft/azure-vhd-utils => github.com/kinvolk/azure-vhd-utils v0.0.0-20210818134022-97083698b75f diff --git a/go.sum b/go.sum index d6b06585e..6b901ef35 100644 --- a/go.sum +++ b/go.sum @@ -478,6 +478,8 @@ github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFR github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd/go.mod h1:hPqNNc0+uJM6H+SuU8sEs5K5IQeKccPqeSjfgcKGgPk= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +github.com/scaleway/scaleway-sdk-go v1.0.0-beta.23 h1:zr2sP1pxJ+iPAmZipnwz+uXmpvMEJOndD9Y+F0Dn42A= +github.com/scaleway/scaleway-sdk-go v1.0.0-beta.23/go.mod h1:fCa7OJZ/9DRTnOKmxvT6pn+LPWUptQAmHF/SBJUGEcg= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sigma/bdoor v0.0.0-20160202064022-babf2a4017b0/go.mod h1:WBu7REWbxC/s/J06jsk//d+9DOz9BbsmcIrimuGRFbs= From e46767a9650b47f045acbc8703d8fdf3c70f29fb Mon Sep 17 00:00:00 2001 From: Mathieu Tortuyaux Date: Mon, 12 Feb 2024 17:14:06 +0100 Subject: [PATCH 4/8] mod: go mod vendor Signed-off-by: Mathieu Tortuyaux --- .../scaleway/scaleway-sdk-go/LICENSE | 191 + .../api/block/v1alpha1/block_sdk.go | 1195 +++ .../api/block/v1alpha1/snapshot_utils.go | 59 + .../api/block/v1alpha1/volume_utils.go | 149 + .../api/instance/v1/image_utils.go | 56 + .../api/instance/v1/instance_metadata_sdk.go | 319 + .../api/instance/v1/instance_sdk.go | 6498 +++++++++++++++++ .../api/instance/v1/instance_utils.go | 434 ++ .../api/instance/v1/server_utils.go | 409 ++ .../api/instance/v1/snapshot_utils.go | 56 + .../api/instance/v1/volume_utils.go | 113 + .../api/marketplace/v1/marketplace_sdk.go | 221 + .../api/marketplace/v1/marketplace_utils.go | 93 + .../scaleway-sdk-go/internal/async/wait.go | 90 + .../internal/auth/access_key.go | 21 + .../scaleway-sdk-go/internal/auth/auth.go | 29 + .../scaleway-sdk-go/internal/auth/jwt.go | 55 + .../scaleway-sdk-go/internal/auth/no_auth.go | 19 + .../scaleway-sdk-go/internal/auth/token.go | 51 + .../scaleway-sdk-go/internal/errors/error.go | 41 + .../internal/generic/fields.go | 17 + .../scaleway-sdk-go/internal/generic/ptr.go | 11 + .../scaleway-sdk-go/internal/generic/sort.go | 17 + .../internal/marshaler/duration.go | 160 + .../internal/parameter/query.go | 42 + .../scaleway-sdk-go/logger/default_logger.go | 112 + .../scaleway/scaleway-sdk-go/logger/logger.go | 52 + .../namegenerator/name_generator.go | 863 +++ .../scaleway/scaleway-sdk-go/scw/README.md | 52 + .../scaleway/scaleway-sdk-go/scw/client.go | 583 ++ .../scaleway-sdk-go/scw/client_option.go | 276 + .../scaleway/scaleway-sdk-go/scw/config.go | 361 + .../scaleway/scaleway-sdk-go/scw/convert.go | 176 + .../scaleway-sdk-go/scw/custom_types.go | 450 ++ .../scaleway/scaleway-sdk-go/scw/env.go | 145 + .../scaleway/scaleway-sdk-go/scw/errors.go | 552 ++ .../scaleway/scaleway-sdk-go/scw/locality.go | 221 + .../scaleway/scaleway-sdk-go/scw/path.go | 89 + .../scaleway/scaleway-sdk-go/scw/request.go | 99 + .../scaleway-sdk-go/scw/request_header.go | 32 + .../scw/request_header_wasm.go | 32 + .../scaleway-sdk-go/scw/request_option.go | 50 + .../scaleway/scaleway-sdk-go/scw/transport.go | 62 + .../scaleway/scaleway-sdk-go/scw/version.go | 11 + .../scaleway/scaleway-sdk-go/validation/is.go | 61 + vendor/gopkg.in/yaml.v2/.travis.yml | 17 + vendor/gopkg.in/yaml.v2/LICENSE | 201 + vendor/gopkg.in/yaml.v2/LICENSE.libyaml | 31 + vendor/gopkg.in/yaml.v2/NOTICE | 13 + vendor/gopkg.in/yaml.v2/README.md | 133 + vendor/gopkg.in/yaml.v2/apic.go | 744 ++ vendor/gopkg.in/yaml.v2/decode.go | 815 +++ vendor/gopkg.in/yaml.v2/emitterc.go | 1685 +++++ vendor/gopkg.in/yaml.v2/encode.go | 390 + vendor/gopkg.in/yaml.v2/parserc.go | 1095 +++ vendor/gopkg.in/yaml.v2/readerc.go | 412 ++ vendor/gopkg.in/yaml.v2/resolve.go | 258 + vendor/gopkg.in/yaml.v2/scannerc.go | 2711 +++++++ vendor/gopkg.in/yaml.v2/sorter.go | 113 + vendor/gopkg.in/yaml.v2/writerc.go | 26 + vendor/gopkg.in/yaml.v2/yaml.go | 478 ++ vendor/gopkg.in/yaml.v2/yamlh.go | 739 ++ vendor/gopkg.in/yaml.v2/yamlprivateh.go | 173 + vendor/modules.txt | 18 + 64 files changed, 24677 insertions(+) create mode 100644 vendor/github.com/scaleway/scaleway-sdk-go/LICENSE create mode 100644 vendor/github.com/scaleway/scaleway-sdk-go/api/block/v1alpha1/block_sdk.go create mode 100644 vendor/github.com/scaleway/scaleway-sdk-go/api/block/v1alpha1/snapshot_utils.go create mode 100644 vendor/github.com/scaleway/scaleway-sdk-go/api/block/v1alpha1/volume_utils.go create mode 100644 vendor/github.com/scaleway/scaleway-sdk-go/api/instance/v1/image_utils.go create mode 100644 vendor/github.com/scaleway/scaleway-sdk-go/api/instance/v1/instance_metadata_sdk.go create mode 100644 vendor/github.com/scaleway/scaleway-sdk-go/api/instance/v1/instance_sdk.go create mode 100644 vendor/github.com/scaleway/scaleway-sdk-go/api/instance/v1/instance_utils.go create mode 100644 vendor/github.com/scaleway/scaleway-sdk-go/api/instance/v1/server_utils.go create mode 100644 vendor/github.com/scaleway/scaleway-sdk-go/api/instance/v1/snapshot_utils.go create mode 100644 vendor/github.com/scaleway/scaleway-sdk-go/api/instance/v1/volume_utils.go create mode 100644 vendor/github.com/scaleway/scaleway-sdk-go/api/marketplace/v1/marketplace_sdk.go create mode 100644 vendor/github.com/scaleway/scaleway-sdk-go/api/marketplace/v1/marketplace_utils.go create mode 100644 vendor/github.com/scaleway/scaleway-sdk-go/internal/async/wait.go create mode 100644 vendor/github.com/scaleway/scaleway-sdk-go/internal/auth/access_key.go create mode 100644 vendor/github.com/scaleway/scaleway-sdk-go/internal/auth/auth.go create mode 100644 vendor/github.com/scaleway/scaleway-sdk-go/internal/auth/jwt.go create mode 100644 vendor/github.com/scaleway/scaleway-sdk-go/internal/auth/no_auth.go create mode 100644 vendor/github.com/scaleway/scaleway-sdk-go/internal/auth/token.go create mode 100644 vendor/github.com/scaleway/scaleway-sdk-go/internal/errors/error.go create mode 100644 vendor/github.com/scaleway/scaleway-sdk-go/internal/generic/fields.go create mode 100644 vendor/github.com/scaleway/scaleway-sdk-go/internal/generic/ptr.go create mode 100644 vendor/github.com/scaleway/scaleway-sdk-go/internal/generic/sort.go create mode 100644 vendor/github.com/scaleway/scaleway-sdk-go/internal/marshaler/duration.go create mode 100644 vendor/github.com/scaleway/scaleway-sdk-go/internal/parameter/query.go create mode 100644 vendor/github.com/scaleway/scaleway-sdk-go/logger/default_logger.go create mode 100644 vendor/github.com/scaleway/scaleway-sdk-go/logger/logger.go create mode 100644 vendor/github.com/scaleway/scaleway-sdk-go/namegenerator/name_generator.go create mode 100644 vendor/github.com/scaleway/scaleway-sdk-go/scw/README.md create mode 100644 vendor/github.com/scaleway/scaleway-sdk-go/scw/client.go create mode 100644 vendor/github.com/scaleway/scaleway-sdk-go/scw/client_option.go create mode 100644 vendor/github.com/scaleway/scaleway-sdk-go/scw/config.go create mode 100644 vendor/github.com/scaleway/scaleway-sdk-go/scw/convert.go create mode 100644 vendor/github.com/scaleway/scaleway-sdk-go/scw/custom_types.go create mode 100644 vendor/github.com/scaleway/scaleway-sdk-go/scw/env.go create mode 100644 vendor/github.com/scaleway/scaleway-sdk-go/scw/errors.go create mode 100644 vendor/github.com/scaleway/scaleway-sdk-go/scw/locality.go create mode 100644 vendor/github.com/scaleway/scaleway-sdk-go/scw/path.go create mode 100644 vendor/github.com/scaleway/scaleway-sdk-go/scw/request.go create mode 100644 vendor/github.com/scaleway/scaleway-sdk-go/scw/request_header.go create mode 100644 vendor/github.com/scaleway/scaleway-sdk-go/scw/request_header_wasm.go create mode 100644 vendor/github.com/scaleway/scaleway-sdk-go/scw/request_option.go create mode 100644 vendor/github.com/scaleway/scaleway-sdk-go/scw/transport.go create mode 100644 vendor/github.com/scaleway/scaleway-sdk-go/scw/version.go create mode 100644 vendor/github.com/scaleway/scaleway-sdk-go/validation/is.go create mode 100644 vendor/gopkg.in/yaml.v2/.travis.yml create mode 100644 vendor/gopkg.in/yaml.v2/LICENSE create mode 100644 vendor/gopkg.in/yaml.v2/LICENSE.libyaml create mode 100644 vendor/gopkg.in/yaml.v2/NOTICE create mode 100644 vendor/gopkg.in/yaml.v2/README.md create mode 100644 vendor/gopkg.in/yaml.v2/apic.go create mode 100644 vendor/gopkg.in/yaml.v2/decode.go create mode 100644 vendor/gopkg.in/yaml.v2/emitterc.go create mode 100644 vendor/gopkg.in/yaml.v2/encode.go create mode 100644 vendor/gopkg.in/yaml.v2/parserc.go create mode 100644 vendor/gopkg.in/yaml.v2/readerc.go create mode 100644 vendor/gopkg.in/yaml.v2/resolve.go create mode 100644 vendor/gopkg.in/yaml.v2/scannerc.go create mode 100644 vendor/gopkg.in/yaml.v2/sorter.go create mode 100644 vendor/gopkg.in/yaml.v2/writerc.go create mode 100644 vendor/gopkg.in/yaml.v2/yaml.go create mode 100644 vendor/gopkg.in/yaml.v2/yamlh.go create mode 100644 vendor/gopkg.in/yaml.v2/yamlprivateh.go diff --git a/vendor/github.com/scaleway/scaleway-sdk-go/LICENSE b/vendor/github.com/scaleway/scaleway-sdk-go/LICENSE new file mode 100644 index 000000000..2712cc51a --- /dev/null +++ b/vendor/github.com/scaleway/scaleway-sdk-go/LICENSE @@ -0,0 +1,191 @@ + + Apache License + Version 2.0, January 2004 + https://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + Copyright 2019 Scaleway. + + 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 + + https://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. diff --git a/vendor/github.com/scaleway/scaleway-sdk-go/api/block/v1alpha1/block_sdk.go b/vendor/github.com/scaleway/scaleway-sdk-go/api/block/v1alpha1/block_sdk.go new file mode 100644 index 000000000..b729fb7d3 --- /dev/null +++ b/vendor/github.com/scaleway/scaleway-sdk-go/api/block/v1alpha1/block_sdk.go @@ -0,0 +1,1195 @@ +// This file was automatically generated. DO NOT EDIT. +// If you have any remark or suggestion do not hesitate to open an issue. + +// Package block provides methods and message types of the block v1alpha1 API. +package block + +import ( + "bytes" + "encoding/json" + "fmt" + "net" + "net/http" + "net/url" + "strings" + "time" + + "github.com/scaleway/scaleway-sdk-go/internal/errors" + "github.com/scaleway/scaleway-sdk-go/internal/marshaler" + "github.com/scaleway/scaleway-sdk-go/internal/parameter" + "github.com/scaleway/scaleway-sdk-go/namegenerator" + "github.com/scaleway/scaleway-sdk-go/scw" +) + +// always import dependencies +var ( + _ fmt.Stringer + _ json.Unmarshaler + _ url.URL + _ net.IP + _ http.Header + _ bytes.Reader + _ time.Time + _ = strings.Join + + _ scw.ScalewayRequest + _ marshaler.Duration + _ scw.File + _ = parameter.AddToQuery + _ = namegenerator.GetRandomName +) + +type ListSnapshotsRequestOrderBy string + +const ( + // Order by creation date (ascending chronological order). + ListSnapshotsRequestOrderByCreatedAtAsc = ListSnapshotsRequestOrderBy("created_at_asc") + // Order by creation date (descending chronological order). + ListSnapshotsRequestOrderByCreatedAtDesc = ListSnapshotsRequestOrderBy("created_at_desc") + // Order by name (ascending order). + ListSnapshotsRequestOrderByNameAsc = ListSnapshotsRequestOrderBy("name_asc") + // Order by name (descending order). + ListSnapshotsRequestOrderByNameDesc = ListSnapshotsRequestOrderBy("name_desc") +) + +func (enum ListSnapshotsRequestOrderBy) String() string { + if enum == "" { + // return default value if empty + return "created_at_asc" + } + return string(enum) +} + +func (enum ListSnapshotsRequestOrderBy) MarshalJSON() ([]byte, error) { + return []byte(fmt.Sprintf(`"%s"`, enum)), nil +} + +func (enum *ListSnapshotsRequestOrderBy) UnmarshalJSON(data []byte) error { + tmp := "" + + if err := json.Unmarshal(data, &tmp); err != nil { + return err + } + + *enum = ListSnapshotsRequestOrderBy(ListSnapshotsRequestOrderBy(tmp).String()) + return nil +} + +type ListVolumesRequestOrderBy string + +const ( + // Order by creation date (ascending chronological order). + ListVolumesRequestOrderByCreatedAtAsc = ListVolumesRequestOrderBy("created_at_asc") + // Order by creation date (descending chronological order). + ListVolumesRequestOrderByCreatedAtDesc = ListVolumesRequestOrderBy("created_at_desc") + // Order by name (ascending order). + ListVolumesRequestOrderByNameAsc = ListVolumesRequestOrderBy("name_asc") + // Order by name (descending order). + ListVolumesRequestOrderByNameDesc = ListVolumesRequestOrderBy("name_desc") +) + +func (enum ListVolumesRequestOrderBy) String() string { + if enum == "" { + // return default value if empty + return "created_at_asc" + } + return string(enum) +} + +func (enum ListVolumesRequestOrderBy) MarshalJSON() ([]byte, error) { + return []byte(fmt.Sprintf(`"%s"`, enum)), nil +} + +func (enum *ListVolumesRequestOrderBy) UnmarshalJSON(data []byte) error { + tmp := "" + + if err := json.Unmarshal(data, &tmp); err != nil { + return err + } + + *enum = ListVolumesRequestOrderBy(ListVolumesRequestOrderBy(tmp).String()) + return nil +} + +type ReferenceStatus string + +const ( + // If unspecified, the status of the reference is unknown by default. + ReferenceStatusUnknownStatus = ReferenceStatus("unknown_status") + // When the reference is being attached (transient). + ReferenceStatusAttaching = ReferenceStatus("attaching") + // When the reference attached to a volume. + ReferenceStatusAttached = ReferenceStatus("attached") + // When the reference is being detached (transient). + ReferenceStatusDetaching = ReferenceStatus("detaching") + // When the reference is detached from a volume - the reference ceases to exist. + ReferenceStatusDetached = ReferenceStatus("detached") + // Reference undergoing snapshotting operation (transient). + ReferenceStatusSnapshotting = ReferenceStatus("snapshotting") + // Error status. + ReferenceStatusError = ReferenceStatus("error") +) + +func (enum ReferenceStatus) String() string { + if enum == "" { + // return default value if empty + return "unknown_status" + } + return string(enum) +} + +func (enum ReferenceStatus) MarshalJSON() ([]byte, error) { + return []byte(fmt.Sprintf(`"%s"`, enum)), nil +} + +func (enum *ReferenceStatus) UnmarshalJSON(data []byte) error { + tmp := "" + + if err := json.Unmarshal(data, &tmp); err != nil { + return err + } + + *enum = ReferenceStatus(ReferenceStatus(tmp).String()) + return nil +} + +type ReferenceType string + +const ( + // If unspecified, the reference type is unknown by default. + ReferenceTypeUnknownType = ReferenceType("unknown_type") + // Reference linked to a snapshot (for snapshots only). + ReferenceTypeLink = ReferenceType("link") + // Exclusive reference that can be associated to a volume (for volumes only). + ReferenceTypeExclusive = ReferenceType("exclusive") + // Access to the volume or snapshot in a read-only mode, without storage write access to the resource. + ReferenceTypeReadOnly = ReferenceType("read_only") +) + +func (enum ReferenceType) String() string { + if enum == "" { + // return default value if empty + return "unknown_type" + } + return string(enum) +} + +func (enum ReferenceType) MarshalJSON() ([]byte, error) { + return []byte(fmt.Sprintf(`"%s"`, enum)), nil +} + +func (enum *ReferenceType) UnmarshalJSON(data []byte) error { + tmp := "" + + if err := json.Unmarshal(data, &tmp); err != nil { + return err + } + + *enum = ReferenceType(ReferenceType(tmp).String()) + return nil +} + +type SnapshotStatus string + +const ( + // If unspecified, the snapshot status is unknown by default. + SnapshotStatusUnknownStatus = SnapshotStatus("unknown_status") + // The snapshot is under creation (transient). + SnapshotStatusCreating = SnapshotStatus("creating") + // Snapshot exists and is not attached to any reference. + SnapshotStatusAvailable = SnapshotStatus("available") + // Snapshot in an error status. + SnapshotStatusError = SnapshotStatus("error") + // Snapshot is being deleted (transient). + SnapshotStatusDeleting = SnapshotStatus("deleting") + // Snapshot was deleted. + SnapshotStatusDeleted = SnapshotStatus("deleted") + // Snapshot attached to one or more references. + SnapshotStatusInUse = SnapshotStatus("in_use") + SnapshotStatusLocked = SnapshotStatus("locked") +) + +func (enum SnapshotStatus) String() string { + if enum == "" { + // return default value if empty + return "unknown_status" + } + return string(enum) +} + +func (enum SnapshotStatus) MarshalJSON() ([]byte, error) { + return []byte(fmt.Sprintf(`"%s"`, enum)), nil +} + +func (enum *SnapshotStatus) UnmarshalJSON(data []byte) error { + tmp := "" + + if err := json.Unmarshal(data, &tmp); err != nil { + return err + } + + *enum = SnapshotStatus(SnapshotStatus(tmp).String()) + return nil +} + +type StorageClass string + +const ( + // If unspecified, the Storage Class is unknown by default. + StorageClassUnknownStorageClass = StorageClass("unknown_storage_class") + // No specific Storage Class selected. + StorageClassUnspecified = StorageClass("unspecified") + // Classic storage. + StorageClassBssd = StorageClass("bssd") + // Performance storage with lower latency. + StorageClassSbs = StorageClass("sbs") +) + +func (enum StorageClass) String() string { + if enum == "" { + // return default value if empty + return "unknown_storage_class" + } + return string(enum) +} + +func (enum StorageClass) MarshalJSON() ([]byte, error) { + return []byte(fmt.Sprintf(`"%s"`, enum)), nil +} + +func (enum *StorageClass) UnmarshalJSON(data []byte) error { + tmp := "" + + if err := json.Unmarshal(data, &tmp); err != nil { + return err + } + + *enum = StorageClass(StorageClass(tmp).String()) + return nil +} + +type VolumeStatus string + +const ( + // If unspecified, the volume status is unknown by default. + VolumeStatusUnknownStatus = VolumeStatus("unknown_status") + // The volume is under creation (transient). + VolumeStatusCreating = VolumeStatus("creating") + // The volume exists and is not attached to any reference. + VolumeStatusAvailable = VolumeStatus("available") + // The volume exists and is already attached to a reference. + VolumeStatusInUse = VolumeStatus("in_use") + // The volume undergoing deletion (transient). + VolumeStatusDeleting = VolumeStatus("deleting") + VolumeStatusDeleted = VolumeStatus("deleted") + // The volume is being increased (transient). + VolumeStatusResizing = VolumeStatus("resizing") + // The volume is an error status. + VolumeStatusError = VolumeStatus("error") + // The volume is undergoing snapshotting operation (transient). + VolumeStatusSnapshotting = VolumeStatus("snapshotting") + VolumeStatusLocked = VolumeStatus("locked") +) + +func (enum VolumeStatus) String() string { + if enum == "" { + // return default value if empty + return "unknown_status" + } + return string(enum) +} + +func (enum VolumeStatus) MarshalJSON() ([]byte, error) { + return []byte(fmt.Sprintf(`"%s"`, enum)), nil +} + +func (enum *VolumeStatus) UnmarshalJSON(data []byte) error { + tmp := "" + + if err := json.Unmarshal(data, &tmp); err != nil { + return err + } + + *enum = VolumeStatus(VolumeStatus(tmp).String()) + return nil +} + +// SnapshotParentVolume: snapshot parent volume. +type SnapshotParentVolume struct { + // ID: parent volume UUID (volume from which the snapshot originates). + ID string `json:"id"` + + // Name: name of the parent volume. + Name string `json:"name"` + + // Type: volume type of the parent volume. + Type string `json:"type"` + + // Status: current status the parent volume. + // Default value: unknown_status + Status VolumeStatus `json:"status"` +} + +// VolumeSpecifications: volume specifications. +type VolumeSpecifications struct { + // PerfIops: the maximum IO/s expected, according to the different options available in stock (`5000 | 15000`). + PerfIops *uint32 `json:"perf_iops"` + + // Class: the storage class of the volume. + // Default value: unknown_storage_class + Class StorageClass `json:"class"` +} + +// Reference: reference. +type Reference struct { + // ID: UUID of the reference. + ID string `json:"id"` + + // ProductResourceType: type of resource to which the reference is associated. + ProductResourceType string `json:"product_resource_type"` + + // ProductResourceID: UUID of the product resource it refers to (according to the product_resource_type). + ProductResourceID string `json:"product_resource_id"` + + // CreatedAt: creation date of the reference. + CreatedAt *time.Time `json:"created_at"` + + // Type: type of reference (link, exclusive, read_only). + // Default value: unknown_type + Type ReferenceType `json:"type"` + + // Status: status of reference (attaching, attached, detaching). + // Default value: unknown_status + Status ReferenceStatus `json:"status"` +} + +// CreateVolumeRequestFromEmpty: create volume request from empty. +type CreateVolumeRequestFromEmpty struct { + // Size: must be compliant with the minimum (1 GB) and maximum (10 TB) allowed size. + Size scw.Size `json:"size"` +} + +// CreateVolumeRequestFromSnapshot: create volume request from snapshot. +type CreateVolumeRequestFromSnapshot struct { + // Size: must be compliant with the minimum (1 GB) and maximum (10 TB) allowed size. + // Size is optional and is used only if a resize of the volume is requested, otherwise original snapshot size will be used. + Size *scw.Size `json:"size"` + + // SnapshotID: source snapshot from which volume will be created. + SnapshotID string `json:"snapshot_id"` +} + +// SnapshotSummary: snapshot summary. +type SnapshotSummary struct { + // ID: UUID of the snapshot. + ID string `json:"id"` + + // Name: name of the snapshot. + Name string `json:"name"` + + // ParentVolume: if the parent volume has been deleted, value is null. + ParentVolume *SnapshotParentVolume `json:"parent_volume"` + + // Size: size of the snapshot in bytes. + Size scw.Size `json:"size"` + + // ProjectID: UUID of the project the snapshot belongs to. + ProjectID string `json:"project_id"` + + // CreatedAt: creation date of the snapshot. + CreatedAt *time.Time `json:"created_at"` + + // UpdatedAt: last modification date of the properties of a snapshot. + UpdatedAt *time.Time `json:"updated_at"` + + // Status: current status of the snapshot (available, in_use, ...). + // Default value: unknown_status + Status SnapshotStatus `json:"status"` + + // Tags: list of tags assigned to the volume. + Tags []string `json:"tags"` + + // Zone: snapshot Availability Zone. + Zone scw.Zone `json:"zone"` + + // Class: storage class of the snapshot. + // Default value: unknown_storage_class + Class StorageClass `json:"class"` +} + +// VolumeType: volume type. +type VolumeType struct { + // Type: volume type. + Type string `json:"type"` + + // Pricing: price of the volume billed in GB/hour. + Pricing *scw.Money `json:"pricing"` + + // SnapshotPricing: price of the snapshot billed in GB/hour. + SnapshotPricing *scw.Money `json:"snapshot_pricing"` + + // Specs: volume specifications of the volume type. + Specs *VolumeSpecifications `json:"specs"` +} + +// Volume: volume. +type Volume struct { + // ID: UUID of the volume. + ID string `json:"id"` + + // Name: name of the volume. + Name string `json:"name"` + + // Type: volume type. + Type string `json:"type"` + + // Size: volume size in bytes. + Size scw.Size `json:"size"` + + // ProjectID: UUID of the project to which the volume belongs. + ProjectID string `json:"project_id"` + + // CreatedAt: creation date of the volume. + CreatedAt *time.Time `json:"created_at"` + + // UpdatedAt: last update of the properties of a volume. + UpdatedAt *time.Time `json:"updated_at"` + + // References: list of the references to the volume. + References []*Reference `json:"references"` + + // ParentSnapshotID: when a volume is created from a snapshot, is the UUID of the snapshot from which the volume has been created. + ParentSnapshotID *string `json:"parent_snapshot_id"` + + // Status: current status of the volume (available, in_use, ...). + // Default value: unknown_status + Status VolumeStatus `json:"status"` + + // Tags: list of tags assigned to the volume. + Tags []string `json:"tags"` + + // Zone: volume zone. + Zone scw.Zone `json:"zone"` + + // Specs: specifications of the volume. + Specs *VolumeSpecifications `json:"specs"` + + // LastDetachedAt: last time the volume was detached. + LastDetachedAt *time.Time `json:"last_detached_at"` +} + +// CreateSnapshotRequest: create snapshot request. +type CreateSnapshotRequest struct { + // Zone: zone to target. If none is passed will use default zone from the config. + Zone scw.Zone `json:"-"` + + // VolumeID: UUID of the volume to snapshot. + VolumeID string `json:"volume_id"` + + // Name: name of the snapshot. + Name string `json:"name"` + + // ProjectID: UUID of the project to which the volume and the snapshot belong. + ProjectID string `json:"project_id"` + + // Tags: list of tags assigned to the snapshot. + Tags []string `json:"tags"` +} + +// CreateVolumeRequest: create volume request. +type CreateVolumeRequest struct { + // Zone: zone to target. If none is passed will use default zone from the config. + Zone scw.Zone `json:"-"` + + // Name: name of the volume. + Name string `json:"name"` + + // PerfIops: the maximum IO/s expected, according to the different options available in stock (`5000 | 15000`). + // Precisely one of PerfIops must be set. + PerfIops *uint32 `json:"perf_iops,omitempty"` + + // ProjectID: UUID of the project the volume belongs to. + ProjectID string `json:"project_id"` + + // FromEmpty: specify the size of the new volume if creating a new one from scratch. + // Precisely one of FromEmpty, FromSnapshot must be set. + FromEmpty *CreateVolumeRequestFromEmpty `json:"from_empty,omitempty"` + + // FromSnapshot: specify the snapshot ID of the original snapshot. + // Precisely one of FromEmpty, FromSnapshot must be set. + FromSnapshot *CreateVolumeRequestFromSnapshot `json:"from_snapshot,omitempty"` + + // Tags: list of tags assigned to the volume. + Tags []string `json:"tags"` +} + +// DeleteSnapshotRequest: delete snapshot request. +type DeleteSnapshotRequest struct { + // Zone: zone to target. If none is passed will use default zone from the config. + Zone scw.Zone `json:"-"` + + // SnapshotID: UUID of the snapshot. + SnapshotID string `json:"-"` +} + +// DeleteVolumeRequest: delete volume request. +type DeleteVolumeRequest struct { + // Zone: zone to target. If none is passed will use default zone from the config. + Zone scw.Zone `json:"-"` + + // VolumeID: UUID of the volume. + VolumeID string `json:"-"` +} + +// GetSnapshotRequest: get snapshot request. +type GetSnapshotRequest struct { + // Zone: zone to target. If none is passed will use default zone from the config. + Zone scw.Zone `json:"-"` + + // SnapshotID: UUID of the snapshot. + SnapshotID string `json:"-"` +} + +// GetVolumeRequest: get volume request. +type GetVolumeRequest struct { + // Zone: zone to target. If none is passed will use default zone from the config. + Zone scw.Zone `json:"-"` + + // VolumeID: UUID of the volume. + VolumeID string `json:"-"` +} + +// ListSnapshotsRequest: list snapshots request. +type ListSnapshotsRequest struct { + // Zone: zone to target. If none is passed will use default zone from the config. + Zone scw.Zone `json:"-"` + + // OrderBy: criteria to use when ordering the list. + // Default value: created_at_asc + OrderBy ListSnapshotsRequestOrderBy `json:"-"` + + // ProjectID: filter by Project ID. + ProjectID *string `json:"-"` + + // OrganizationID: filter by Organization ID. + OrganizationID *string `json:"-"` + + // Page: page number. + Page *int32 `json:"-"` + + // PageSize: page size, defines how many entries are returned in one page, must be lower or equal to 100. + PageSize *uint32 `json:"-"` + + // VolumeID: filter snapshots by the ID of the original volume. + VolumeID *string `json:"-"` + + // Name: filter snapshots by their names. + Name *string `json:"-"` +} + +// ListSnapshotsResponse: list snapshots response. +type ListSnapshotsResponse struct { + // Snapshots: paginated returned list of snapshots. + Snapshots []*SnapshotSummary `json:"snapshots"` + + // TotalCount: total number of snpashots in the project. + TotalCount uint64 `json:"total_count"` +} + +// UnsafeGetTotalCount should not be used +// Internal usage only +func (r *ListSnapshotsResponse) UnsafeGetTotalCount() uint64 { + return r.TotalCount +} + +// UnsafeAppend should not be used +// Internal usage only +func (r *ListSnapshotsResponse) UnsafeAppend(res interface{}) (uint64, error) { + results, ok := res.(*ListSnapshotsResponse) + if !ok { + return 0, errors.New("%T type cannot be appended to type %T", res, r) + } + + r.Snapshots = append(r.Snapshots, results.Snapshots...) + r.TotalCount += uint64(len(results.Snapshots)) + return uint64(len(results.Snapshots)), nil +} + +// ListVolumeTypesRequest: list volume types request. +type ListVolumeTypesRequest struct { + // Zone: zone to target. If none is passed will use default zone from the config. + Zone scw.Zone `json:"-"` + + // Page: page number. + Page *int32 `json:"-"` + + // PageSize: page size, defines how many entries are returned in one page, must be lower or equal to 100. + PageSize *uint32 `json:"-"` +} + +// ListVolumeTypesResponse: list volume types response. +type ListVolumeTypesResponse struct { + // VolumeTypes: returns paginated list of volume-types. + VolumeTypes []*VolumeType `json:"volume_types"` + + // TotalCount: total number of volume-types currently available in stock. + TotalCount uint64 `json:"total_count"` +} + +// UnsafeGetTotalCount should not be used +// Internal usage only +func (r *ListVolumeTypesResponse) UnsafeGetTotalCount() uint64 { + return r.TotalCount +} + +// UnsafeAppend should not be used +// Internal usage only +func (r *ListVolumeTypesResponse) UnsafeAppend(res interface{}) (uint64, error) { + results, ok := res.(*ListVolumeTypesResponse) + if !ok { + return 0, errors.New("%T type cannot be appended to type %T", res, r) + } + + r.VolumeTypes = append(r.VolumeTypes, results.VolumeTypes...) + r.TotalCount += uint64(len(results.VolumeTypes)) + return uint64(len(results.VolumeTypes)), nil +} + +// ListVolumesRequest: list volumes request. +type ListVolumesRequest struct { + // Zone: zone to target. If none is passed will use default zone from the config. + Zone scw.Zone `json:"-"` + + // OrderBy: criteria to use when ordering the list. + // Default value: created_at_asc + OrderBy ListVolumesRequestOrderBy `json:"-"` + + // ProjectID: filter by Project ID. + ProjectID *string `json:"-"` + + // OrganizationID: filter by Organization ID. + OrganizationID *string `json:"-"` + + // Page: page number. + Page *int32 `json:"-"` + + // PageSize: page size, defines how many entries are returned in one page, must be lower or equal to 100. + PageSize *uint32 `json:"-"` + + // Name: filter the return volumes by their names. + Name *string `json:"-"` + + // ProductResourceID: filter by a product resource ID linked to this volume (such as an Instance ID). + ProductResourceID *string `json:"-"` +} + +// ListVolumesResponse: list volumes response. +type ListVolumesResponse struct { + // Volumes: paginated returned list of volumes. + Volumes []*Volume `json:"volumes"` + + // TotalCount: total number of volumes in the project. + TotalCount uint64 `json:"total_count"` +} + +// UnsafeGetTotalCount should not be used +// Internal usage only +func (r *ListVolumesResponse) UnsafeGetTotalCount() uint64 { + return r.TotalCount +} + +// UnsafeAppend should not be used +// Internal usage only +func (r *ListVolumesResponse) UnsafeAppend(res interface{}) (uint64, error) { + results, ok := res.(*ListVolumesResponse) + if !ok { + return 0, errors.New("%T type cannot be appended to type %T", res, r) + } + + r.Volumes = append(r.Volumes, results.Volumes...) + r.TotalCount += uint64(len(results.Volumes)) + return uint64(len(results.Volumes)), nil +} + +// Snapshot: snapshot. +type Snapshot struct { + // ID: UUID of the snapshot. + ID string `json:"id"` + + // Name: name of the snapshot. + Name string `json:"name"` + + // ParentVolume: if the parent volume was deleted, value is null. + ParentVolume *SnapshotParentVolume `json:"parent_volume"` + + // Size: size in bytes of the snapshot. + Size scw.Size `json:"size"` + + // ProjectID: UUID of the project the snapshot belongs to. + ProjectID string `json:"project_id"` + + // CreatedAt: creation date of the snapshot. + CreatedAt *time.Time `json:"created_at"` + + // UpdatedAt: last modification date of the properties of a snapshot. + UpdatedAt *time.Time `json:"updated_at"` + + // References: list of the references to the snapshot. + References []*Reference `json:"references"` + + // Status: current status of the snapshot (available, in_use, ...). + // Default value: unknown_status + Status SnapshotStatus `json:"status"` + + // Tags: list of tags assigned to the volume. + Tags []string `json:"tags"` + + // Zone: snapshot zone. + Zone scw.Zone `json:"zone"` + + // Class: storage class of the snapshot. + // Default value: unknown_storage_class + Class StorageClass `json:"class"` +} + +// UpdateSnapshotRequest: update snapshot request. +type UpdateSnapshotRequest struct { + // Zone: zone to target. If none is passed will use default zone from the config. + Zone scw.Zone `json:"-"` + + // SnapshotID: UUID of the snapshot. + SnapshotID string `json:"-"` + + // Name: when defined, is the name of the snapshot. + Name *string `json:"name,omitempty"` + + // Tags: list of tags assigned to the snapshot. + Tags *[]string `json:"tags,omitempty"` +} + +// UpdateVolumeRequest: update volume request. +type UpdateVolumeRequest struct { + // Zone: zone to target. If none is passed will use default zone from the config. + Zone scw.Zone `json:"-"` + + // VolumeID: UUID of the volume. + VolumeID string `json:"-"` + + // Name: when defined, is the new name of the volume. + Name *string `json:"name,omitempty"` + + // Size: size in bytes of the volume, with a granularity of 1 GB (10^9 bytes). + // Must be compliant with the minimum (1GB) and maximum (10TB) allowed size. + Size *scw.Size `json:"size,omitempty"` + + // Tags: list of tags assigned to the volume. + Tags *[]string `json:"tags,omitempty"` + + // PerfIops: the selected value must be available for the volume's current storage class. + PerfIops *uint32 `json:"perf_iops,omitempty"` +} + +// This API allows you to use and manage your Block Storage volumes. +type API struct { + client *scw.Client +} + +// NewAPI returns a API object from a Scaleway client. +func NewAPI(client *scw.Client) *API { + return &API{ + client: client, + } +} +func (s *API) Zones() []scw.Zone { + return []scw.Zone{scw.ZoneFrPar1, scw.ZoneFrPar2, scw.ZoneNlAms1, scw.ZoneNlAms3, scw.ZonePlWaw3} +} + +// ListVolumeTypes: List all available volume types in a specified zone. The volume types listed are ordered by name in ascending order. +func (s *API) ListVolumeTypes(req *ListVolumeTypesRequest, opts ...scw.RequestOption) (*ListVolumeTypesResponse, error) { + var err error + + if req.Zone == "" { + defaultZone, _ := s.client.GetDefaultZone() + req.Zone = defaultZone + } + + defaultPageSize, exist := s.client.GetDefaultPageSize() + if (req.PageSize == nil || *req.PageSize == 0) && exist { + req.PageSize = &defaultPageSize + } + + query := url.Values{} + parameter.AddToQuery(query, "page", req.Page) + parameter.AddToQuery(query, "page_size", req.PageSize) + + if fmt.Sprint(req.Zone) == "" { + return nil, errors.New("field Zone cannot be empty in request") + } + + scwReq := &scw.ScalewayRequest{ + Method: "GET", + Path: "/block/v1alpha1/zones/" + fmt.Sprint(req.Zone) + "/volume-types", + Query: query, + } + + var resp ListVolumeTypesResponse + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +// ListVolumes: List all existing volumes in a specified zone. By default, the volumes listed are ordered by creation date in ascending order. This can be modified via the `order_by` field. +func (s *API) ListVolumes(req *ListVolumesRequest, opts ...scw.RequestOption) (*ListVolumesResponse, error) { + var err error + + if req.Zone == "" { + defaultZone, _ := s.client.GetDefaultZone() + req.Zone = defaultZone + } + + defaultPageSize, exist := s.client.GetDefaultPageSize() + if (req.PageSize == nil || *req.PageSize == 0) && exist { + req.PageSize = &defaultPageSize + } + + query := url.Values{} + parameter.AddToQuery(query, "order_by", req.OrderBy) + parameter.AddToQuery(query, "project_id", req.ProjectID) + parameter.AddToQuery(query, "organization_id", req.OrganizationID) + parameter.AddToQuery(query, "page", req.Page) + parameter.AddToQuery(query, "page_size", req.PageSize) + parameter.AddToQuery(query, "name", req.Name) + parameter.AddToQuery(query, "product_resource_id", req.ProductResourceID) + + if fmt.Sprint(req.Zone) == "" { + return nil, errors.New("field Zone cannot be empty in request") + } + + scwReq := &scw.ScalewayRequest{ + Method: "GET", + Path: "/block/v1alpha1/zones/" + fmt.Sprint(req.Zone) + "/volumes", + Query: query, + } + + var resp ListVolumesResponse + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +// CreateVolume: To create a new volume from scratch, you must specify `from_empty` and the `size`. +// To create a volume from an existing snapshot, specify `from_snapshot` and the `snapshot_id` in the request payload instead, size is optional and can be specified if you need to extend the original size. The volume will take on the same volume class and underlying IOPS limitations as the original snapshot. +func (s *API) CreateVolume(req *CreateVolumeRequest, opts ...scw.RequestOption) (*Volume, error) { + var err error + + if req.Zone == "" { + defaultZone, _ := s.client.GetDefaultZone() + req.Zone = defaultZone + } + + if req.ProjectID == "" { + defaultProjectID, _ := s.client.GetDefaultProjectID() + req.ProjectID = defaultProjectID + } + + if fmt.Sprint(req.Zone) == "" { + return nil, errors.New("field Zone cannot be empty in request") + } + + scwReq := &scw.ScalewayRequest{ + Method: "POST", + Path: "/block/v1alpha1/zones/" + fmt.Sprint(req.Zone) + "/volumes", + } + + err = scwReq.SetBody(req) + if err != nil { + return nil, err + } + + var resp Volume + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +// GetVolume: Retrieve technical information about a specific volume. Details such as size, type, and status are returned in the response. +func (s *API) GetVolume(req *GetVolumeRequest, opts ...scw.RequestOption) (*Volume, error) { + var err error + + if req.Zone == "" { + defaultZone, _ := s.client.GetDefaultZone() + req.Zone = defaultZone + } + + if fmt.Sprint(req.Zone) == "" { + return nil, errors.New("field Zone cannot be empty in request") + } + + if fmt.Sprint(req.VolumeID) == "" { + return nil, errors.New("field VolumeID cannot be empty in request") + } + + scwReq := &scw.ScalewayRequest{ + Method: "GET", + Path: "/block/v1alpha1/zones/" + fmt.Sprint(req.Zone) + "/volumes/" + fmt.Sprint(req.VolumeID) + "", + } + + var resp Volume + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +// DeleteVolume: You must specify the `volume_id` of the volume you want to delete. The volume must not be in the `in_use` status. +func (s *API) DeleteVolume(req *DeleteVolumeRequest, opts ...scw.RequestOption) error { + var err error + + if req.Zone == "" { + defaultZone, _ := s.client.GetDefaultZone() + req.Zone = defaultZone + } + + if fmt.Sprint(req.Zone) == "" { + return errors.New("field Zone cannot be empty in request") + } + + if fmt.Sprint(req.VolumeID) == "" { + return errors.New("field VolumeID cannot be empty in request") + } + + scwReq := &scw.ScalewayRequest{ + Method: "DELETE", + Path: "/block/v1alpha1/zones/" + fmt.Sprint(req.Zone) + "/volumes/" + fmt.Sprint(req.VolumeID) + "", + } + + err = s.client.Do(scwReq, nil, opts...) + if err != nil { + return err + } + return nil +} + +// UpdateVolume: Update the technical details of a volume, such as its name, tags, or its new size and `volume_type` (within the same Block Storage class). +// You can only resize a volume to a larger size. It is currently not possible to change your Block Storage Class. +func (s *API) UpdateVolume(req *UpdateVolumeRequest, opts ...scw.RequestOption) (*Volume, error) { + var err error + + if req.Zone == "" { + defaultZone, _ := s.client.GetDefaultZone() + req.Zone = defaultZone + } + + if fmt.Sprint(req.Zone) == "" { + return nil, errors.New("field Zone cannot be empty in request") + } + + if fmt.Sprint(req.VolumeID) == "" { + return nil, errors.New("field VolumeID cannot be empty in request") + } + + scwReq := &scw.ScalewayRequest{ + Method: "PATCH", + Path: "/block/v1alpha1/zones/" + fmt.Sprint(req.Zone) + "/volumes/" + fmt.Sprint(req.VolumeID) + "", + } + + err = scwReq.SetBody(req) + if err != nil { + return nil, err + } + + var resp Volume + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +// ListSnapshots: List all available snapshots in a specified zone. By default, the snapshots listed are ordered by creation date in ascending order. This can be modified via the `order_by` field. +func (s *API) ListSnapshots(req *ListSnapshotsRequest, opts ...scw.RequestOption) (*ListSnapshotsResponse, error) { + var err error + + if req.Zone == "" { + defaultZone, _ := s.client.GetDefaultZone() + req.Zone = defaultZone + } + + defaultPageSize, exist := s.client.GetDefaultPageSize() + if (req.PageSize == nil || *req.PageSize == 0) && exist { + req.PageSize = &defaultPageSize + } + + query := url.Values{} + parameter.AddToQuery(query, "order_by", req.OrderBy) + parameter.AddToQuery(query, "project_id", req.ProjectID) + parameter.AddToQuery(query, "organization_id", req.OrganizationID) + parameter.AddToQuery(query, "page", req.Page) + parameter.AddToQuery(query, "page_size", req.PageSize) + parameter.AddToQuery(query, "volume_id", req.VolumeID) + parameter.AddToQuery(query, "name", req.Name) + + if fmt.Sprint(req.Zone) == "" { + return nil, errors.New("field Zone cannot be empty in request") + } + + scwReq := &scw.ScalewayRequest{ + Method: "GET", + Path: "/block/v1alpha1/zones/" + fmt.Sprint(req.Zone) + "/snapshots", + Query: query, + } + + var resp ListSnapshotsResponse + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +// GetSnapshot: Retrieve technical information about a specific snapshot. Details such as size, volume type, and status are returned in the response. +func (s *API) GetSnapshot(req *GetSnapshotRequest, opts ...scw.RequestOption) (*Snapshot, error) { + var err error + + if req.Zone == "" { + defaultZone, _ := s.client.GetDefaultZone() + req.Zone = defaultZone + } + + if fmt.Sprint(req.Zone) == "" { + return nil, errors.New("field Zone cannot be empty in request") + } + + if fmt.Sprint(req.SnapshotID) == "" { + return nil, errors.New("field SnapshotID cannot be empty in request") + } + + scwReq := &scw.ScalewayRequest{ + Method: "GET", + Path: "/block/v1alpha1/zones/" + fmt.Sprint(req.Zone) + "/snapshots/" + fmt.Sprint(req.SnapshotID) + "", + } + + var resp Snapshot + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +// CreateSnapshot: To create a snapshot, the volume must be in the `in_use` or the `available` status. +// If your volume is in a transient state, you need to wait until the end of the current operation. +func (s *API) CreateSnapshot(req *CreateSnapshotRequest, opts ...scw.RequestOption) (*Snapshot, error) { + var err error + + if req.Zone == "" { + defaultZone, _ := s.client.GetDefaultZone() + req.Zone = defaultZone + } + + if req.ProjectID == "" { + defaultProjectID, _ := s.client.GetDefaultProjectID() + req.ProjectID = defaultProjectID + } + + if fmt.Sprint(req.Zone) == "" { + return nil, errors.New("field Zone cannot be empty in request") + } + + scwReq := &scw.ScalewayRequest{ + Method: "POST", + Path: "/block/v1alpha1/zones/" + fmt.Sprint(req.Zone) + "/snapshots", + } + + err = scwReq.SetBody(req) + if err != nil { + return nil, err + } + + var resp Snapshot + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +// DeleteSnapshot: You must specify the `snapshot_id` of the snapshot you want to delete. The snapshot must not be in use. +func (s *API) DeleteSnapshot(req *DeleteSnapshotRequest, opts ...scw.RequestOption) error { + var err error + + if req.Zone == "" { + defaultZone, _ := s.client.GetDefaultZone() + req.Zone = defaultZone + } + + if fmt.Sprint(req.Zone) == "" { + return errors.New("field Zone cannot be empty in request") + } + + if fmt.Sprint(req.SnapshotID) == "" { + return errors.New("field SnapshotID cannot be empty in request") + } + + scwReq := &scw.ScalewayRequest{ + Method: "DELETE", + Path: "/block/v1alpha1/zones/" + fmt.Sprint(req.Zone) + "/snapshots/" + fmt.Sprint(req.SnapshotID) + "", + } + + err = s.client.Do(scwReq, nil, opts...) + if err != nil { + return err + } + return nil +} + +// UpdateSnapshot: Update the name or tags of the snapshot. +func (s *API) UpdateSnapshot(req *UpdateSnapshotRequest, opts ...scw.RequestOption) (*Snapshot, error) { + var err error + + if req.Zone == "" { + defaultZone, _ := s.client.GetDefaultZone() + req.Zone = defaultZone + } + + if fmt.Sprint(req.Zone) == "" { + return nil, errors.New("field Zone cannot be empty in request") + } + + if fmt.Sprint(req.SnapshotID) == "" { + return nil, errors.New("field SnapshotID cannot be empty in request") + } + + scwReq := &scw.ScalewayRequest{ + Method: "PATCH", + Path: "/block/v1alpha1/zones/" + fmt.Sprint(req.Zone) + "/snapshots/" + fmt.Sprint(req.SnapshotID) + "", + } + + err = scwReq.SetBody(req) + if err != nil { + return nil, err + } + + var resp Snapshot + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} diff --git a/vendor/github.com/scaleway/scaleway-sdk-go/api/block/v1alpha1/snapshot_utils.go b/vendor/github.com/scaleway/scaleway-sdk-go/api/block/v1alpha1/snapshot_utils.go new file mode 100644 index 000000000..d16f6114f --- /dev/null +++ b/vendor/github.com/scaleway/scaleway-sdk-go/api/block/v1alpha1/snapshot_utils.go @@ -0,0 +1,59 @@ +package block + +import ( + "time" + + "github.com/scaleway/scaleway-sdk-go/internal/async" + "github.com/scaleway/scaleway-sdk-go/internal/errors" + "github.com/scaleway/scaleway-sdk-go/scw" +) + +// WaitForSnapshotRequest is used by WaitForSnapshot method. +type WaitForSnapshotRequest struct { + SnapshotID string + Zone scw.Zone + Timeout *time.Duration + RetryInterval *time.Duration +} + +// WaitForSnapshot wait for the snapshot to be in a "terminal state" before returning. +func (s *API) WaitForSnapshot(req *WaitForSnapshotRequest, opts ...scw.RequestOption) (*Snapshot, error) { + timeout := defaultTimeout + if req.Timeout != nil { + timeout = *req.Timeout + } + retryInterval := defaultRetryInterval + if req.RetryInterval != nil { + retryInterval = *req.RetryInterval + } + + terminalStatus := map[SnapshotStatus]struct{}{ + SnapshotStatusAvailable: {}, + SnapshotStatusInUse: {}, + SnapshotStatusError: {}, + SnapshotStatusLocked: {}, + SnapshotStatusDeleted: {}, + } + + snapshot, err := async.WaitSync(&async.WaitSyncConfig{ + Get: func() (interface{}, bool, error) { + res, err := s.GetSnapshot(&GetSnapshotRequest{ + SnapshotID: req.SnapshotID, + Zone: req.Zone, + }, opts...) + + if err != nil { + return nil, false, err + } + _, isTerminal := terminalStatus[res.Status] + + return res, isTerminal, err + }, + Timeout: timeout, + IntervalStrategy: async.LinearIntervalStrategy(retryInterval), + }) + if err != nil { + return nil, errors.Wrap(err, "waiting for snapshot failed") + } + return snapshot.(*Snapshot), nil +} diff --git a/vendor/github.com/scaleway/scaleway-sdk-go/api/block/v1alpha1/volume_utils.go b/vendor/github.com/scaleway/scaleway-sdk-go/api/block/v1alpha1/volume_utils.go new file mode 100644 index 000000000..229b4111d --- /dev/null +++ b/vendor/github.com/scaleway/scaleway-sdk-go/api/block/v1alpha1/volume_utils.go @@ -0,0 +1,149 @@ +package block + +import ( + "time" + + "github.com/scaleway/scaleway-sdk-go/internal/async" + "github.com/scaleway/scaleway-sdk-go/internal/errors" + "github.com/scaleway/scaleway-sdk-go/scw" +) + +const ( + defaultTimeout = 5 * time.Minute + defaultRetryInterval = 5 * time.Second +) + +// WaitForVolumeRequest is used by WaitForVolume method. +type WaitForVolumeRequest struct { + VolumeID string + Zone scw.Zone + Timeout *time.Duration + RetryInterval *time.Duration + + // If set, will wait until this specific status has been reached or the + // volume has an error status. This is useful when we need to wait for + // the volume to transition from "in_use" to "available". + TerminalStatus *VolumeStatus +} + +// WaitForVolume waits for the volume to be in a "terminal state" before returning. +func (s *API) WaitForVolume(req *WaitForVolumeRequest, opts ...scw.RequestOption) (*Volume, error) { + timeout := defaultTimeout + if req.Timeout != nil { + timeout = *req.Timeout + } + retryInterval := defaultRetryInterval + if req.RetryInterval != nil { + retryInterval = *req.RetryInterval + } + + terminalStatus := map[VolumeStatus]struct{}{ + VolumeStatusError: {}, + VolumeStatusLocked: {}, + VolumeStatusDeleted: {}, + } + + if req.TerminalStatus != nil { + terminalStatus[*req.TerminalStatus] = struct{}{} + } else { + terminalStatus[VolumeStatusAvailable] = struct{}{} + terminalStatus[VolumeStatusInUse] = struct{}{} + } + + volume, err := async.WaitSync(&async.WaitSyncConfig{ + Get: func() (interface{}, bool, error) { + res, err := s.GetVolume(&GetVolumeRequest{ + VolumeID: req.VolumeID, + Zone: req.Zone, + }, opts...) + + if err != nil { + return nil, false, err + } + _, isTerminal := terminalStatus[res.Status] + + return res, isTerminal, err + }, + Timeout: timeout, + IntervalStrategy: async.LinearIntervalStrategy(retryInterval), + }) + if err != nil { + return nil, errors.Wrap(err, "waiting for volume failed") + } + return volume.(*Volume), nil +} + +// WaitForVolumeAndReferencesRequest is used by WaitForVolumeAndReferences method. +type WaitForVolumeAndReferencesRequest struct { + VolumeID string + Zone scw.Zone + Timeout *time.Duration + RetryInterval *time.Duration + + VolumeTerminalStatus *VolumeStatus + ReferenceTerminalStatus *ReferenceStatus +} + +// WaitForVolumeAndReferences waits for the volume and its references to be in a "terminal state" before returning. +func (s *API) WaitForVolumeAndReferences(req *WaitForVolumeAndReferencesRequest, opts ...scw.RequestOption) (*Volume, error) { + timeout := defaultTimeout + if req.Timeout != nil { + timeout = *req.Timeout + } + retryInterval := defaultRetryInterval + if req.RetryInterval != nil { + retryInterval = *req.RetryInterval + } + + terminalStatus := map[VolumeStatus]struct{}{ + VolumeStatusError: {}, + VolumeStatusLocked: {}, + VolumeStatusDeleted: {}, + } + if req.VolumeTerminalStatus != nil { + terminalStatus[*req.VolumeTerminalStatus] = struct{}{} + } else { + terminalStatus[VolumeStatusAvailable] = struct{}{} + terminalStatus[VolumeStatusInUse] = struct{}{} + } + + referenceTerminalStatus := map[ReferenceStatus]struct{}{ + ReferenceStatusError: {}, + } + if req.ReferenceTerminalStatus != nil { + referenceTerminalStatus[*req.ReferenceTerminalStatus] = struct{}{} + } else { + referenceTerminalStatus[ReferenceStatusAttached] = struct{}{} + referenceTerminalStatus[ReferenceStatusDetached] = struct{}{} + } + + volume, err := async.WaitSync(&async.WaitSyncConfig{ + Get: func() (interface{}, bool, error) { + volume, err := s.GetVolume(&GetVolumeRequest{ + VolumeID: req.VolumeID, + Zone: req.Zone, + }, opts...) + if err != nil { + return nil, false, err + } + + referencesAreTerminal := true + + for _, reference := range volume.References { + _, referenceIsTerminal := referenceTerminalStatus[reference.Status] + referencesAreTerminal = referencesAreTerminal && referenceIsTerminal + } + + _, isTerminal := terminalStatus[volume.Status] + + return volume, isTerminal && referencesAreTerminal, nil + }, + Timeout: timeout, + IntervalStrategy: async.LinearIntervalStrategy(retryInterval), + }) + if err != nil { + return nil, errors.Wrap(err, "waiting for Volume failed") + } + + return volume.(*Volume), nil +} diff --git a/vendor/github.com/scaleway/scaleway-sdk-go/api/instance/v1/image_utils.go b/vendor/github.com/scaleway/scaleway-sdk-go/api/instance/v1/image_utils.go new file mode 100644 index 000000000..99538fdff --- /dev/null +++ b/vendor/github.com/scaleway/scaleway-sdk-go/api/instance/v1/image_utils.go @@ -0,0 +1,56 @@ +package instance + +import ( + "time" + + "github.com/scaleway/scaleway-sdk-go/internal/async" + "github.com/scaleway/scaleway-sdk-go/internal/errors" + "github.com/scaleway/scaleway-sdk-go/scw" +) + +// WaitForImageRequest is used by WaitForImage method. +type WaitForImageRequest struct { + ImageID string + Zone scw.Zone + Timeout *time.Duration + RetryInterval *time.Duration +} + +// WaitForImage wait for the image to be in a "terminal state" before returning. +func (s *API) WaitForImage(req *WaitForImageRequest, opts ...scw.RequestOption) (*Image, error) { + timeout := defaultTimeout + if req.Timeout != nil { + timeout = *req.Timeout + } + retryInterval := defaultRetryInterval + if req.RetryInterval != nil { + retryInterval = *req.RetryInterval + } + + terminalStatus := map[ImageState]struct{}{ + ImageStateAvailable: {}, + ImageStateError: {}, + } + + image, err := async.WaitSync(&async.WaitSyncConfig{ + Get: func() (interface{}, bool, error) { + res, err := s.GetImage(&GetImageRequest{ + ImageID: req.ImageID, + Zone: req.Zone, + }, opts...) + + if err != nil { + return nil, false, err + } + _, isTerminal := terminalStatus[res.Image.State] + + return res.Image, isTerminal, err + }, + Timeout: timeout, + IntervalStrategy: async.LinearIntervalStrategy(retryInterval), + }) + if err != nil { + return nil, errors.Wrap(err, "waiting for image failed") + } + return image.(*Image), nil +} diff --git a/vendor/github.com/scaleway/scaleway-sdk-go/api/instance/v1/instance_metadata_sdk.go b/vendor/github.com/scaleway/scaleway-sdk-go/api/instance/v1/instance_metadata_sdk.go new file mode 100644 index 000000000..6bf35d64b --- /dev/null +++ b/vendor/github.com/scaleway/scaleway-sdk-go/api/instance/v1/instance_metadata_sdk.go @@ -0,0 +1,319 @@ +package instance + +import ( + "bytes" + "encoding/json" + "io/ioutil" + "math/rand" + "net" + "net/http" + "strconv" + "time" + + "github.com/scaleway/scaleway-sdk-go/internal/errors" +) + +var ( + metadataRetryBindPort = 200 +) + +const metadataAPIv4 = "http://169.254.42.42" +const metadataAPIv6 = "http://[fd00:42::42]" + +// MetadataAPI metadata API +type MetadataAPI struct { + MetadataURL *string +} + +// NewMetadataAPI returns a MetadataAPI object from a Scaleway client. +func NewMetadataAPI() *MetadataAPI { + return &MetadataAPI{} +} + +func (meta *MetadataAPI) getMetadataUrl() string { + if meta.MetadataURL != nil { + return *meta.MetadataURL + } + + for _, url := range []string{metadataAPIv4, metadataAPIv6} { + http.DefaultClient.Timeout = 3 * time.Second + resp, err := http.Get(url) + if err == nil && resp.StatusCode == 200 { + meta.MetadataURL = &url + return url + } + } + return metadataAPIv4 +} + +// GetMetadata returns the metadata available from the server +func (meta *MetadataAPI) GetMetadata() (m *Metadata, err error) { + resp, err := http.Get(meta.getMetadataUrl() + "/conf?format=json") + if err != nil { + return nil, errors.Wrap(err, "error getting metadataURL") + } + defer resp.Body.Close() + + metadata := &Metadata{} + err = json.NewDecoder(resp.Body).Decode(metadata) + if err != nil { + return nil, errors.Wrap(err, "error decoding metadata") + } + return metadata, nil +} + +// MetadataIP represents all public IPs attached +type MetadataIP struct { + ID string `json:"id"` + Address string `json:"address"` + Dynamic bool `json:"dynamic"` + Gateway string `json:"gateway"` + Netmask string `json:"netmask"` + Family string `json:"family"` + ProvisioningMode string `json:"provisioning_mode"` + Tags []string `json:"tags"` +} + +// Metadata represents the struct return by the metadata API +type Metadata struct { + ID string `json:"id,omitempty"` + Name string `json:"name,omitempty"` + Hostname string `json:"hostname,omitempty"` + Organization string `json:"organization,omitempty"` + Project string `json:"project,omitempty"` + CommercialType string `json:"commercial_type,omitempty"` + //PublicIP IPv4 only + PublicIP struct { + ID string `json:"id"` + Address string `json:"address"` + Dynamic bool `json:"dynamic"` + Gateway string `json:"gateway"` + Netmask string `json:"netmask"` + Family string `json:"family"` + ProvisioningMode string `json:"provisioning_mode"` + } `json:"public_ip,omitempty"` + PublicIpsV4 []MetadataIP `json:"public_ips_v4,omitempty"` + PublicIpsV6 []MetadataIP `json:"public_ips_v6,omitempty"` + PrivateIP string `json:"private_ip,omitempty"` + IPv6 struct { + Netmask string `json:"netmask,omitempty"` + Gateway string `json:"gateway,omitempty"` + Address string `json:"address,omitempty"` + } `json:"ipv6,omitempty"` + Location struct { + PlatformID string `json:"platform_id,omitempty"` + HypervisorID string `json:"hypervisor_id,omitempty"` + NodeID string `json:"node_id,omitempty"` + ClusterID string `json:"cluster_id,omitempty"` + ZoneID string `json:"zone_id,omitempty"` + } `json:"location,omitempty"` + Tags []string `json:"tags,omitempty"` + StateDetail string `json:"state_detail,omitempty"` + SSHPublicKeys []struct { + Description string `json:"description,omitempty"` + ModificationDate string `json:"modification_date,omitempty"` + IP string `json:"ip,omitempty"` + Key string `json:"key,omitempty"` + Fingerprint string `json:"fingerprint,omitempty"` + ID string `json:"id,omitempty"` + CreationDate string `json:"creation_date,omitempty"` + } `json:"ssh_public_keys,omitempty"` + Timezone string `json:"timezone,omitempty"` + Bootscript struct { + Kernel string `json:"kernel,omitempty"` + Title string `json:"title,omitempty"` + Default bool `json:"default,omitempty"` + Dtb string `json:"dtb,omitempty"` + Public bool `json:"publc,omitempty"` + Initrd string `json:"initrd,omitempty"` + Bootcmdargs string `json:"bootcmdargs,omitempty"` + Architecture string `json:"architecture,omitempty"` + Organization string `json:"organization,omitempty"` + Project string `json:"project,omitempty"` + ID string `json:"id,omitempty"` + } `json:"bootscript,omitempty"` + Volumes map[string]struct { + Name string `json:"name,omitempty"` + ModificationDate string `json:"modification_date,omitempty"` + ExportURI string `json:"export_uri,omitempty"` + VolumeType string `json:"volume_type,omitempty"` + CreationDate string `json:"creation_date,omitempty"` + State string `json:"state,omitempty"` + Organization string `json:"organization,omitempty"` + Project string `json:"project,omitempty"` + Server struct { + ID string `json:"id,omitempty"` + Name string `json:"name,omitempty"` + } `json:"server,omitempty"` + ID string `json:"id,omitempty"` + Size int `json:"size,omitempty"` + } `json:"volumes,omitempty"` + PrivateNICs []struct { + ID string `json:"id,omitempty"` + PrivateNetworkID string `json:"private_network_id,omitempty"` + ServerID string `json:"server_id,omitempty"` + MacAddress string `json:"mac_address,omitempty"` + CreationDate string `json:"creation_date,omitempty"` + Zone string `json:"zone,omitempty"` + } `json:"private_nics,omitempty"` +} + +// ListUserData returns the metadata available from the server +func (meta *MetadataAPI) ListUserData() (res *UserData, err error) { + retries := 0 + for retries <= metadataRetryBindPort { + port := rand.Intn(1024) + localTCPAddr, err := net.ResolveTCPAddr("tcp", ":"+strconv.Itoa(port)) + if err != nil { + return nil, errors.Wrap(err, "error resolving tcp address") + } + + userdataClient := &http.Client{ + Transport: &http.Transport{ + DialContext: (&net.Dialer{ + LocalAddr: localTCPAddr, + DualStack: false, + FallbackDelay: time.Second * -1, + }).DialContext, + }, + } + + resp, err := userdataClient.Get(meta.getMetadataUrl() + "/user_data?format=json") + if err != nil { + retries++ // retry with a different source port + continue + } + defer resp.Body.Close() + + userdata := &UserData{} + err = json.NewDecoder(resp.Body).Decode(userdata) + if err != nil { + return nil, errors.Wrap(err, "error decoding userdata") + } + return userdata, nil + } + return nil, errors.New("too many bind port retries for ListUserData") +} + +// GetUserData returns the value for the given metadata key +func (meta *MetadataAPI) GetUserData(key string) ([]byte, error) { + if key == "" { + return make([]byte, 0), errors.New("key must not be empty in GetUserData") + } + + retries := 0 + for retries <= metadataRetryBindPort { + port := rand.Intn(1024) + localTCPAddr, err := net.ResolveTCPAddr("tcp", ":"+strconv.Itoa(port)) + if err != nil { + return make([]byte, 0), errors.Wrap(err, "error resolving tcp address") + } + + userdataClient := &http.Client{ + Transport: &http.Transport{ + DialContext: (&net.Dialer{ + LocalAddr: localTCPAddr, + DualStack: false, + FallbackDelay: time.Second * -1, + }).DialContext, + }, + } + + resp, err := userdataClient.Get(meta.getMetadataUrl() + "/user_data/" + key) + if err != nil { + retries++ // retry with a different source port + continue + } + defer resp.Body.Close() + + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return make([]byte, 0), errors.Wrap(err, "error reading userdata body") + } + + return body, nil + } + return make([]byte, 0), errors.New("too may bind port retries for GetUserData") +} + +// SetUserData sets the userdata key with the given value +func (meta *MetadataAPI) SetUserData(key string, value []byte) error { + if key == "" { + return errors.New("key must not be empty in SetUserData") + } + + retries := 0 + for retries <= metadataRetryBindPort { + port := rand.Intn(1024) + localTCPAddr, err := net.ResolveTCPAddr("tcp", ":"+strconv.Itoa(port)) + if err != nil { + return errors.Wrap(err, "error resolving tcp address") + } + + userdataClient := &http.Client{ + Transport: &http.Transport{ + DialContext: (&net.Dialer{ + LocalAddr: localTCPAddr, + DualStack: false, + FallbackDelay: time.Second * -1, + }).DialContext, + }, + } + request, err := http.NewRequest("PATCH", meta.getMetadataUrl()+"/user_data/"+key, bytes.NewBuffer(value)) + if err != nil { + return errors.Wrap(err, "error creating patch userdata request") + } + request.Header.Set("Content-Type", "text/plain") + _, err = userdataClient.Do(request) + if err != nil { + retries++ // retry with a different source port + continue + } + + return nil + } + return errors.New("too may bind port retries for SetUserData") +} + +// DeleteUserData deletes the userdata key and the associated value +func (meta *MetadataAPI) DeleteUserData(key string) error { + if key == "" { + return errors.New("key must not be empty in DeleteUserData") + } + + retries := 0 + for retries <= metadataRetryBindPort { + port := rand.Intn(1024) + localTCPAddr, err := net.ResolveTCPAddr("tcp", ":"+strconv.Itoa(port)) + if err != nil { + return errors.Wrap(err, "error resolving tcp address") + } + + userdataClient := &http.Client{ + Transport: &http.Transport{ + DialContext: (&net.Dialer{ + LocalAddr: localTCPAddr, + DualStack: false, + FallbackDelay: time.Second * -1, + }).DialContext, + }, + } + request, err := http.NewRequest("DELETE", meta.getMetadataUrl()+"/user_data/"+key, bytes.NewBuffer([]byte(""))) + if err != nil { + return errors.Wrap(err, "error creating delete userdata request") + } + _, err = userdataClient.Do(request) + if err != nil { + retries++ // retry with a different source port + continue + } + + return nil + } + return errors.New("too may bind port retries for DeleteUserData") +} + +// UserData represents the user data +type UserData struct { + UserData []string `json:"user_data,omitempty"` +} diff --git a/vendor/github.com/scaleway/scaleway-sdk-go/api/instance/v1/instance_sdk.go b/vendor/github.com/scaleway/scaleway-sdk-go/api/instance/v1/instance_sdk.go new file mode 100644 index 000000000..10ba03753 --- /dev/null +++ b/vendor/github.com/scaleway/scaleway-sdk-go/api/instance/v1/instance_sdk.go @@ -0,0 +1,6498 @@ +// This file was automatically generated. DO NOT EDIT. +// If you have any remark or suggestion do not hesitate to open an issue. + +// Package instance provides methods and message types of the instance v1 API. +package instance + +import ( + "bytes" + "encoding/json" + "fmt" + "net" + "net/http" + "net/url" + "strings" + "time" + + "github.com/scaleway/scaleway-sdk-go/internal/errors" + "github.com/scaleway/scaleway-sdk-go/internal/marshaler" + "github.com/scaleway/scaleway-sdk-go/internal/parameter" + "github.com/scaleway/scaleway-sdk-go/namegenerator" + "github.com/scaleway/scaleway-sdk-go/scw" +) + +// always import dependencies +var ( + _ fmt.Stringer + _ json.Unmarshaler + _ url.URL + _ net.IP + _ http.Header + _ bytes.Reader + _ time.Time + _ = strings.Join + + _ scw.ScalewayRequest + _ marshaler.Duration + _ scw.File + _ = parameter.AddToQuery + _ = namegenerator.GetRandomName +) + +type Arch string + +const ( + ArchUnknownArch = Arch("unknown_arch") + ArchX86_64 = Arch("x86_64") + ArchArm = Arch("arm") + ArchArm64 = Arch("arm64") +) + +func (enum Arch) String() string { + if enum == "" { + // return default value if empty + return "unknown_arch" + } + return string(enum) +} + +func (enum Arch) MarshalJSON() ([]byte, error) { + return []byte(fmt.Sprintf(`"%s"`, enum)), nil +} + +func (enum *Arch) UnmarshalJSON(data []byte) error { + tmp := "" + + if err := json.Unmarshal(data, &tmp); err != nil { + return err + } + + *enum = Arch(Arch(tmp).String()) + return nil +} + +type AttachServerVolumeRequestVolumeType string + +const ( + AttachServerVolumeRequestVolumeTypeUnknownVolumeType = AttachServerVolumeRequestVolumeType("unknown_volume_type") + AttachServerVolumeRequestVolumeTypeLSSD = AttachServerVolumeRequestVolumeType("l_ssd") + AttachServerVolumeRequestVolumeTypeBSSD = AttachServerVolumeRequestVolumeType("b_ssd") + AttachServerVolumeRequestVolumeTypeSbsVolume = AttachServerVolumeRequestVolumeType("sbs_volume") +) + +func (enum AttachServerVolumeRequestVolumeType) String() string { + if enum == "" { + // return default value if empty + return "unknown_volume_type" + } + return string(enum) +} + +func (enum AttachServerVolumeRequestVolumeType) MarshalJSON() ([]byte, error) { + return []byte(fmt.Sprintf(`"%s"`, enum)), nil +} + +func (enum *AttachServerVolumeRequestVolumeType) UnmarshalJSON(data []byte) error { + tmp := "" + + if err := json.Unmarshal(data, &tmp); err != nil { + return err + } + + *enum = AttachServerVolumeRequestVolumeType(AttachServerVolumeRequestVolumeType(tmp).String()) + return nil +} + +type BootType string + +const ( + BootTypeLocal = BootType("local") + BootTypeBootscript = BootType("bootscript") + BootTypeRescue = BootType("rescue") +) + +func (enum BootType) String() string { + if enum == "" { + // return default value if empty + return "local" + } + return string(enum) +} + +func (enum BootType) MarshalJSON() ([]byte, error) { + return []byte(fmt.Sprintf(`"%s"`, enum)), nil +} + +func (enum *BootType) UnmarshalJSON(data []byte) error { + tmp := "" + + if err := json.Unmarshal(data, &tmp); err != nil { + return err + } + + *enum = BootType(BootType(tmp).String()) + return nil +} + +type IPState string + +const ( + IPStateUnknownState = IPState("unknown_state") + IPStateDetached = IPState("detached") + IPStateAttached = IPState("attached") + IPStatePending = IPState("pending") + IPStateError = IPState("error") +) + +func (enum IPState) String() string { + if enum == "" { + // return default value if empty + return "unknown_state" + } + return string(enum) +} + +func (enum IPState) MarshalJSON() ([]byte, error) { + return []byte(fmt.Sprintf(`"%s"`, enum)), nil +} + +func (enum *IPState) UnmarshalJSON(data []byte) error { + tmp := "" + + if err := json.Unmarshal(data, &tmp); err != nil { + return err + } + + *enum = IPState(IPState(tmp).String()) + return nil +} + +type IPType string + +const ( + IPTypeUnknownIptype = IPType("unknown_iptype") + IPTypeNat = IPType("nat") + IPTypeRoutedIPv4 = IPType("routed_ipv4") + IPTypeRoutedIPv6 = IPType("routed_ipv6") +) + +func (enum IPType) String() string { + if enum == "" { + // return default value if empty + return "unknown_iptype" + } + return string(enum) +} + +func (enum IPType) MarshalJSON() ([]byte, error) { + return []byte(fmt.Sprintf(`"%s"`, enum)), nil +} + +func (enum *IPType) UnmarshalJSON(data []byte) error { + tmp := "" + + if err := json.Unmarshal(data, &tmp); err != nil { + return err + } + + *enum = IPType(IPType(tmp).String()) + return nil +} + +type ImageState string + +const ( + ImageStateAvailable = ImageState("available") + ImageStateCreating = ImageState("creating") + ImageStateError = ImageState("error") +) + +func (enum ImageState) String() string { + if enum == "" { + // return default value if empty + return "available" + } + return string(enum) +} + +func (enum ImageState) MarshalJSON() ([]byte, error) { + return []byte(fmt.Sprintf(`"%s"`, enum)), nil +} + +func (enum *ImageState) UnmarshalJSON(data []byte) error { + tmp := "" + + if err := json.Unmarshal(data, &tmp); err != nil { + return err + } + + *enum = ImageState(ImageState(tmp).String()) + return nil +} + +type ListServersRequestOrder string + +const ( + ListServersRequestOrderCreationDateDesc = ListServersRequestOrder("creation_date_desc") + ListServersRequestOrderCreationDateAsc = ListServersRequestOrder("creation_date_asc") + ListServersRequestOrderModificationDateDesc = ListServersRequestOrder("modification_date_desc") + ListServersRequestOrderModificationDateAsc = ListServersRequestOrder("modification_date_asc") +) + +func (enum ListServersRequestOrder) String() string { + if enum == "" { + // return default value if empty + return "creation_date_desc" + } + return string(enum) +} + +func (enum ListServersRequestOrder) MarshalJSON() ([]byte, error) { + return []byte(fmt.Sprintf(`"%s"`, enum)), nil +} + +func (enum *ListServersRequestOrder) UnmarshalJSON(data []byte) error { + tmp := "" + + if err := json.Unmarshal(data, &tmp); err != nil { + return err + } + + *enum = ListServersRequestOrder(ListServersRequestOrder(tmp).String()) + return nil +} + +type PlacementGroupPolicyMode string + +const ( + PlacementGroupPolicyModeOptional = PlacementGroupPolicyMode("optional") + PlacementGroupPolicyModeEnforced = PlacementGroupPolicyMode("enforced") +) + +func (enum PlacementGroupPolicyMode) String() string { + if enum == "" { + // return default value if empty + return "optional" + } + return string(enum) +} + +func (enum PlacementGroupPolicyMode) MarshalJSON() ([]byte, error) { + return []byte(fmt.Sprintf(`"%s"`, enum)), nil +} + +func (enum *PlacementGroupPolicyMode) UnmarshalJSON(data []byte) error { + tmp := "" + + if err := json.Unmarshal(data, &tmp); err != nil { + return err + } + + *enum = PlacementGroupPolicyMode(PlacementGroupPolicyMode(tmp).String()) + return nil +} + +type PlacementGroupPolicyType string + +const ( + PlacementGroupPolicyTypeMaxAvailability = PlacementGroupPolicyType("max_availability") + PlacementGroupPolicyTypeLowLatency = PlacementGroupPolicyType("low_latency") +) + +func (enum PlacementGroupPolicyType) String() string { + if enum == "" { + // return default value if empty + return "max_availability" + } + return string(enum) +} + +func (enum PlacementGroupPolicyType) MarshalJSON() ([]byte, error) { + return []byte(fmt.Sprintf(`"%s"`, enum)), nil +} + +func (enum *PlacementGroupPolicyType) UnmarshalJSON(data []byte) error { + tmp := "" + + if err := json.Unmarshal(data, &tmp); err != nil { + return err + } + + *enum = PlacementGroupPolicyType(PlacementGroupPolicyType(tmp).String()) + return nil +} + +type PrivateNICState string + +const ( + PrivateNICStateAvailable = PrivateNICState("available") + PrivateNICStateSyncing = PrivateNICState("syncing") + PrivateNICStateSyncingError = PrivateNICState("syncing_error") +) + +func (enum PrivateNICState) String() string { + if enum == "" { + // return default value if empty + return "available" + } + return string(enum) +} + +func (enum PrivateNICState) MarshalJSON() ([]byte, error) { + return []byte(fmt.Sprintf(`"%s"`, enum)), nil +} + +func (enum *PrivateNICState) UnmarshalJSON(data []byte) error { + tmp := "" + + if err := json.Unmarshal(data, &tmp); err != nil { + return err + } + + *enum = PrivateNICState(PrivateNICState(tmp).String()) + return nil +} + +type SecurityGroupPolicy string + +const ( + SecurityGroupPolicyUnknownPolicy = SecurityGroupPolicy("unknown_policy") + SecurityGroupPolicyAccept = SecurityGroupPolicy("accept") + SecurityGroupPolicyDrop = SecurityGroupPolicy("drop") +) + +func (enum SecurityGroupPolicy) String() string { + if enum == "" { + // return default value if empty + return "unknown_policy" + } + return string(enum) +} + +func (enum SecurityGroupPolicy) MarshalJSON() ([]byte, error) { + return []byte(fmt.Sprintf(`"%s"`, enum)), nil +} + +func (enum *SecurityGroupPolicy) UnmarshalJSON(data []byte) error { + tmp := "" + + if err := json.Unmarshal(data, &tmp); err != nil { + return err + } + + *enum = SecurityGroupPolicy(SecurityGroupPolicy(tmp).String()) + return nil +} + +type SecurityGroupRuleAction string + +const ( + SecurityGroupRuleActionUnknownAction = SecurityGroupRuleAction("unknown_action") + SecurityGroupRuleActionAccept = SecurityGroupRuleAction("accept") + SecurityGroupRuleActionDrop = SecurityGroupRuleAction("drop") +) + +func (enum SecurityGroupRuleAction) String() string { + if enum == "" { + // return default value if empty + return "unknown_action" + } + return string(enum) +} + +func (enum SecurityGroupRuleAction) MarshalJSON() ([]byte, error) { + return []byte(fmt.Sprintf(`"%s"`, enum)), nil +} + +func (enum *SecurityGroupRuleAction) UnmarshalJSON(data []byte) error { + tmp := "" + + if err := json.Unmarshal(data, &tmp); err != nil { + return err + } + + *enum = SecurityGroupRuleAction(SecurityGroupRuleAction(tmp).String()) + return nil +} + +type SecurityGroupRuleDirection string + +const ( + SecurityGroupRuleDirectionUnknownDirection = SecurityGroupRuleDirection("unknown_direction") + SecurityGroupRuleDirectionInbound = SecurityGroupRuleDirection("inbound") + SecurityGroupRuleDirectionOutbound = SecurityGroupRuleDirection("outbound") +) + +func (enum SecurityGroupRuleDirection) String() string { + if enum == "" { + // return default value if empty + return "unknown_direction" + } + return string(enum) +} + +func (enum SecurityGroupRuleDirection) MarshalJSON() ([]byte, error) { + return []byte(fmt.Sprintf(`"%s"`, enum)), nil +} + +func (enum *SecurityGroupRuleDirection) UnmarshalJSON(data []byte) error { + tmp := "" + + if err := json.Unmarshal(data, &tmp); err != nil { + return err + } + + *enum = SecurityGroupRuleDirection(SecurityGroupRuleDirection(tmp).String()) + return nil +} + +type SecurityGroupRuleProtocol string + +const ( + SecurityGroupRuleProtocolUnknownProtocol = SecurityGroupRuleProtocol("unknown_protocol") + SecurityGroupRuleProtocolTCP = SecurityGroupRuleProtocol("TCP") + SecurityGroupRuleProtocolUDP = SecurityGroupRuleProtocol("UDP") + SecurityGroupRuleProtocolICMP = SecurityGroupRuleProtocol("ICMP") + SecurityGroupRuleProtocolANY = SecurityGroupRuleProtocol("ANY") +) + +func (enum SecurityGroupRuleProtocol) String() string { + if enum == "" { + // return default value if empty + return "unknown_protocol" + } + return string(enum) +} + +func (enum SecurityGroupRuleProtocol) MarshalJSON() ([]byte, error) { + return []byte(fmt.Sprintf(`"%s"`, enum)), nil +} + +func (enum *SecurityGroupRuleProtocol) UnmarshalJSON(data []byte) error { + tmp := "" + + if err := json.Unmarshal(data, &tmp); err != nil { + return err + } + + *enum = SecurityGroupRuleProtocol(SecurityGroupRuleProtocol(tmp).String()) + return nil +} + +type SecurityGroupState string + +const ( + SecurityGroupStateAvailable = SecurityGroupState("available") + SecurityGroupStateSyncing = SecurityGroupState("syncing") + SecurityGroupStateSyncingError = SecurityGroupState("syncing_error") +) + +func (enum SecurityGroupState) String() string { + if enum == "" { + // return default value if empty + return "available" + } + return string(enum) +} + +func (enum SecurityGroupState) MarshalJSON() ([]byte, error) { + return []byte(fmt.Sprintf(`"%s"`, enum)), nil +} + +func (enum *SecurityGroupState) UnmarshalJSON(data []byte) error { + tmp := "" + + if err := json.Unmarshal(data, &tmp); err != nil { + return err + } + + *enum = SecurityGroupState(SecurityGroupState(tmp).String()) + return nil +} + +type ServerAction string + +const ( + ServerActionPoweron = ServerAction("poweron") + ServerActionBackup = ServerAction("backup") + ServerActionStopInPlace = ServerAction("stop_in_place") + ServerActionPoweroff = ServerAction("poweroff") + ServerActionTerminate = ServerAction("terminate") + ServerActionReboot = ServerAction("reboot") + ServerActionEnableRoutedIP = ServerAction("enable_routed_ip") +) + +func (enum ServerAction) String() string { + if enum == "" { + // return default value if empty + return "poweron" + } + return string(enum) +} + +func (enum ServerAction) MarshalJSON() ([]byte, error) { + return []byte(fmt.Sprintf(`"%s"`, enum)), nil +} + +func (enum *ServerAction) UnmarshalJSON(data []byte) error { + tmp := "" + + if err := json.Unmarshal(data, &tmp); err != nil { + return err + } + + *enum = ServerAction(ServerAction(tmp).String()) + return nil +} + +type ServerIPIPFamily string + +const ( + ServerIPIPFamilyInet = ServerIPIPFamily("inet") + ServerIPIPFamilyInet6 = ServerIPIPFamily("inet6") +) + +func (enum ServerIPIPFamily) String() string { + if enum == "" { + // return default value if empty + return "inet" + } + return string(enum) +} + +func (enum ServerIPIPFamily) MarshalJSON() ([]byte, error) { + return []byte(fmt.Sprintf(`"%s"`, enum)), nil +} + +func (enum *ServerIPIPFamily) UnmarshalJSON(data []byte) error { + tmp := "" + + if err := json.Unmarshal(data, &tmp); err != nil { + return err + } + + *enum = ServerIPIPFamily(ServerIPIPFamily(tmp).String()) + return nil +} + +type ServerIPProvisioningMode string + +const ( + ServerIPProvisioningModeManual = ServerIPProvisioningMode("manual") + ServerIPProvisioningModeDHCP = ServerIPProvisioningMode("dhcp") + ServerIPProvisioningModeSlaac = ServerIPProvisioningMode("slaac") +) + +func (enum ServerIPProvisioningMode) String() string { + if enum == "" { + // return default value if empty + return "manual" + } + return string(enum) +} + +func (enum ServerIPProvisioningMode) MarshalJSON() ([]byte, error) { + return []byte(fmt.Sprintf(`"%s"`, enum)), nil +} + +func (enum *ServerIPProvisioningMode) UnmarshalJSON(data []byte) error { + tmp := "" + + if err := json.Unmarshal(data, &tmp); err != nil { + return err + } + + *enum = ServerIPProvisioningMode(ServerIPProvisioningMode(tmp).String()) + return nil +} + +type ServerIPState string + +const ( + ServerIPStateUnknownState = ServerIPState("unknown_state") + ServerIPStateDetached = ServerIPState("detached") + ServerIPStateAttached = ServerIPState("attached") + ServerIPStatePending = ServerIPState("pending") + ServerIPStateError = ServerIPState("error") +) + +func (enum ServerIPState) String() string { + if enum == "" { + // return default value if empty + return "unknown_state" + } + return string(enum) +} + +func (enum ServerIPState) MarshalJSON() ([]byte, error) { + return []byte(fmt.Sprintf(`"%s"`, enum)), nil +} + +func (enum *ServerIPState) UnmarshalJSON(data []byte) error { + tmp := "" + + if err := json.Unmarshal(data, &tmp); err != nil { + return err + } + + *enum = ServerIPState(ServerIPState(tmp).String()) + return nil +} + +type ServerState string + +const ( + ServerStateRunning = ServerState("running") + ServerStateStopped = ServerState("stopped") + ServerStateStoppedInPlace = ServerState("stopped in place") + ServerStateStarting = ServerState("starting") + ServerStateStopping = ServerState("stopping") + ServerStateLocked = ServerState("locked") +) + +func (enum ServerState) String() string { + if enum == "" { + // return default value if empty + return "running" + } + return string(enum) +} + +func (enum ServerState) MarshalJSON() ([]byte, error) { + return []byte(fmt.Sprintf(`"%s"`, enum)), nil +} + +func (enum *ServerState) UnmarshalJSON(data []byte) error { + tmp := "" + + if err := json.Unmarshal(data, &tmp); err != nil { + return err + } + + *enum = ServerState(ServerState(tmp).String()) + return nil +} + +type ServerTypesAvailability string + +const ( + ServerTypesAvailabilityAvailable = ServerTypesAvailability("available") + ServerTypesAvailabilityScarce = ServerTypesAvailability("scarce") + ServerTypesAvailabilityShortage = ServerTypesAvailability("shortage") +) + +func (enum ServerTypesAvailability) String() string { + if enum == "" { + // return default value if empty + return "available" + } + return string(enum) +} + +func (enum ServerTypesAvailability) MarshalJSON() ([]byte, error) { + return []byte(fmt.Sprintf(`"%s"`, enum)), nil +} + +func (enum *ServerTypesAvailability) UnmarshalJSON(data []byte) error { + tmp := "" + + if err := json.Unmarshal(data, &tmp); err != nil { + return err + } + + *enum = ServerTypesAvailability(ServerTypesAvailability(tmp).String()) + return nil +} + +type SnapshotState string + +const ( + SnapshotStateAvailable = SnapshotState("available") + SnapshotStateSnapshotting = SnapshotState("snapshotting") + SnapshotStateError = SnapshotState("error") + SnapshotStateInvalidData = SnapshotState("invalid_data") + SnapshotStateImporting = SnapshotState("importing") + SnapshotStateExporting = SnapshotState("exporting") +) + +func (enum SnapshotState) String() string { + if enum == "" { + // return default value if empty + return "available" + } + return string(enum) +} + +func (enum SnapshotState) MarshalJSON() ([]byte, error) { + return []byte(fmt.Sprintf(`"%s"`, enum)), nil +} + +func (enum *SnapshotState) UnmarshalJSON(data []byte) error { + tmp := "" + + if err := json.Unmarshal(data, &tmp); err != nil { + return err + } + + *enum = SnapshotState(SnapshotState(tmp).String()) + return nil +} + +type SnapshotVolumeType string + +const ( + SnapshotVolumeTypeUnknownVolumeType = SnapshotVolumeType("unknown_volume_type") + SnapshotVolumeTypeLSSD = SnapshotVolumeType("l_ssd") + SnapshotVolumeTypeBSSD = SnapshotVolumeType("b_ssd") + SnapshotVolumeTypeUnified = SnapshotVolumeType("unified") +) + +func (enum SnapshotVolumeType) String() string { + if enum == "" { + // return default value if empty + return "unknown_volume_type" + } + return string(enum) +} + +func (enum SnapshotVolumeType) MarshalJSON() ([]byte, error) { + return []byte(fmt.Sprintf(`"%s"`, enum)), nil +} + +func (enum *SnapshotVolumeType) UnmarshalJSON(data []byte) error { + tmp := "" + + if err := json.Unmarshal(data, &tmp); err != nil { + return err + } + + *enum = SnapshotVolumeType(SnapshotVolumeType(tmp).String()) + return nil +} + +type TaskStatus string + +const ( + TaskStatusPending = TaskStatus("pending") + TaskStatusStarted = TaskStatus("started") + TaskStatusSuccess = TaskStatus("success") + TaskStatusFailure = TaskStatus("failure") + TaskStatusRetry = TaskStatus("retry") +) + +func (enum TaskStatus) String() string { + if enum == "" { + // return default value if empty + return "pending" + } + return string(enum) +} + +func (enum TaskStatus) MarshalJSON() ([]byte, error) { + return []byte(fmt.Sprintf(`"%s"`, enum)), nil +} + +func (enum *TaskStatus) UnmarshalJSON(data []byte) error { + tmp := "" + + if err := json.Unmarshal(data, &tmp); err != nil { + return err + } + + *enum = TaskStatus(TaskStatus(tmp).String()) + return nil +} + +type VolumeServerState string + +const ( + VolumeServerStateAvailable = VolumeServerState("available") + VolumeServerStateSnapshotting = VolumeServerState("snapshotting") + VolumeServerStateFetching = VolumeServerState("fetching") + VolumeServerStateResizing = VolumeServerState("resizing") + VolumeServerStateSaving = VolumeServerState("saving") + VolumeServerStateHotsyncing = VolumeServerState("hotsyncing") + VolumeServerStateError = VolumeServerState("error") +) + +func (enum VolumeServerState) String() string { + if enum == "" { + // return default value if empty + return "available" + } + return string(enum) +} + +func (enum VolumeServerState) MarshalJSON() ([]byte, error) { + return []byte(fmt.Sprintf(`"%s"`, enum)), nil +} + +func (enum *VolumeServerState) UnmarshalJSON(data []byte) error { + tmp := "" + + if err := json.Unmarshal(data, &tmp); err != nil { + return err + } + + *enum = VolumeServerState(VolumeServerState(tmp).String()) + return nil +} + +type VolumeServerVolumeType string + +const ( + VolumeServerVolumeTypeLSSD = VolumeServerVolumeType("l_ssd") + VolumeServerVolumeTypeBSSD = VolumeServerVolumeType("b_ssd") + VolumeServerVolumeTypeSbsVolume = VolumeServerVolumeType("sbs_volume") + VolumeServerVolumeTypeScratch = VolumeServerVolumeType("scratch") +) + +func (enum VolumeServerVolumeType) String() string { + if enum == "" { + // return default value if empty + return "l_ssd" + } + return string(enum) +} + +func (enum VolumeServerVolumeType) MarshalJSON() ([]byte, error) { + return []byte(fmt.Sprintf(`"%s"`, enum)), nil +} + +func (enum *VolumeServerVolumeType) UnmarshalJSON(data []byte) error { + tmp := "" + + if err := json.Unmarshal(data, &tmp); err != nil { + return err + } + + *enum = VolumeServerVolumeType(VolumeServerVolumeType(tmp).String()) + return nil +} + +type VolumeState string + +const ( + VolumeStateAvailable = VolumeState("available") + VolumeStateSnapshotting = VolumeState("snapshotting") + VolumeStateFetching = VolumeState("fetching") + VolumeStateResizing = VolumeState("resizing") + VolumeStateSaving = VolumeState("saving") + VolumeStateHotsyncing = VolumeState("hotsyncing") + VolumeStateError = VolumeState("error") +) + +func (enum VolumeState) String() string { + if enum == "" { + // return default value if empty + return "available" + } + return string(enum) +} + +func (enum VolumeState) MarshalJSON() ([]byte, error) { + return []byte(fmt.Sprintf(`"%s"`, enum)), nil +} + +func (enum *VolumeState) UnmarshalJSON(data []byte) error { + tmp := "" + + if err := json.Unmarshal(data, &tmp); err != nil { + return err + } + + *enum = VolumeState(VolumeState(tmp).String()) + return nil +} + +type VolumeVolumeType string + +const ( + VolumeVolumeTypeLSSD = VolumeVolumeType("l_ssd") + VolumeVolumeTypeBSSD = VolumeVolumeType("b_ssd") + VolumeVolumeTypeUnified = VolumeVolumeType("unified") + VolumeVolumeTypeScratch = VolumeVolumeType("scratch") + VolumeVolumeTypeSbsVolume = VolumeVolumeType("sbs_volume") + VolumeVolumeTypeSbsSnapshot = VolumeVolumeType("sbs_snapshot") +) + +func (enum VolumeVolumeType) String() string { + if enum == "" { + // return default value if empty + return "l_ssd" + } + return string(enum) +} + +func (enum VolumeVolumeType) MarshalJSON() ([]byte, error) { + return []byte(fmt.Sprintf(`"%s"`, enum)), nil +} + +func (enum *VolumeVolumeType) UnmarshalJSON(data []byte) error { + tmp := "" + + if err := json.Unmarshal(data, &tmp); err != nil { + return err + } + + *enum = VolumeVolumeType(VolumeVolumeType(tmp).String()) + return nil +} + +// ServerSummary: server summary. +type ServerSummary struct { + ID string `json:"id"` + + Name string `json:"name"` +} + +// Bootscript: bootscript. +type Bootscript struct { + // Bootcmdargs: bootscript arguments. + Bootcmdargs string `json:"bootcmdargs"` + + // Default: display if the bootscript is the default bootscript (if no other boot option is configured). + Default bool `json:"default"` + + // Dtb: provide information regarding a Device Tree Binary (DTB) for use with C1 servers. + Dtb string `json:"dtb"` + + // ID: bootscript ID. + ID string `json:"id"` + + // Initrd: initrd (initial ramdisk) configuration. + Initrd string `json:"initrd"` + + // Kernel: instance kernel version. + Kernel string `json:"kernel"` + + // Organization: bootscript Organization ID. + Organization string `json:"organization"` + + // Project: bootscript Project ID. + Project string `json:"project"` + + // Public: provide information if the bootscript is public. + Public bool `json:"public"` + + // Title: bootscript title. + Title string `json:"title"` + + // Arch: bootscript architecture. + // Default value: unknown_arch + Arch Arch `json:"arch"` + + // Zone: zone in which the bootscript is located. + Zone scw.Zone `json:"zone"` +} + +// Volume: volume. +type Volume struct { + // ID: volume unique ID. + ID string `json:"id"` + + // Name: volume name. + Name string `json:"name"` + + // Deprecated: ExportURI: show the volume NBD export URI. + ExportURI *string `json:"export_uri"` + + // Size: volume disk size. + Size scw.Size `json:"size"` + + // VolumeType: volume type. + // Default value: l_ssd + VolumeType VolumeVolumeType `json:"volume_type"` + + // CreationDate: volume creation date. + CreationDate *time.Time `json:"creation_date"` + + // ModificationDate: volume modification date. + ModificationDate *time.Time `json:"modification_date"` + + // Organization: volume Organization ID. + Organization string `json:"organization"` + + // Project: volume Project ID. + Project string `json:"project"` + + // Tags: volume tags. + Tags []string `json:"tags"` + + // Server: instance attached to the volume. + Server *ServerSummary `json:"server"` + + // State: volume state. + // Default value: available + State VolumeState `json:"state"` + + // Zone: zone in which the volume is located. + Zone scw.Zone `json:"zone"` +} + +// VolumeSummary: volume summary. +type VolumeSummary struct { + ID string `json:"id"` + + Name string `json:"name"` + + Size scw.Size `json:"size"` + + // VolumeType: default value: l_ssd + VolumeType VolumeVolumeType `json:"volume_type"` +} + +// ServerTypeNetworkInterface: server type network interface. +type ServerTypeNetworkInterface struct { + // InternalBandwidth: maximum internal bandwidth in bits per seconds. + InternalBandwidth *uint64 `json:"internal_bandwidth"` + + // InternetBandwidth: maximum internet bandwidth in bits per seconds. + InternetBandwidth *uint64 `json:"internet_bandwidth"` +} + +// ServerTypeVolumeConstraintSizes: server type volume constraint sizes. +type ServerTypeVolumeConstraintSizes struct { + // MinSize: minimum volume size in bytes. + MinSize scw.Size `json:"min_size"` + + // MaxSize: maximum volume size in bytes. + MaxSize scw.Size `json:"max_size"` +} + +// Image: image. +type Image struct { + ID string `json:"id"` + + Name string `json:"name"` + + // Arch: default value: unknown_arch + Arch Arch `json:"arch"` + + CreationDate *time.Time `json:"creation_date"` + + ModificationDate *time.Time `json:"modification_date"` + + // Deprecated + DefaultBootscript *Bootscript `json:"default_bootscript"` + + ExtraVolumes map[string]*Volume `json:"extra_volumes"` + + FromServer string `json:"from_server"` + + Organization string `json:"organization"` + + Public bool `json:"public"` + + RootVolume *VolumeSummary `json:"root_volume"` + + // State: default value: available + State ImageState `json:"state"` + + Project string `json:"project"` + + Tags []string `json:"tags"` + + // Zone: zone to target. If none is passed will use default zone from the config. + Zone scw.Zone `json:"zone"` +} + +// PlacementGroup: placement group. +type PlacementGroup struct { + // ID: placement group unique ID. + ID string `json:"id"` + + // Name: placement group name. + Name string `json:"name"` + + // Organization: placement group Organization ID. + Organization string `json:"organization"` + + // Project: placement group Project ID. + Project string `json:"project"` + + // Tags: placement group tags. + Tags []string `json:"tags"` + + // PolicyMode: select the failure mode when the placement cannot be respected, either optional or enforced. + // Default value: optional + PolicyMode PlacementGroupPolicyMode `json:"policy_mode"` + + // PolicyType: select the behavior of the placement group, either low_latency (group) or max_availability (spread). + // Default value: max_availability + PolicyType PlacementGroupPolicyType `json:"policy_type"` + + // PolicyRespected: returns true if the policy is respected, false otherwise. + PolicyRespected bool `json:"policy_respected"` + + // Zone: zone in which the placement group is located. + Zone scw.Zone `json:"zone"` +} + +// PrivateNIC: private nic. +type PrivateNIC struct { + // ID: private NIC unique ID. + ID string `json:"id"` + + // ServerID: instance to which the private NIC is attached. + ServerID string `json:"server_id"` + + // PrivateNetworkID: private Network the private NIC is attached to. + PrivateNetworkID string `json:"private_network_id"` + + // MacAddress: private NIC MAC address. + MacAddress string `json:"mac_address"` + + // State: private NIC state. + // Default value: available + State PrivateNICState `json:"state"` + + // Tags: private NIC tags. + Tags []string `json:"tags"` +} + +// SecurityGroupSummary: security group summary. +type SecurityGroupSummary struct { + ID string `json:"id"` + + Name string `json:"name"` +} + +// ServerIP: server ip. +type ServerIP struct { + // ID: unique ID of the IP address. + ID string `json:"id"` + + // Address: instance's public IP-Address. + Address net.IP `json:"address"` + + // Gateway: gateway's IP address. + Gateway net.IP `json:"gateway"` + + // Netmask: cIDR netmask. + Netmask string `json:"netmask"` + + // Family: IP address family (inet or inet6). + // Default value: inet + Family ServerIPIPFamily `json:"family"` + + // Dynamic: true if the IP address is dynamic. + Dynamic bool `json:"dynamic"` + + // ProvisioningMode: information about this address provisioning mode. + // Default value: manual + ProvisioningMode ServerIPProvisioningMode `json:"provisioning_mode"` + + // Tags: tags associated with the IP. + Tags []string `json:"tags"` + + // IpamID: the ip_id of an IPAM ip if the ip is created from IPAM, null if not. + IpamID string `json:"ipam_id"` + + // State: IP address state. + // Default value: unknown_state + State ServerIPState `json:"state"` +} + +// ServerIPv6: server i pv6. +type ServerIPv6 struct { + // Address: instance IPv6 IP-Address. + Address net.IP `json:"address"` + + // Gateway: iPv6 IP-addresses gateway. + Gateway net.IP `json:"gateway"` + + // Netmask: iPv6 IP-addresses CIDR netmask. + Netmask string `json:"netmask"` +} + +// ServerLocation: server location. +type ServerLocation struct { + ClusterID string `json:"cluster_id"` + + HypervisorID string `json:"hypervisor_id"` + + NodeID string `json:"node_id"` + + PlatformID string `json:"platform_id"` + + ZoneID string `json:"zone_id"` +} + +// ServerMaintenance: server maintenance. +type ServerMaintenance struct { + Reason string `json:"reason"` + + StartDate *time.Time `json:"start_date"` +} + +// VolumeServer: volume server. +type VolumeServer struct { + ID string `json:"id"` + + Name string `json:"name"` + + ExportURI string `json:"export_uri"` + + Organization string `json:"organization"` + + Server *ServerSummary `json:"server"` + + Size scw.Size `json:"size"` + + // VolumeType: default value: l_ssd + VolumeType VolumeServerVolumeType `json:"volume_type"` + + CreationDate *time.Time `json:"creation_date"` + + ModificationDate *time.Time `json:"modification_date"` + + // State: default value: available + State VolumeServerState `json:"state"` + + Project string `json:"project"` + + Boot bool `json:"boot"` + + // Zone: zone to target. If none is passed will use default zone from the config. + Zone scw.Zone `json:"zone"` +} + +// SnapshotBaseVolume: snapshot base volume. +type SnapshotBaseVolume struct { + // ID: volume ID on which the snapshot is based. + ID string `json:"id"` + + // Name: volume name on which the snapshot is based on. + Name string `json:"name"` +} + +// ServerTypeCapabilities: server type capabilities. +type ServerTypeCapabilities struct { + // BlockStorage: defines whether the Instance supports block storage. + BlockStorage *bool `json:"block_storage"` + + // BootTypes: list of supported boot types. + BootTypes []BootType `json:"boot_types"` +} + +// ServerTypeNetwork: server type network. +type ServerTypeNetwork struct { + // Interfaces: list of available network interfaces. + Interfaces []*ServerTypeNetworkInterface `json:"interfaces"` + + // SumInternalBandwidth: total maximum internal bandwidth in bits per seconds. + SumInternalBandwidth *uint64 `json:"sum_internal_bandwidth"` + + // SumInternetBandwidth: total maximum internet bandwidth in bits per seconds. + SumInternetBandwidth *uint64 `json:"sum_internet_bandwidth"` + + // IPv6Support: true if IPv6 is enabled. + IPv6Support bool `json:"ipv6_support"` +} + +// ServerTypeVolumeConstraintsByType: server type volume constraints by type. +type ServerTypeVolumeConstraintsByType struct { + // LSSD: local SSD volumes. + LSSD *ServerTypeVolumeConstraintSizes `json:"l_ssd"` +} + +// VolumeTypeCapabilities: volume type capabilities. +type VolumeTypeCapabilities struct { + Snapshot bool `json:"snapshot"` +} + +// VolumeTypeConstraints: volume type constraints. +type VolumeTypeConstraints struct { + Min scw.Size `json:"min"` + + Max scw.Size `json:"max"` +} + +// Server: server. +type Server struct { + // ID: instance unique ID. + ID string `json:"id"` + + // Name: instance name. + Name string `json:"name"` + + // Organization: instance Organization ID. + Organization string `json:"organization"` + + // Project: instance Project ID. + Project string `json:"project"` + + // AllowedActions: list of allowed actions on the Instance. + AllowedActions []ServerAction `json:"allowed_actions"` + + // Tags: tags associated with the Instance. + Tags []string `json:"tags"` + + // CommercialType: instance commercial type (eg. GP1-M). + CommercialType string `json:"commercial_type"` + + // CreationDate: instance creation date. + CreationDate *time.Time `json:"creation_date"` + + // DynamicIPRequired: true if a dynamic IPv4 is required. + DynamicIPRequired bool `json:"dynamic_ip_required"` + + // RoutedIPEnabled: true to configure the instance so it uses the new routed IP mode. + RoutedIPEnabled bool `json:"routed_ip_enabled"` + + // EnableIPv6: true if IPv6 is enabled. + EnableIPv6 bool `json:"enable_ipv6"` + + // Hostname: instance host name. + Hostname string `json:"hostname"` + + // Image: information about the Instance image. + Image *Image `json:"image"` + + // Protected: defines whether the Instance protection option is activated. + Protected bool `json:"protected"` + + // PrivateIP: private IP address of the Instance. + PrivateIP *string `json:"private_ip"` + + // PublicIP: information about the public IP. + PublicIP *ServerIP `json:"public_ip"` + + // PublicIPs: information about all the public IPs attached to the server. + PublicIPs []*ServerIP `json:"public_ips"` + + // MacAddress: the server's MAC address. + MacAddress string `json:"mac_address"` + + // ModificationDate: instance modification date. + ModificationDate *time.Time `json:"modification_date"` + + // State: instance state. + // Default value: running + State ServerState `json:"state"` + + // Location: instance location. + Location *ServerLocation `json:"location"` + + // IPv6: instance IPv6 address. + IPv6 *ServerIPv6 `json:"ipv6"` + + // Deprecated: Bootscript: instance bootscript. + Bootscript *Bootscript `json:"bootscript"` + + // BootType: instance boot type. + // Default value: local + BootType BootType `json:"boot_type"` + + // Volumes: instance volumes. + Volumes map[string]*VolumeServer `json:"volumes"` + + // SecurityGroup: instance security group. + SecurityGroup *SecurityGroupSummary `json:"security_group"` + + // Maintenances: instance planned maintenance. + Maintenances []*ServerMaintenance `json:"maintenances"` + + // StateDetail: detailed information about the Instance state. + StateDetail string `json:"state_detail"` + + // Arch: instance architecture. + // Default value: unknown_arch + Arch Arch `json:"arch"` + + // PlacementGroup: instance placement group. + PlacementGroup *PlacementGroup `json:"placement_group"` + + // PrivateNics: instance private NICs. + PrivateNics []*PrivateNIC `json:"private_nics"` + + // Zone: zone in which the Instance is located. + Zone scw.Zone `json:"zone"` +} + +// IP: ip. +type IP struct { + ID string `json:"id"` + + Address net.IP `json:"address"` + + Reverse *string `json:"reverse"` + + Server *ServerSummary `json:"server"` + + Organization string `json:"organization"` + + Tags []string `json:"tags"` + + Project string `json:"project"` + + // Type: default value: unknown_iptype + Type IPType `json:"type"` + + // State: default value: unknown_state + State IPState `json:"state"` + + Prefix scw.IPNet `json:"prefix"` + + IpamID string `json:"ipam_id"` + + // Zone: zone to target. If none is passed will use default zone from the config. + Zone scw.Zone `json:"zone"` +} + +// VolumeTemplate: volume template. +type VolumeTemplate struct { + // ID: UUID of the volume. + ID string `json:"id,omitempty"` + + // Name: name of the volume. + Name string `json:"name,omitempty"` + + // Size: disk size of the volume, must be a multiple of 512. + Size scw.Size `json:"size,omitempty"` + + // VolumeType: type of the volume. + // Default value: l_ssd + VolumeType VolumeVolumeType `json:"volume_type,omitempty"` + + // Deprecated: Organization: organization ID of the volume. + // Precisely one of Project, Organization must be set. + Organization *string `json:"organization,omitempty"` + + // Project: project ID of the volume. + // Precisely one of Project, Organization must be set. + Project *string `json:"project,omitempty"` +} + +// SecurityGroup: security group. +type SecurityGroup struct { + // ID: security group unique ID. + ID string `json:"id"` + + // Name: security group name. + Name string `json:"name"` + + // Description: security group description. + Description string `json:"description"` + + // EnableDefaultSecurity: true if SMTP is blocked on IPv4 and IPv6. This feature is read only, please open a support ticket if you need to make it configurable. + EnableDefaultSecurity bool `json:"enable_default_security"` + + // InboundDefaultPolicy: default inbound policy. + // Default value: unknown_policy + InboundDefaultPolicy SecurityGroupPolicy `json:"inbound_default_policy"` + + // OutboundDefaultPolicy: default outbound policy. + // Default value: unknown_policy + OutboundDefaultPolicy SecurityGroupPolicy `json:"outbound_default_policy"` + + // Organization: security group Organization ID. + Organization string `json:"organization"` + + // Project: security group Project ID. + Project string `json:"project"` + + // Tags: security group tags. + Tags []string `json:"tags"` + + // Deprecated: OrganizationDefault: true if it is your default security group for this Organization ID. + OrganizationDefault *bool `json:"organization_default"` + + // ProjectDefault: true if it is your default security group for this Project ID. + ProjectDefault bool `json:"project_default"` + + // CreationDate: security group creation date. + CreationDate *time.Time `json:"creation_date"` + + // ModificationDate: security group modification date. + ModificationDate *time.Time `json:"modification_date"` + + // Servers: list of Instances attached to this security group. + Servers []*ServerSummary `json:"servers"` + + // Stateful: defines whether the security group is stateful. + Stateful bool `json:"stateful"` + + // State: security group state. + // Default value: available + State SecurityGroupState `json:"state"` + + // Zone: zone in which the security group is located. + Zone scw.Zone `json:"zone"` +} + +// SecurityGroupRule: security group rule. +type SecurityGroupRule struct { + ID string `json:"id"` + + // Protocol: default value: unknown_protocol + Protocol SecurityGroupRuleProtocol `json:"protocol"` + + // Direction: default value: unknown_direction + Direction SecurityGroupRuleDirection `json:"direction"` + + // Action: default value: unknown_action + Action SecurityGroupRuleAction `json:"action"` + + IPRange scw.IPNet `json:"ip_range"` + + DestPortFrom *uint32 `json:"dest_port_from"` + + DestPortTo *uint32 `json:"dest_port_to"` + + Position uint32 `json:"position"` + + Editable bool `json:"editable"` + + // Zone: zone to target. If none is passed will use default zone from the config. + Zone scw.Zone `json:"zone"` +} + +// VolumeServerTemplate: volume server template. +type VolumeServerTemplate struct { + // ID: UUID of the volume. + ID *string `json:"id,omitempty"` + + // Boot: force the Instance to boot on this volume. + Boot *bool `json:"boot,omitempty"` + + // Name: name of the volume. + Name *string `json:"name,omitempty"` + + // Size: disk size of the volume, must be a multiple of 512. + Size *scw.Size `json:"size,omitempty"` + + // VolumeType: type of the volume. + // Default value: l_ssd + VolumeType VolumeVolumeType `json:"volume_type,omitempty"` + + // BaseSnapshot: ID of the snapshot on which this volume will be based. + BaseSnapshot *string `json:"base_snapshot,omitempty"` + + // Organization: organization ID of the volume. + Organization *string `json:"organization,omitempty"` + + // Project: project ID of the volume. + Project *string `json:"project,omitempty"` +} + +// Snapshot: snapshot. +type Snapshot struct { + // ID: snapshot ID. + ID string `json:"id"` + + // Name: snapshot name. + Name string `json:"name"` + + // Organization: snapshot Organization ID. + Organization string `json:"organization"` + + // Project: snapshot Project ID. + Project string `json:"project"` + + // Tags: snapshot tags. + Tags []string `json:"tags"` + + // VolumeType: snapshot volume type. + // Default value: l_ssd + VolumeType VolumeVolumeType `json:"volume_type"` + + // Size: snapshot size. + Size scw.Size `json:"size"` + + // State: snapshot state. + // Default value: available + State SnapshotState `json:"state"` + + // BaseVolume: volume on which the snapshot is based on. + BaseVolume *SnapshotBaseVolume `json:"base_volume"` + + // CreationDate: snapshot creation date. + CreationDate *time.Time `json:"creation_date"` + + // ModificationDate: snapshot modification date. + ModificationDate *time.Time `json:"modification_date"` + + // Zone: snapshot zone. + Zone scw.Zone `json:"zone"` + + // ErrorReason: reason for the failed snapshot import. + ErrorReason *string `json:"error_reason"` +} + +// Task: task. +type Task struct { + // ID: unique ID of the task. + ID string `json:"id"` + + // Description: description of the task. + Description string `json:"description"` + + // Progress: progress of the task in percent. + Progress int32 `json:"progress"` + + // StartedAt: task start date. + StartedAt *time.Time `json:"started_at"` + + // TerminatedAt: task end date. + TerminatedAt *time.Time `json:"terminated_at"` + + // Status: task status. + // Default value: pending + Status TaskStatus `json:"status"` + + HrefFrom string `json:"href_from"` + + HrefResult string `json:"href_result"` + + // Zone: zone in which the task is excecuted. + Zone scw.Zone `json:"zone"` +} + +// Dashboard: dashboard. +type Dashboard struct { + VolumesCount uint32 `json:"volumes_count"` + + RunningServersCount uint32 `json:"running_servers_count"` + + ServersByTypes map[string]uint32 `json:"servers_by_types"` + + ImagesCount uint32 `json:"images_count"` + + SnapshotsCount uint32 `json:"snapshots_count"` + + ServersCount uint32 `json:"servers_count"` + + IPsCount uint32 `json:"ips_count"` + + SecurityGroupsCount uint32 `json:"security_groups_count"` + + IPsUnused uint32 `json:"ips_unused"` + + VolumesLSSDCount uint32 `json:"volumes_l_ssd_count"` + + VolumesBSSDCount uint32 `json:"volumes_b_ssd_count"` + + VolumesLSSDTotalSize scw.Size `json:"volumes_l_ssd_total_size"` + + VolumesBSSDTotalSize scw.Size `json:"volumes_b_ssd_total_size"` + + PrivateNicsCount uint32 `json:"private_nics_count"` + + PlacementGroupsCount uint32 `json:"placement_groups_count"` +} + +// PlacementGroupServer: placement group server. +type PlacementGroupServer struct { + // ID: instance UUID. + ID string `json:"id"` + + // Name: instance name. + Name string `json:"name"` + + // PolicyRespected: defines whether the placement group policy is respected (either 1 or 0). + PolicyRespected bool `json:"policy_respected"` +} + +// GetServerTypesAvailabilityResponseAvailability: get server types availability response availability. +type GetServerTypesAvailabilityResponseAvailability struct { + // Availability: default value: available + Availability ServerTypesAvailability `json:"availability"` +} + +// ServerType: server type. +type ServerType struct { + // Deprecated: MonthlyPrice: estimated monthly price, for a 30 days month, in Euro. + MonthlyPrice *float32 `json:"monthly_price"` + + // HourlyPrice: hourly price in Euro. + HourlyPrice float32 `json:"hourly_price"` + + // AltNames: alternative Instance name, if any. + AltNames []string `json:"alt_names"` + + // PerVolumeConstraint: additional volume constraints. + PerVolumeConstraint *ServerTypeVolumeConstraintsByType `json:"per_volume_constraint"` + + // VolumesConstraint: initial volume constraints. + VolumesConstraint *ServerTypeVolumeConstraintSizes `json:"volumes_constraint"` + + // Ncpus: number of CPU. + Ncpus uint32 `json:"ncpus"` + + // Gpu: number of GPU. + Gpu *uint64 `json:"gpu"` + + // RAM: available RAM in bytes. + RAM uint64 `json:"ram"` + + // Arch: CPU architecture. + // Default value: unknown_arch + Arch Arch `json:"arch"` + + // Baremetal: true if it is a baremetal Instance. + Baremetal bool `json:"baremetal"` + + // Network: network available for the Instance. + Network *ServerTypeNetwork `json:"network"` + + // Capabilities: capabilities. + Capabilities *ServerTypeCapabilities `json:"capabilities"` + + // ScratchStorageMaxSize: maximum available scratch storage. + ScratchStorageMaxSize *scw.Size `json:"scratch_storage_max_size"` +} + +// VolumeType: volume type. +type VolumeType struct { + DisplayName string `json:"display_name"` + + Capabilities *VolumeTypeCapabilities `json:"capabilities"` + + Constraints *VolumeTypeConstraints `json:"constraints"` +} + +// ServerActionRequestVolumeBackupTemplate: server action request volume backup template. +type ServerActionRequestVolumeBackupTemplate struct { + // VolumeType: overrides the `volume_type` of the snapshot for this volume. + // If omitted, the volume type of the original volume will be used. + // Default value: unknown_volume_type + VolumeType SnapshotVolumeType `json:"volume_type,omitempty"` +} + +// SetSecurityGroupRulesRequestRule: set security group rules request rule. +type SetSecurityGroupRulesRequestRule struct { + // ID: UUID of the security rule to update. If no value is provided, a new rule will be created. + ID *string `json:"id"` + + // Action: action to apply when the rule matches a packet. + // Default value: unknown_action + Action SecurityGroupRuleAction `json:"action"` + + // Protocol: protocol family this rule applies to. + // Default value: unknown_protocol + Protocol SecurityGroupRuleProtocol `json:"protocol"` + + // Direction: direction the rule applies to. + // Default value: unknown_direction + Direction SecurityGroupRuleDirection `json:"direction"` + + // IPRange: range of IP addresses these rules apply to. + IPRange scw.IPNet `json:"ip_range"` + + // DestPortFrom: beginning of the range of ports this rule applies to (inclusive). This value will be set to null if protocol is ICMP or ANY. + DestPortFrom *uint32 `json:"dest_port_from"` + + // DestPortTo: end of the range of ports this rule applies to (inclusive). This value will be set to null if protocol is ICMP or ANY, or if it is equal to dest_port_from. + DestPortTo *uint32 `json:"dest_port_to"` + + // Position: position of this rule in the security group rules list. If several rules are passed with the same position, the resulting order is undefined. + Position uint32 `json:"position"` + + // Editable: indicates if this rule is editable. Rules with the value false will be ignored. + Editable *bool `json:"editable"` + + // Zone: zone of the rule. This field is ignored. + Zone *scw.Zone `json:"zone"` +} + +// NullableStringValue: nullable string value. +type NullableStringValue struct { + Null bool `json:"null,omitempty"` + + Value string `json:"value,omitempty"` +} + +// VolumeImageUpdateTemplate: volume image update template. +type VolumeImageUpdateTemplate struct { + // ID: UUID of the snapshot. + ID string `json:"id,omitempty"` +} + +// SecurityGroupTemplate: security group template. +type SecurityGroupTemplate struct { + ID string `json:"id,omitempty"` + + Name string `json:"name,omitempty"` +} + +// ApplyBlockMigrationRequest: apply block migration request. +type ApplyBlockMigrationRequest struct { + // Zone: zone to target. If none is passed will use default zone from the config. + Zone scw.Zone `json:"-"` + + // VolumeID: the volume to migrate, along with potentially other resources, according to the migration plan generated with a call to PlanBlockMigration. + // Precisely one of VolumeID, SnapshotID must be set. + VolumeID *string `json:"volume_id,omitempty"` + + // SnapshotID: the snapshot to migrate, along with potentially other resources, according to the migration plan generated with a call to PlanBlockMigration. + // Precisely one of VolumeID, SnapshotID must be set. + SnapshotID *string `json:"snapshot_id,omitempty"` + + // ValidationKey: a value to be retrieved from a call to PlanBlockMigration, to confirm that the volume and/or snapshots specified in said plan should be migrated. + ValidationKey string `json:"validation_key,omitempty"` +} + +// AttachServerVolumeRequest: attach server volume request. +type AttachServerVolumeRequest struct { + // Zone: zone to target. If none is passed will use default zone from the config. + Zone scw.Zone `json:"-"` + + ServerID string `json:"-"` + + VolumeID string `json:"volume_id,omitempty"` + + // VolumeType: default value: unknown_volume_type + VolumeType AttachServerVolumeRequestVolumeType `json:"volume_type,omitempty"` + + Boot *bool `json:"boot,omitempty"` +} + +// AttachServerVolumeResponse: attach server volume response. +type AttachServerVolumeResponse struct { + Server *Server `json:"server"` +} + +// CreateIPRequest: create ip request. +type CreateIPRequest struct { + // Zone: zone to target. If none is passed will use default zone from the config. + Zone scw.Zone `json:"-"` + + // Deprecated: Organization: organization ID in which the IP is reserved. + // Precisely one of Project, Organization must be set. + Organization *string `json:"organization,omitempty"` + + // Project: project ID in which the IP is reserved. + // Precisely one of Project, Organization must be set. + Project *string `json:"project,omitempty"` + + // Tags: tags of the IP. + Tags []string `json:"tags,omitempty"` + + // Server: UUID of the Instance you want to attach the IP to. + Server *string `json:"server,omitempty"` + + // Type: IP type to reserve (either 'nat', 'routed_ipv4' or 'routed_ipv6'). + // Default value: unknown_iptype + Type IPType `json:"type,omitempty"` +} + +// CreateIPResponse: create ip response. +type CreateIPResponse struct { + IP *IP `json:"ip"` +} + +// CreateImageRequest: create image request. +type CreateImageRequest struct { + // Zone: zone to target. If none is passed will use default zone from the config. + Zone scw.Zone `json:"-"` + + // Name: name of the image. + Name string `json:"name,omitempty"` + + // RootVolume: UUID of the snapshot. + RootVolume string `json:"root_volume,omitempty"` + + // Arch: architecture of the image. + // Default value: unknown_arch + Arch Arch `json:"arch,omitempty"` + + // Deprecated: DefaultBootscript: default bootscript of the image. + DefaultBootscript *string `json:"default_bootscript,omitempty"` + + // ExtraVolumes: additional volumes of the image. + ExtraVolumes map[string]*VolumeTemplate `json:"extra_volumes,omitempty"` + + // Deprecated: Organization: organization ID of the image. + // Precisely one of Project, Organization must be set. + Organization *string `json:"organization,omitempty"` + + // Project: project ID of the image. + // Precisely one of Project, Organization must be set. + Project *string `json:"project,omitempty"` + + // Tags: tags of the image. + Tags []string `json:"tags,omitempty"` + + // Public: true to create a public image. + Public *bool `json:"public,omitempty"` +} + +// CreateImageResponse: create image response. +type CreateImageResponse struct { + Image *Image `json:"image"` +} + +// CreatePlacementGroupRequest: create placement group request. +type CreatePlacementGroupRequest struct { + // Zone: zone to target. If none is passed will use default zone from the config. + Zone scw.Zone `json:"-"` + + // Name: name of the placement group. + Name string `json:"name,omitempty"` + + // Deprecated: Organization: organization ID of the placement group. + // Precisely one of Project, Organization must be set. + Organization *string `json:"organization,omitempty"` + + // Project: project ID of the placement group. + // Precisely one of Project, Organization must be set. + Project *string `json:"project,omitempty"` + + // Tags: tags of the placement group. + Tags []string `json:"tags,omitempty"` + + // PolicyMode: operating mode of the placement group. + // Default value: optional + PolicyMode PlacementGroupPolicyMode `json:"policy_mode,omitempty"` + + // PolicyType: policy type of the placement group. + // Default value: max_availability + PolicyType PlacementGroupPolicyType `json:"policy_type,omitempty"` +} + +// CreatePlacementGroupResponse: create placement group response. +type CreatePlacementGroupResponse struct { + PlacementGroup *PlacementGroup `json:"placement_group"` +} + +// CreatePrivateNICRequest: create private nic request. +type CreatePrivateNICRequest struct { + // Zone: zone to target. If none is passed will use default zone from the config. + Zone scw.Zone `json:"-"` + + // ServerID: UUID of the Instance the private NIC will be attached to. + ServerID string `json:"-"` + + // PrivateNetworkID: UUID of the private network where the private NIC will be attached. + PrivateNetworkID string `json:"private_network_id,omitempty"` + + // Tags: private NIC tags. + Tags []string `json:"tags,omitempty"` + + // IPIDs: ip_ids defined from IPAM. + IPIDs []string `json:"ip_ids,omitempty"` +} + +// CreatePrivateNICResponse: create private nic response. +type CreatePrivateNICResponse struct { + PrivateNic *PrivateNIC `json:"private_nic"` +} + +// CreateSecurityGroupRequest: create security group request. +type CreateSecurityGroupRequest struct { + // Zone: zone to target. If none is passed will use default zone from the config. + Zone scw.Zone `json:"-"` + + // Name: name of the security group. + Name string `json:"name,omitempty"` + + // Description: description of the security group. + Description string `json:"description,omitempty"` + + // Deprecated: Organization: organization ID the security group belongs to. + // Precisely one of Project, Organization must be set. + Organization *string `json:"organization,omitempty"` + + // Project: project ID the security group belong to. + // Precisely one of Project, Organization must be set. + Project *string `json:"project,omitempty"` + + // Tags: tags of the security group. + Tags []string `json:"tags,omitempty"` + + // Deprecated: OrganizationDefault: defines whether this security group becomes the default security group for new Instances. + // Precisely one of OrganizationDefault, ProjectDefault must be set. + OrganizationDefault *bool `json:"organization_default,omitempty"` + + // ProjectDefault: whether this security group becomes the default security group for new Instances. + // Precisely one of OrganizationDefault, ProjectDefault must be set. + ProjectDefault *bool `json:"project_default,omitempty"` + + // Stateful: whether the security group is stateful or not. + Stateful bool `json:"stateful,omitempty"` + + // InboundDefaultPolicy: default policy for inbound rules. + // Default value: unknown_policy + InboundDefaultPolicy SecurityGroupPolicy `json:"inbound_default_policy,omitempty"` + + // OutboundDefaultPolicy: default policy for outbound rules. + // Default value: unknown_policy + OutboundDefaultPolicy SecurityGroupPolicy `json:"outbound_default_policy,omitempty"` + + // EnableDefaultSecurity: true to block SMTP on IPv4 and IPv6. This feature is read only, please open a support ticket if you need to make it configurable. + EnableDefaultSecurity *bool `json:"enable_default_security,omitempty"` +} + +// CreateSecurityGroupResponse: create security group response. +type CreateSecurityGroupResponse struct { + SecurityGroup *SecurityGroup `json:"security_group"` +} + +// CreateSecurityGroupRuleRequest: create security group rule request. +type CreateSecurityGroupRuleRequest struct { + // Zone: zone to target. If none is passed will use default zone from the config. + Zone scw.Zone `json:"-"` + + // SecurityGroupID: UUID of the security group. + SecurityGroupID string `json:"-"` + + // Protocol: default value: unknown_protocol + Protocol SecurityGroupRuleProtocol `json:"protocol,omitempty"` + + // Direction: default value: unknown_direction + Direction SecurityGroupRuleDirection `json:"direction,omitempty"` + + // Action: default value: unknown_action + Action SecurityGroupRuleAction `json:"action,omitempty"` + + IPRange scw.IPNet `json:"ip_range,omitempty"` + + // DestPortFrom: beginning of the range of ports to apply this rule to (inclusive). + DestPortFrom *uint32 `json:"dest_port_from,omitempty"` + + // DestPortTo: end of the range of ports to apply this rule to (inclusive). + DestPortTo *uint32 `json:"dest_port_to,omitempty"` + + // Position: position of this rule in the security group rules list. + Position uint32 `json:"position,omitempty"` + + // Editable: indicates if this rule is editable (will be ignored). + Editable bool `json:"editable,omitempty"` +} + +// CreateSecurityGroupRuleResponse: create security group rule response. +type CreateSecurityGroupRuleResponse struct { + Rule *SecurityGroupRule `json:"rule"` +} + +// CreateServerRequest: create server request. +type CreateServerRequest struct { + // Zone: zone to target. If none is passed will use default zone from the config. + Zone scw.Zone `json:"-"` + + // Name: instance name. + Name string `json:"name,omitempty"` + + // DynamicIPRequired: define if a dynamic IPv4 is required for the Instance. + DynamicIPRequired *bool `json:"dynamic_ip_required,omitempty"` + + // RoutedIPEnabled: if true, configure the Instance so it uses the new routed IP mode. + RoutedIPEnabled *bool `json:"routed_ip_enabled,omitempty"` + + // CommercialType: define the Instance commercial type (i.e. GP1-S). + CommercialType string `json:"commercial_type,omitempty"` + + // Image: instance image ID or label. + Image string `json:"image,omitempty"` + + // Volumes: volumes attached to the server. + Volumes map[string]*VolumeServerTemplate `json:"volumes,omitempty"` + + // EnableIPv6: true if IPv6 is enabled on the server. + EnableIPv6 bool `json:"enable_ipv6,omitempty"` + + // PublicIP: ID of the reserved IP to attach to the Instance. + PublicIP *string `json:"public_ip,omitempty"` + + // PublicIPs: a list of reserved IP IDs to attach to the Instance. + PublicIPs *[]string `json:"public_ips,omitempty"` + + // BootType: boot type to use. + // Default value: local + BootType *BootType `json:"boot_type,omitempty"` + + // Deprecated: Bootscript: bootscript ID to use when `boot_type` is set to `bootscript`. + Bootscript *string `json:"bootscript,omitempty"` + + // Deprecated: Organization: instance Organization ID. + // Precisely one of Project, Organization must be set. + Organization *string `json:"organization,omitempty"` + + // Project: instance Project ID. + // Precisely one of Project, Organization must be set. + Project *string `json:"project,omitempty"` + + // Tags: instance tags. + Tags []string `json:"tags,omitempty"` + + // SecurityGroup: security group ID. + SecurityGroup *string `json:"security_group,omitempty"` + + // PlacementGroup: placement group ID if Instance must be part of a placement group. + PlacementGroup *string `json:"placement_group,omitempty"` +} + +// CreateServerResponse: create server response. +type CreateServerResponse struct { + Server *Server `json:"server"` +} + +// CreateSnapshotRequest: create snapshot request. +type CreateSnapshotRequest struct { + // Zone: zone to target. If none is passed will use default zone from the config. + Zone scw.Zone `json:"-"` + + // Name: name of the snapshot. + Name string `json:"name,omitempty"` + + // VolumeID: UUID of the volume. + VolumeID *string `json:"volume_id,omitempty"` + + // Tags: tags of the snapshot. + Tags *[]string `json:"tags,omitempty"` + + // Deprecated: Organization: organization ID of the snapshot. + // Precisely one of Project, Organization must be set. + Organization *string `json:"organization,omitempty"` + + // Project: project ID of the snapshot. + // Precisely one of Project, Organization must be set. + Project *string `json:"project,omitempty"` + + // VolumeType: overrides the volume_type of the snapshot. + // If omitted, the volume type of the original volume will be used. + // Default value: unknown_volume_type + VolumeType SnapshotVolumeType `json:"volume_type,omitempty"` + + // Bucket: bucket name for snapshot imports. + Bucket *string `json:"bucket,omitempty"` + + // Key: object key for snapshot imports. + Key *string `json:"key,omitempty"` + + // Size: imported snapshot size, must be a multiple of 512. + Size *scw.Size `json:"size,omitempty"` +} + +// CreateSnapshotResponse: create snapshot response. +type CreateSnapshotResponse struct { + Snapshot *Snapshot `json:"snapshot"` + + Task *Task `json:"task"` +} + +// CreateVolumeRequest: create volume request. +type CreateVolumeRequest struct { + // Zone: zone to target. If none is passed will use default zone from the config. + Zone scw.Zone `json:"-"` + + // Name: volume name. + Name string `json:"name,omitempty"` + + // Deprecated: Organization: volume Organization ID. + // Precisely one of Project, Organization must be set. + Organization *string `json:"organization,omitempty"` + + // Project: volume Project ID. + // Precisely one of Project, Organization must be set. + Project *string `json:"project,omitempty"` + + // Tags: volume tags. + Tags []string `json:"tags,omitempty"` + + // VolumeType: volume type. + // Default value: l_ssd + VolumeType VolumeVolumeType `json:"volume_type,omitempty"` + + // Size: volume disk size, must be a multiple of 512. + // Precisely one of Size, BaseSnapshot must be set. + Size *scw.Size `json:"size,omitempty"` + + // BaseSnapshot: ID of the snapshot on which this volume will be based. + // Precisely one of Size, BaseSnapshot must be set. + BaseSnapshot *string `json:"base_snapshot,omitempty"` +} + +// CreateVolumeResponse: create volume response. +type CreateVolumeResponse struct { + Volume *Volume `json:"volume"` +} + +// DeleteIPRequest: delete ip request. +type DeleteIPRequest struct { + // Zone: zone to target. If none is passed will use default zone from the config. + Zone scw.Zone `json:"-"` + + // IP: ID or address of the IP to delete. + IP string `json:"-"` +} + +// DeleteImageRequest: delete image request. +type DeleteImageRequest struct { + // Zone: zone to target. If none is passed will use default zone from the config. + Zone scw.Zone `json:"-"` + + // ImageID: UUID of the image you want to delete. + ImageID string `json:"-"` +} + +// DeletePlacementGroupRequest: delete placement group request. +type DeletePlacementGroupRequest struct { + // Zone: zone to target. If none is passed will use default zone from the config. + Zone scw.Zone `json:"-"` + + // PlacementGroupID: UUID of the placement group you want to delete. + PlacementGroupID string `json:"-"` +} + +// DeletePrivateNICRequest: delete private nic request. +type DeletePrivateNICRequest struct { + // Zone: zone to target. If none is passed will use default zone from the config. + Zone scw.Zone `json:"-"` + + // ServerID: instance to which the private NIC is attached. + ServerID string `json:"-"` + + // PrivateNicID: private NIC unique ID. + PrivateNicID string `json:"-"` +} + +// DeleteSecurityGroupRequest: delete security group request. +type DeleteSecurityGroupRequest struct { + // Zone: zone to target. If none is passed will use default zone from the config. + Zone scw.Zone `json:"-"` + + // SecurityGroupID: UUID of the security group you want to delete. + SecurityGroupID string `json:"-"` +} + +// DeleteSecurityGroupRuleRequest: delete security group rule request. +type DeleteSecurityGroupRuleRequest struct { + // Zone: zone to target. If none is passed will use default zone from the config. + Zone scw.Zone `json:"-"` + + SecurityGroupID string `json:"-"` + + SecurityGroupRuleID string `json:"-"` +} + +// DeleteServerRequest: delete server request. +type DeleteServerRequest struct { + // Zone: zone to target. If none is passed will use default zone from the config. + Zone scw.Zone `json:"-"` + + ServerID string `json:"-"` +} + +// DeleteServerUserDataRequest: delete server user data request. +type DeleteServerUserDataRequest struct { + // Zone: zone to target. If none is passed will use default zone from the config. + Zone scw.Zone `json:"-"` + + // ServerID: UUID of the Instance. + ServerID string `json:"-"` + + // Key: key of the user data to delete. + Key string `json:"-"` +} + +// DeleteSnapshotRequest: delete snapshot request. +type DeleteSnapshotRequest struct { + // Zone: zone to target. If none is passed will use default zone from the config. + Zone scw.Zone `json:"-"` + + // SnapshotID: UUID of the snapshot you want to delete. + SnapshotID string `json:"-"` +} + +// DeleteVolumeRequest: delete volume request. +type DeleteVolumeRequest struct { + // Zone: zone to target. If none is passed will use default zone from the config. + Zone scw.Zone `json:"-"` + + // VolumeID: UUID of the volume you want to delete. + VolumeID string `json:"-"` +} + +// DetachServerVolumeRequest: detach server volume request. +type DetachServerVolumeRequest struct { + // Zone: zone to target. If none is passed will use default zone from the config. + Zone scw.Zone `json:"-"` + + ServerID string `json:"-"` + + VolumeID string `json:"volume_id,omitempty"` +} + +// DetachServerVolumeResponse: detach server volume response. +type DetachServerVolumeResponse struct { + Server *Server `json:"server"` +} + +// ExportSnapshotRequest: export snapshot request. +type ExportSnapshotRequest struct { + // Zone: zone to target. If none is passed will use default zone from the config. + Zone scw.Zone `json:"-"` + + // SnapshotID: snapshot ID. + SnapshotID string `json:"-"` + + // Bucket: s3 bucket name. + Bucket string `json:"bucket,omitempty"` + + // Key: s3 object key. + Key string `json:"key,omitempty"` +} + +// ExportSnapshotResponse: export snapshot response. +type ExportSnapshotResponse struct { + Task *Task `json:"task"` +} + +// GetBootscriptRequest: get bootscript request. +type GetBootscriptRequest struct { + // Zone: zone to target. If none is passed will use default zone from the config. + Zone scw.Zone `json:"-"` + + BootscriptID string `json:"-"` +} + +// GetBootscriptResponse: get bootscript response. +type GetBootscriptResponse struct { + Bootscript *Bootscript `json:"bootscript"` +} + +// GetDashboardRequest: get dashboard request. +type GetDashboardRequest struct { + // Zone: zone to target. If none is passed will use default zone from the config. + Zone scw.Zone `json:"-"` + + Organization *string `json:"-"` + + Project *string `json:"-"` +} + +// GetDashboardResponse: get dashboard response. +type GetDashboardResponse struct { + Dashboard *Dashboard `json:"dashboard"` +} + +// GetIPRequest: get ip request. +type GetIPRequest struct { + // Zone: zone to target. If none is passed will use default zone from the config. + Zone scw.Zone `json:"-"` + + // IP: IP ID or address to get. + IP string `json:"-"` +} + +// GetIPResponse: get ip response. +type GetIPResponse struct { + IP *IP `json:"ip"` +} + +// GetImageRequest: get image request. +type GetImageRequest struct { + // Zone: zone to target. If none is passed will use default zone from the config. + Zone scw.Zone `json:"-"` + + // ImageID: UUID of the image you want to get. + ImageID string `json:"-"` +} + +// GetImageResponse: get image response. +type GetImageResponse struct { + Image *Image `json:"image"` +} + +// GetPlacementGroupRequest: get placement group request. +type GetPlacementGroupRequest struct { + // Zone: zone to target. If none is passed will use default zone from the config. + Zone scw.Zone `json:"-"` + + // PlacementGroupID: UUID of the placement group you want to get. + PlacementGroupID string `json:"-"` +} + +// GetPlacementGroupResponse: get placement group response. +type GetPlacementGroupResponse struct { + PlacementGroup *PlacementGroup `json:"placement_group"` +} + +// GetPlacementGroupServersRequest: get placement group servers request. +type GetPlacementGroupServersRequest struct { + // Zone: zone to target. If none is passed will use default zone from the config. + Zone scw.Zone `json:"-"` + + // PlacementGroupID: UUID of the placement group you want to get. + PlacementGroupID string `json:"-"` +} + +// GetPlacementGroupServersResponse: get placement group servers response. +type GetPlacementGroupServersResponse struct { + // Servers: instances attached to the placement group. + Servers []*PlacementGroupServer `json:"servers"` +} + +// GetPrivateNICRequest: get private nic request. +type GetPrivateNICRequest struct { + // Zone: zone to target. If none is passed will use default zone from the config. + Zone scw.Zone `json:"-"` + + // ServerID: instance to which the private NIC is attached. + ServerID string `json:"-"` + + // PrivateNicID: private NIC unique ID. + PrivateNicID string `json:"-"` +} + +// GetPrivateNICResponse: get private nic response. +type GetPrivateNICResponse struct { + PrivateNic *PrivateNIC `json:"private_nic"` +} + +// GetSecurityGroupRequest: get security group request. +type GetSecurityGroupRequest struct { + // Zone: zone to target. If none is passed will use default zone from the config. + Zone scw.Zone `json:"-"` + + // SecurityGroupID: UUID of the security group you want to get. + SecurityGroupID string `json:"-"` +} + +// GetSecurityGroupResponse: get security group response. +type GetSecurityGroupResponse struct { + SecurityGroup *SecurityGroup `json:"security_group"` +} + +// GetSecurityGroupRuleRequest: get security group rule request. +type GetSecurityGroupRuleRequest struct { + // Zone: zone to target. If none is passed will use default zone from the config. + Zone scw.Zone `json:"-"` + + SecurityGroupID string `json:"-"` + + SecurityGroupRuleID string `json:"-"` +} + +// GetSecurityGroupRuleResponse: get security group rule response. +type GetSecurityGroupRuleResponse struct { + Rule *SecurityGroupRule `json:"rule"` +} + +// GetServerRequest: get server request. +type GetServerRequest struct { + // Zone: zone to target. If none is passed will use default zone from the config. + Zone scw.Zone `json:"-"` + + // ServerID: UUID of the Instance you want to get. + ServerID string `json:"-"` +} + +// GetServerResponse: get server response. +type GetServerResponse struct { + Server *Server `json:"server"` +} + +// GetServerTypesAvailabilityRequest: get server types availability request. +type GetServerTypesAvailabilityRequest struct { + // Zone: zone to target. If none is passed will use default zone from the config. + Zone scw.Zone `json:"-"` + + // PerPage: a positive integer lower or equal to 100 to select the number of items to return. + PerPage *uint32 `json:"-"` + + // Page: a positive integer to choose the page to return. + Page *int32 `json:"-"` +} + +// GetServerTypesAvailabilityResponse: get server types availability response. +type GetServerTypesAvailabilityResponse struct { + // Servers: map of server types. + Servers map[string]*GetServerTypesAvailabilityResponseAvailability `json:"servers"` + + TotalCount uint32 `json:"total_count"` +} + +// UnsafeGetTotalCount should not be used +// Internal usage only +func (r *GetServerTypesAvailabilityResponse) UnsafeGetTotalCount() uint32 { + return r.TotalCount +} + +// UnsafeAppend should not be used +// Internal usage only +func (r *GetServerTypesAvailabilityResponse) UnsafeAppend(res interface{}) (uint32, error) { + results, ok := res.(*GetServerTypesAvailabilityResponse) + if !ok { + return 0, errors.New("%T type cannot be appended to type %T", res, r) + } + + if r.Servers == nil { + r.Servers = make(map[string]*GetServerTypesAvailabilityResponseAvailability) + } + for k, v := range results.Servers { + r.Servers[k] = v + } + r.TotalCount += uint32(len(results.Servers)) + return uint32(len(results.Servers)), nil +} + +// GetSnapshotRequest: get snapshot request. +type GetSnapshotRequest struct { + // Zone: zone to target. If none is passed will use default zone from the config. + Zone scw.Zone `json:"-"` + + // SnapshotID: UUID of the snapshot you want to get. + SnapshotID string `json:"-"` +} + +// GetSnapshotResponse: get snapshot response. +type GetSnapshotResponse struct { + Snapshot *Snapshot `json:"snapshot"` +} + +// GetVolumeRequest: get volume request. +type GetVolumeRequest struct { + // Zone: zone to target. If none is passed will use default zone from the config. + Zone scw.Zone `json:"-"` + + // VolumeID: UUID of the volume you want to get. + VolumeID string `json:"-"` +} + +// GetVolumeResponse: get volume response. +type GetVolumeResponse struct { + Volume *Volume `json:"volume"` +} + +// ListBootscriptsRequest: list bootscripts request. +type ListBootscriptsRequest struct { + // Zone: zone to target. If none is passed will use default zone from the config. + Zone scw.Zone `json:"-"` + + Arch *string `json:"-"` + + Title *string `json:"-"` + + Default *bool `json:"-"` + + Public *bool `json:"-"` + + PerPage *uint32 `json:"-"` + + Page *int32 `json:"-"` +} + +// ListBootscriptsResponse: list bootscripts response. +type ListBootscriptsResponse struct { + // TotalCount: total number of bootscripts. + TotalCount uint32 `json:"total_count"` + + // Bootscripts: list of bootscripts. + Bootscripts []*Bootscript `json:"bootscripts"` +} + +// UnsafeGetTotalCount should not be used +// Internal usage only +func (r *ListBootscriptsResponse) UnsafeGetTotalCount() uint32 { + return r.TotalCount +} + +// UnsafeAppend should not be used +// Internal usage only +func (r *ListBootscriptsResponse) UnsafeAppend(res interface{}) (uint32, error) { + results, ok := res.(*ListBootscriptsResponse) + if !ok { + return 0, errors.New("%T type cannot be appended to type %T", res, r) + } + + r.Bootscripts = append(r.Bootscripts, results.Bootscripts...) + r.TotalCount += uint32(len(results.Bootscripts)) + return uint32(len(results.Bootscripts)), nil +} + +// ListDefaultSecurityGroupRulesRequest: list default security group rules request. +type ListDefaultSecurityGroupRulesRequest struct { + // Zone: zone to target. If none is passed will use default zone from the config. + Zone scw.Zone `json:"-"` +} + +// ListIPsRequest: list i ps request. +type ListIPsRequest struct { + // Zone: zone to target. If none is passed will use default zone from the config. + Zone scw.Zone `json:"-"` + + // Project: project ID in which the IPs are reserved. + Project *string `json:"-"` + + // Organization: organization ID in which the IPs are reserved. + Organization *string `json:"-"` + + // Tags: filter IPs with these exact tags (to filter with several tags, use commas to separate them). + Tags []string `json:"-"` + + // Name: filter on the IP address (Works as a LIKE operation on the IP address). + Name *string `json:"-"` + + // PerPage: a positive integer lower or equal to 100 to select the number of items to return. + PerPage *uint32 `json:"-"` + + // Page: a positive integer to choose the page to return. + Page *int32 `json:"-"` + + // Type: filter on the IP Mobility IP type (whose value should be either 'nat', 'routed_ipv4' or 'routed_ipv6'). + Type *string `json:"-"` +} + +// ListIPsResponse: list i ps response. +type ListIPsResponse struct { + // TotalCount: total number of ips. + TotalCount uint32 `json:"total_count"` + + // IPs: list of ips. + IPs []*IP `json:"ips"` +} + +// UnsafeGetTotalCount should not be used +// Internal usage only +func (r *ListIPsResponse) UnsafeGetTotalCount() uint32 { + return r.TotalCount +} + +// UnsafeAppend should not be used +// Internal usage only +func (r *ListIPsResponse) UnsafeAppend(res interface{}) (uint32, error) { + results, ok := res.(*ListIPsResponse) + if !ok { + return 0, errors.New("%T type cannot be appended to type %T", res, r) + } + + r.IPs = append(r.IPs, results.IPs...) + r.TotalCount += uint32(len(results.IPs)) + return uint32(len(results.IPs)), nil +} + +// ListImagesRequest: list images request. +type ListImagesRequest struct { + // Zone: zone to target. If none is passed will use default zone from the config. + Zone scw.Zone `json:"-"` + + Organization *string `json:"-"` + + PerPage *uint32 `json:"-"` + + Page *int32 `json:"-"` + + Name *string `json:"-"` + + Public *bool `json:"-"` + + Arch *string `json:"-"` + + Project *string `json:"-"` + + Tags *string `json:"-"` +} + +// ListImagesResponse: list images response. +type ListImagesResponse struct { + // TotalCount: total number of images. + TotalCount uint32 `json:"total_count"` + + // Images: list of images. + Images []*Image `json:"images"` +} + +// UnsafeGetTotalCount should not be used +// Internal usage only +func (r *ListImagesResponse) UnsafeGetTotalCount() uint32 { + return r.TotalCount +} + +// UnsafeAppend should not be used +// Internal usage only +func (r *ListImagesResponse) UnsafeAppend(res interface{}) (uint32, error) { + results, ok := res.(*ListImagesResponse) + if !ok { + return 0, errors.New("%T type cannot be appended to type %T", res, r) + } + + r.Images = append(r.Images, results.Images...) + r.TotalCount += uint32(len(results.Images)) + return uint32(len(results.Images)), nil +} + +// ListPlacementGroupsRequest: list placement groups request. +type ListPlacementGroupsRequest struct { + // Zone: zone to target. If none is passed will use default zone from the config. + Zone scw.Zone `json:"-"` + + // PerPage: a positive integer lower or equal to 100 to select the number of items to return. + PerPage *uint32 `json:"-"` + + // Page: a positive integer to choose the page to return. + Page *int32 `json:"-"` + + // Organization: list only placement groups of this Organization ID. + Organization *string `json:"-"` + + // Project: list only placement groups of this Project ID. + Project *string `json:"-"` + + // Tags: list placement groups with these exact tags (to filter with several tags, use commas to separate them). + Tags []string `json:"-"` + + // Name: filter placement groups by name (for eg. "cluster1" will return "cluster100" and "cluster1" but not "foo"). + Name *string `json:"-"` +} + +// ListPlacementGroupsResponse: list placement groups response. +type ListPlacementGroupsResponse struct { + // TotalCount: total number of placement groups. + TotalCount uint32 `json:"total_count"` + + // PlacementGroups: list of placement groups. + PlacementGroups []*PlacementGroup `json:"placement_groups"` +} + +// UnsafeGetTotalCount should not be used +// Internal usage only +func (r *ListPlacementGroupsResponse) UnsafeGetTotalCount() uint32 { + return r.TotalCount +} + +// UnsafeAppend should not be used +// Internal usage only +func (r *ListPlacementGroupsResponse) UnsafeAppend(res interface{}) (uint32, error) { + results, ok := res.(*ListPlacementGroupsResponse) + if !ok { + return 0, errors.New("%T type cannot be appended to type %T", res, r) + } + + r.PlacementGroups = append(r.PlacementGroups, results.PlacementGroups...) + r.TotalCount += uint32(len(results.PlacementGroups)) + return uint32(len(results.PlacementGroups)), nil +} + +// ListPrivateNICsRequest: list private ni cs request. +type ListPrivateNICsRequest struct { + // Zone: zone to target. If none is passed will use default zone from the config. + Zone scw.Zone `json:"-"` + + // ServerID: instance to which the private NIC is attached. + ServerID string `json:"-"` + + // Tags: private NIC tags. + Tags []string `json:"-"` + + // PerPage: a positive integer lower or equal to 100 to select the number of items to return. + PerPage *uint32 `json:"-"` + + // Page: a positive integer to choose the page to return. + Page *int32 `json:"-"` +} + +// ListPrivateNICsResponse: list private ni cs response. +type ListPrivateNICsResponse struct { + PrivateNics []*PrivateNIC `json:"private_nics"` + + TotalCount uint64 `json:"total_count"` +} + +// UnsafeGetTotalCount should not be used +// Internal usage only +func (r *ListPrivateNICsResponse) UnsafeGetTotalCount() uint64 { + return r.TotalCount +} + +// UnsafeAppend should not be used +// Internal usage only +func (r *ListPrivateNICsResponse) UnsafeAppend(res interface{}) (uint64, error) { + results, ok := res.(*ListPrivateNICsResponse) + if !ok { + return 0, errors.New("%T type cannot be appended to type %T", res, r) + } + + r.PrivateNics = append(r.PrivateNics, results.PrivateNics...) + r.TotalCount += uint64(len(results.PrivateNics)) + return uint64(len(results.PrivateNics)), nil +} + +// ListSecurityGroupRulesRequest: list security group rules request. +type ListSecurityGroupRulesRequest struct { + // Zone: zone to target. If none is passed will use default zone from the config. + Zone scw.Zone `json:"-"` + + // SecurityGroupID: UUID of the security group. + SecurityGroupID string `json:"-"` + + // PerPage: a positive integer lower or equal to 100 to select the number of items to return. + PerPage *uint32 `json:"-"` + + // Page: a positive integer to choose the page to return. + Page *int32 `json:"-"` +} + +// ListSecurityGroupRulesResponse: list security group rules response. +type ListSecurityGroupRulesResponse struct { + // TotalCount: total number of security groups. + TotalCount uint32 `json:"total_count"` + + // Rules: list of security rules. + Rules []*SecurityGroupRule `json:"rules"` +} + +// UnsafeGetTotalCount should not be used +// Internal usage only +func (r *ListSecurityGroupRulesResponse) UnsafeGetTotalCount() uint32 { + return r.TotalCount +} + +// UnsafeAppend should not be used +// Internal usage only +func (r *ListSecurityGroupRulesResponse) UnsafeAppend(res interface{}) (uint32, error) { + results, ok := res.(*ListSecurityGroupRulesResponse) + if !ok { + return 0, errors.New("%T type cannot be appended to type %T", res, r) + } + + r.Rules = append(r.Rules, results.Rules...) + r.TotalCount += uint32(len(results.Rules)) + return uint32(len(results.Rules)), nil +} + +// ListSecurityGroupsRequest: list security groups request. +type ListSecurityGroupsRequest struct { + // Zone: zone to target. If none is passed will use default zone from the config. + Zone scw.Zone `json:"-"` + + // Name: name of the security group. + Name *string `json:"-"` + + // Organization: security group Organization ID. + Organization *string `json:"-"` + + // Project: security group Project ID. + Project *string `json:"-"` + + // Tags: list security groups with these exact tags (to filter with several tags, use commas to separate them). + Tags []string `json:"-"` + + // ProjectDefault: filter security groups with this value for project_default. + ProjectDefault *bool `json:"-"` + + // PerPage: a positive integer lower or equal to 100 to select the number of items to return. + PerPage *uint32 `json:"-"` + + // Page: a positive integer to choose the page to return. + Page *int32 `json:"-"` +} + +// ListSecurityGroupsResponse: list security groups response. +type ListSecurityGroupsResponse struct { + // TotalCount: total number of security groups. + TotalCount uint32 `json:"total_count"` + + // SecurityGroups: list of security groups. + SecurityGroups []*SecurityGroup `json:"security_groups"` +} + +// UnsafeGetTotalCount should not be used +// Internal usage only +func (r *ListSecurityGroupsResponse) UnsafeGetTotalCount() uint32 { + return r.TotalCount +} + +// UnsafeAppend should not be used +// Internal usage only +func (r *ListSecurityGroupsResponse) UnsafeAppend(res interface{}) (uint32, error) { + results, ok := res.(*ListSecurityGroupsResponse) + if !ok { + return 0, errors.New("%T type cannot be appended to type %T", res, r) + } + + r.SecurityGroups = append(r.SecurityGroups, results.SecurityGroups...) + r.TotalCount += uint32(len(results.SecurityGroups)) + return uint32(len(results.SecurityGroups)), nil +} + +// ListServerActionsRequest: list server actions request. +type ListServerActionsRequest struct { + // Zone: zone to target. If none is passed will use default zone from the config. + Zone scw.Zone `json:"-"` + + ServerID string `json:"-"` +} + +// ListServerActionsResponse: list server actions response. +type ListServerActionsResponse struct { + Actions []ServerAction `json:"actions"` +} + +// ListServerUserDataRequest: list server user data request. +type ListServerUserDataRequest struct { + // Zone: zone to target. If none is passed will use default zone from the config. + Zone scw.Zone `json:"-"` + + // ServerID: UUID of the Instance. + ServerID string `json:"-"` +} + +// ListServerUserDataResponse: list server user data response. +type ListServerUserDataResponse struct { + UserData []string `json:"user_data"` +} + +// ListServersRequest: list servers request. +type ListServersRequest struct { + // Zone: zone to target. If none is passed will use default zone from the config. + Zone scw.Zone `json:"-"` + + // PerPage: a positive integer lower or equal to 100 to select the number of items to return. + PerPage *uint32 `json:"-"` + + // Page: a positive integer to choose the page to return. + Page *int32 `json:"-"` + + // Organization: list only Instances of this Organization ID. + Organization *string `json:"-"` + + // Project: list only Instances of this Project ID. + Project *string `json:"-"` + + // Name: filter Instances by name (eg. "server1" will return "server100" and "server1" but not "foo"). + Name *string `json:"-"` + + // PrivateIP: list Instances by private_ip. + PrivateIP *net.IP `json:"-"` + + // WithoutIP: list Instances that are not attached to a public IP. + WithoutIP *bool `json:"-"` + + // CommercialType: list Instances of this commercial type. + CommercialType *string `json:"-"` + + // State: list Instances in this state. + // Default value: running + State *ServerState `json:"-"` + + // Tags: list Instances with these exact tags (to filter with several tags, use commas to separate them). + Tags []string `json:"-"` + + // PrivateNetwork: list Instances in this Private Network. + PrivateNetwork *string `json:"-"` + + // Order: define the order of the returned servers. + // Default value: creation_date_desc + Order ListServersRequestOrder `json:"-"` + + // PrivateNetworks: list Instances from the given Private Networks (use commas to separate them). + PrivateNetworks []string `json:"-"` + + // PrivateNicMacAddress: list Instances associated with the given private NIC MAC address. + PrivateNicMacAddress *string `json:"-"` + + // Servers: list Instances from these server ids (use commas to separate them). + Servers []string `json:"-"` +} + +// ListServersResponse: list servers response. +type ListServersResponse struct { + // TotalCount: total number of Instances. + TotalCount uint32 `json:"total_count"` + + // Servers: list of Instances. + Servers []*Server `json:"servers"` +} + +// UnsafeGetTotalCount should not be used +// Internal usage only +func (r *ListServersResponse) UnsafeGetTotalCount() uint32 { + return r.TotalCount +} + +// UnsafeAppend should not be used +// Internal usage only +func (r *ListServersResponse) UnsafeAppend(res interface{}) (uint32, error) { + results, ok := res.(*ListServersResponse) + if !ok { + return 0, errors.New("%T type cannot be appended to type %T", res, r) + } + + r.Servers = append(r.Servers, results.Servers...) + r.TotalCount += uint32(len(results.Servers)) + return uint32(len(results.Servers)), nil +} + +// ListServersTypesRequest: list servers types request. +type ListServersTypesRequest struct { + // Zone: zone to target. If none is passed will use default zone from the config. + Zone scw.Zone `json:"-"` + + PerPage *uint32 `json:"-"` + + Page *int32 `json:"-"` +} + +// ListServersTypesResponse: list servers types response. +type ListServersTypesResponse struct { + // TotalCount: total number of Instance types. + TotalCount uint32 `json:"total_count"` + + // Servers: list of Instance types. + Servers map[string]*ServerType `json:"servers"` +} + +// UnsafeGetTotalCount should not be used +// Internal usage only +func (r *ListServersTypesResponse) UnsafeGetTotalCount() uint32 { + return r.TotalCount +} + +// UnsafeAppend should not be used +// Internal usage only +func (r *ListServersTypesResponse) UnsafeAppend(res interface{}) (uint32, error) { + results, ok := res.(*ListServersTypesResponse) + if !ok { + return 0, errors.New("%T type cannot be appended to type %T", res, r) + } + + if r.Servers == nil { + r.Servers = make(map[string]*ServerType) + } + for k, v := range results.Servers { + r.Servers[k] = v + } + r.TotalCount += uint32(len(results.Servers)) + return uint32(len(results.Servers)), nil +} + +// ListSnapshotsRequest: list snapshots request. +type ListSnapshotsRequest struct { + // Zone: zone to target. If none is passed will use default zone from the config. + Zone scw.Zone `json:"-"` + + // Organization: list snapshots only for this Organization ID. + Organization *string `json:"-"` + + // Project: list snapshots only for this Project ID. + Project *string `json:"-"` + + // PerPage: number of snapshots returned per page (positive integer lower or equal to 100). + PerPage *uint32 `json:"-"` + + // Page: page to be returned. + Page *int32 `json:"-"` + + // Name: list snapshots of the requested name. + Name *string `json:"-"` + + // Tags: list snapshots that have the requested tag. + Tags *string `json:"-"` + + // BaseVolumeID: list snapshots originating only from this volume. + BaseVolumeID *string `json:"-"` +} + +// ListSnapshotsResponse: list snapshots response. +type ListSnapshotsResponse struct { + // TotalCount: total number of snapshots. + TotalCount uint32 `json:"total_count"` + + // Snapshots: list of snapshots. + Snapshots []*Snapshot `json:"snapshots"` +} + +// UnsafeGetTotalCount should not be used +// Internal usage only +func (r *ListSnapshotsResponse) UnsafeGetTotalCount() uint32 { + return r.TotalCount +} + +// UnsafeAppend should not be used +// Internal usage only +func (r *ListSnapshotsResponse) UnsafeAppend(res interface{}) (uint32, error) { + results, ok := res.(*ListSnapshotsResponse) + if !ok { + return 0, errors.New("%T type cannot be appended to type %T", res, r) + } + + r.Snapshots = append(r.Snapshots, results.Snapshots...) + r.TotalCount += uint32(len(results.Snapshots)) + return uint32(len(results.Snapshots)), nil +} + +// ListVolumesRequest: list volumes request. +type ListVolumesRequest struct { + // Zone: zone to target. If none is passed will use default zone from the config. + Zone scw.Zone `json:"-"` + + // VolumeType: filter by volume type. + // Default value: l_ssd + VolumeType *VolumeVolumeType `json:"-"` + + // PerPage: a positive integer lower or equal to 100 to select the number of items to return. + PerPage *uint32 `json:"-"` + + // Page: a positive integer to choose the page to return. + Page *int32 `json:"-"` + + // Organization: filter volume by Organization ID. + Organization *string `json:"-"` + + // Project: filter volume by Project ID. + Project *string `json:"-"` + + // Tags: filter volumes with these exact tags (to filter with several tags, use commas to separate them). + Tags []string `json:"-"` + + // Name: filter volume by name (for eg. "vol" will return "myvolume" but not "data"). + Name *string `json:"-"` +} + +// ListVolumesResponse: list volumes response. +type ListVolumesResponse struct { + // TotalCount: total number of volumes. + TotalCount uint32 `json:"total_count"` + + // Volumes: list of volumes. + Volumes []*Volume `json:"volumes"` +} + +// UnsafeGetTotalCount should not be used +// Internal usage only +func (r *ListVolumesResponse) UnsafeGetTotalCount() uint32 { + return r.TotalCount +} + +// UnsafeAppend should not be used +// Internal usage only +func (r *ListVolumesResponse) UnsafeAppend(res interface{}) (uint32, error) { + results, ok := res.(*ListVolumesResponse) + if !ok { + return 0, errors.New("%T type cannot be appended to type %T", res, r) + } + + r.Volumes = append(r.Volumes, results.Volumes...) + r.TotalCount += uint32(len(results.Volumes)) + return uint32(len(results.Volumes)), nil +} + +// ListVolumesTypesRequest: list volumes types request. +type ListVolumesTypesRequest struct { + // Zone: zone to target. If none is passed will use default zone from the config. + Zone scw.Zone `json:"-"` + + PerPage *uint32 `json:"-"` + + Page *int32 `json:"-"` +} + +// ListVolumesTypesResponse: list volumes types response. +type ListVolumesTypesResponse struct { + // TotalCount: total number of volume types. + TotalCount uint32 `json:"total_count"` + + // Volumes: map of volume types. + Volumes map[string]*VolumeType `json:"volumes"` +} + +// UnsafeGetTotalCount should not be used +// Internal usage only +func (r *ListVolumesTypesResponse) UnsafeGetTotalCount() uint32 { + return r.TotalCount +} + +// UnsafeAppend should not be used +// Internal usage only +func (r *ListVolumesTypesResponse) UnsafeAppend(res interface{}) (uint32, error) { + results, ok := res.(*ListVolumesTypesResponse) + if !ok { + return 0, errors.New("%T type cannot be appended to type %T", res, r) + } + + if r.Volumes == nil { + r.Volumes = make(map[string]*VolumeType) + } + for k, v := range results.Volumes { + r.Volumes[k] = v + } + r.TotalCount += uint32(len(results.Volumes)) + return uint32(len(results.Volumes)), nil +} + +// MigrationPlan: migration plan. +type MigrationPlan struct { + // Volume: a volume which will be migrated to SBS together with the snapshots, if present. + Volume *Volume `json:"volume"` + + // Snapshots: a list of snapshots which will be migrated to SBS together and with the volume, if present. + Snapshots []*Snapshot `json:"snapshots"` + + // ValidationKey: a value to be passed to ApplyBlockMigrationRequest, to confirm that the execution of the plan is being requested. + ValidationKey string `json:"validation_key"` +} + +// PlanBlockMigrationRequest: plan block migration request. +type PlanBlockMigrationRequest struct { + // Zone: zone to target. If none is passed will use default zone from the config. + Zone scw.Zone `json:"-"` + + // VolumeID: the volume for which the migration plan will be generated. + // Precisely one of VolumeID, SnapshotID must be set. + VolumeID *string `json:"volume_id,omitempty"` + + // SnapshotID: the snapshot for which the migration plan will be generated. + // Precisely one of VolumeID, SnapshotID must be set. + SnapshotID *string `json:"snapshot_id,omitempty"` +} + +// ServerActionRequest: server action request. +type ServerActionRequest struct { + // Zone: zone to target. If none is passed will use default zone from the config. + Zone scw.Zone `json:"-"` + + // ServerID: UUID of the Instance. + ServerID string `json:"-"` + + // Action: action to perform on the Instance. + // Default value: poweron + Action ServerAction `json:"action,omitempty"` + + // Name: name of the backup you want to create. + // This field should only be specified when performing a backup action. + Name *string `json:"name,omitempty"` + + // Volumes: for each volume UUID, the snapshot parameters of the volume. + // This field should only be specified when performing a backup action. + Volumes map[string]*ServerActionRequestVolumeBackupTemplate `json:"volumes,omitempty"` +} + +// ServerActionResponse: server action response. +type ServerActionResponse struct { + Task *Task `json:"task"` +} + +// SetImageRequest: set image request. +type SetImageRequest struct { + // Zone: zone to target. If none is passed will use default zone from the config. + Zone scw.Zone `json:"-"` + + ID string `json:"-"` + + Name string `json:"name"` + + // Arch: default value: unknown_arch + Arch Arch `json:"arch"` + + CreationDate *time.Time `json:"creation_date,omitempty"` + + ModificationDate *time.Time `json:"modification_date,omitempty"` + + // Deprecated + DefaultBootscript *Bootscript `json:"default_bootscript,omitempty"` + + ExtraVolumes map[string]*Volume `json:"extra_volumes"` + + FromServer string `json:"from_server"` + + Organization string `json:"organization"` + + Public bool `json:"public"` + + RootVolume *VolumeSummary `json:"root_volume,omitempty"` + + // State: default value: available + State ImageState `json:"state"` + + Project string `json:"project"` + + Tags *[]string `json:"tags,omitempty"` +} + +// SetPlacementGroupRequest: set placement group request. +type SetPlacementGroupRequest struct { + // Zone: zone to target. If none is passed will use default zone from the config. + Zone scw.Zone `json:"-"` + + PlacementGroupID string `json:"-"` + + Name string `json:"name"` + + Organization string `json:"organization"` + + // PolicyMode: default value: optional + PolicyMode PlacementGroupPolicyMode `json:"policy_mode"` + + // PolicyType: default value: max_availability + PolicyType PlacementGroupPolicyType `json:"policy_type"` + + Project string `json:"project"` + + Tags *[]string `json:"tags,omitempty"` +} + +// SetPlacementGroupResponse: set placement group response. +type SetPlacementGroupResponse struct { + PlacementGroup *PlacementGroup `json:"placement_group"` +} + +// SetPlacementGroupServersRequest: set placement group servers request. +type SetPlacementGroupServersRequest struct { + // Zone: zone to target. If none is passed will use default zone from the config. + Zone scw.Zone `json:"-"` + + // PlacementGroupID: UUID of the placement group you want to set. + PlacementGroupID string `json:"-"` + + // Servers: an array of the Instances' UUIDs you want to configure. + Servers []string `json:"servers"` +} + +// SetPlacementGroupServersResponse: set placement group servers response. +type SetPlacementGroupServersResponse struct { + // Servers: instances attached to the placement group. + Servers []*PlacementGroupServer `json:"servers"` +} + +// SetSecurityGroupRulesRequest: set security group rules request. +type SetSecurityGroupRulesRequest struct { + // Zone: zone to target. If none is passed will use default zone from the config. + Zone scw.Zone `json:"-"` + + // SecurityGroupID: UUID of the security group to update the rules on. + SecurityGroupID string `json:"-"` + + // Rules: list of rules to update in the security group. + Rules []*SetSecurityGroupRulesRequestRule `json:"rules"` +} + +// SetSecurityGroupRulesResponse: set security group rules response. +type SetSecurityGroupRulesResponse struct { + Rules []*SecurityGroupRule `json:"rules"` +} + +// UpdateIPRequest: update ip request. +type UpdateIPRequest struct { + // Zone: zone to target. If none is passed will use default zone from the config. + Zone scw.Zone `json:"-"` + + // IP: IP ID or IP address. + IP string `json:"-"` + + // Reverse: reverse domain name. + Reverse *NullableStringValue `json:"reverse,omitempty"` + + // Type: convert a 'nat' IP to a 'routed_ipv4'. + // Default value: unknown_iptype + Type IPType `json:"type,omitempty"` + + // Tags: an array of keywords you want to tag this IP with. + Tags *[]string `json:"tags,omitempty"` + + Server *NullableStringValue `json:"server,omitempty"` +} + +// UpdateIPResponse: update ip response. +type UpdateIPResponse struct { + IP *IP `json:"ip"` +} + +// UpdateImageRequest: update image request. +type UpdateImageRequest struct { + // Zone: zone to target. If none is passed will use default zone from the config. + Zone scw.Zone `json:"-"` + + // ImageID: UUID of the image. + ImageID string `json:"-"` + + // Name: name of the image. + Name *string `json:"name,omitempty"` + + // Arch: architecture of the image. + // Default value: unknown_arch + Arch Arch `json:"arch,omitempty"` + + // ExtraVolumes: additional snapshots of the image, with extra_volumeKey being the position of the snapshot in the image. + ExtraVolumes map[string]*VolumeImageUpdateTemplate `json:"extra_volumes,omitempty"` + + // Tags: tags of the image. + Tags *[]string `json:"tags,omitempty"` + + // Public: true to set the image as public. + Public *bool `json:"public,omitempty"` +} + +// UpdateImageResponse: update image response. +type UpdateImageResponse struct { + Image *Image `json:"image"` +} + +// UpdatePlacementGroupRequest: update placement group request. +type UpdatePlacementGroupRequest struct { + // Zone: zone to target. If none is passed will use default zone from the config. + Zone scw.Zone `json:"-"` + + // PlacementGroupID: UUID of the placement group. + PlacementGroupID string `json:"-"` + + // Name: name of the placement group. + Name *string `json:"name,omitempty"` + + // Tags: tags of the placement group. + Tags *[]string `json:"tags,omitempty"` + + // PolicyMode: operating mode of the placement group. + // Default value: optional + PolicyMode *PlacementGroupPolicyMode `json:"policy_mode,omitempty"` + + // PolicyType: policy type of the placement group. + // Default value: max_availability + PolicyType *PlacementGroupPolicyType `json:"policy_type,omitempty"` +} + +// UpdatePlacementGroupResponse: update placement group response. +type UpdatePlacementGroupResponse struct { + PlacementGroup *PlacementGroup `json:"placement_group"` +} + +// UpdatePlacementGroupServersRequest: update placement group servers request. +type UpdatePlacementGroupServersRequest struct { + // Zone: zone to target. If none is passed will use default zone from the config. + Zone scw.Zone `json:"-"` + + // PlacementGroupID: UUID of the placement group you want to update. + PlacementGroupID string `json:"-"` + + // Servers: an array of the Instances' UUIDs you want to configure. + Servers []string `json:"servers,omitempty"` +} + +// UpdatePlacementGroupServersResponse: update placement group servers response. +type UpdatePlacementGroupServersResponse struct { + // Servers: instances attached to the placement group. + Servers []*PlacementGroupServer `json:"servers"` +} + +// UpdatePrivateNICRequest: update private nic request. +type UpdatePrivateNICRequest struct { + // Zone: zone to target. If none is passed will use default zone from the config. + Zone scw.Zone `json:"-"` + + // ServerID: UUID of the Instance the private NIC will be attached to. + ServerID string `json:"-"` + + // PrivateNicID: private NIC unique ID. + PrivateNicID string `json:"-"` + + // Tags: tags used to select private NIC/s. + Tags *[]string `json:"tags,omitempty"` +} + +// UpdateSecurityGroupRequest: update security group request. +type UpdateSecurityGroupRequest struct { + // Zone: zone to target. If none is passed will use default zone from the config. + Zone scw.Zone `json:"-"` + + // SecurityGroupID: UUID of the security group. + SecurityGroupID string `json:"-"` + + // Name: name of the security group. + Name *string `json:"name,omitempty"` + + // Description: description of the security group. + Description *string `json:"description,omitempty"` + + // EnableDefaultSecurity: true to block SMTP on IPv4 and IPv6. This feature is read only, please open a support ticket if you need to make it configurable. + EnableDefaultSecurity *bool `json:"enable_default_security,omitempty"` + + // InboundDefaultPolicy: default inbound policy. + // Default value: unknown_policy + InboundDefaultPolicy SecurityGroupPolicy `json:"inbound_default_policy,omitempty"` + + // Tags: tags of the security group. + Tags *[]string `json:"tags,omitempty"` + + // Deprecated: OrganizationDefault: please use project_default instead. + OrganizationDefault *bool `json:"organization_default,omitempty"` + + // ProjectDefault: true use this security group for future Instances created in this project. + ProjectDefault *bool `json:"project_default,omitempty"` + + // OutboundDefaultPolicy: default outbound policy. + // Default value: unknown_policy + OutboundDefaultPolicy SecurityGroupPolicy `json:"outbound_default_policy,omitempty"` + + // Stateful: true to set the security group as stateful. + Stateful *bool `json:"stateful,omitempty"` +} + +// UpdateSecurityGroupResponse: update security group response. +type UpdateSecurityGroupResponse struct { + SecurityGroup *SecurityGroup `json:"security_group"` +} + +// UpdateSecurityGroupRuleRequest: update security group rule request. +type UpdateSecurityGroupRuleRequest struct { + // Zone: zone to target. If none is passed will use default zone from the config. + Zone scw.Zone `json:"-"` + + // SecurityGroupID: UUID of the security group. + SecurityGroupID string `json:"-"` + + // SecurityGroupRuleID: UUID of the rule. + SecurityGroupRuleID string `json:"-"` + + // Protocol: protocol family this rule applies to. + // Default value: unknown_protocol + Protocol SecurityGroupRuleProtocol `json:"protocol,omitempty"` + + // Direction: direction the rule applies to. + // Default value: unknown_direction + Direction SecurityGroupRuleDirection `json:"direction,omitempty"` + + // Action: action to apply when the rule matches a packet. + // Default value: unknown_action + Action SecurityGroupRuleAction `json:"action,omitempty"` + + // IPRange: range of IP addresses these rules apply to. + IPRange *scw.IPNet `json:"ip_range,omitempty"` + + // DestPortFrom: beginning of the range of ports this rule applies to (inclusive). If 0 is provided, unset the parameter. + DestPortFrom *uint32 `json:"dest_port_from,omitempty"` + + // DestPortTo: end of the range of ports this rule applies to (inclusive). If 0 is provided, unset the parameter. + DestPortTo *uint32 `json:"dest_port_to,omitempty"` + + // Position: position of this rule in the security group rules list. + Position *uint32 `json:"position,omitempty"` +} + +// UpdateSecurityGroupRuleResponse: update security group rule response. +type UpdateSecurityGroupRuleResponse struct { + Rule *SecurityGroupRule `json:"rule"` +} + +// UpdateServerRequest: update server request. +type UpdateServerRequest struct { + // Zone: zone to target. If none is passed will use default zone from the config. + Zone scw.Zone `json:"-"` + + // ServerID: UUID of the Instance. + ServerID string `json:"-"` + + // Name: name of the Instance. + Name *string `json:"name,omitempty"` + + // BootType: default value: local + BootType *BootType `json:"boot_type,omitempty"` + + // Tags: tags of the Instance. + Tags *[]string `json:"tags,omitempty"` + + Volumes *map[string]*VolumeServerTemplate `json:"volumes,omitempty"` + + // Deprecated + Bootscript *string `json:"bootscript,omitempty"` + + DynamicIPRequired *bool `json:"dynamic_ip_required,omitempty"` + + // RoutedIPEnabled: true to configure the instance so it uses the new routed IP mode (once this is set to True you cannot set it back to False). + RoutedIPEnabled *bool `json:"routed_ip_enabled,omitempty"` + + // PublicIPs: a list of reserved IP IDs to attach to the Instance. + PublicIPs *[]string `json:"public_ips,omitempty"` + + EnableIPv6 *bool `json:"enable_ipv6,omitempty"` + + Protected *bool `json:"protected,omitempty"` + + SecurityGroup *SecurityGroupTemplate `json:"security_group,omitempty"` + + // PlacementGroup: placement group ID if Instance must be part of a placement group. + PlacementGroup *NullableStringValue `json:"placement_group,omitempty"` + + // PrivateNics: instance private NICs. + PrivateNics *[]string `json:"private_nics,omitempty"` + + // CommercialType: warning: This field has some restrictions: + // - Cannot be changed if the Instance is not in `stopped` state. + // - Cannot be changed if the Instance is in a placement group. + // - Local storage requirements of the target commercial_types must be fulfilled (i.e. if an Instance has 80GB of local storage, it can be changed into a GP1-XS, which has a maximum of 150GB, but it cannot be changed into a DEV1-S, which has only 20GB). + CommercialType *string `json:"commercial_type,omitempty"` +} + +// UpdateServerResponse: update server response. +type UpdateServerResponse struct { + Server *Server `json:"server"` +} + +// UpdateSnapshotRequest: update snapshot request. +type UpdateSnapshotRequest struct { + // Zone: zone to target. If none is passed will use default zone from the config. + Zone scw.Zone `json:"-"` + + // SnapshotID: UUID of the snapshot. + SnapshotID string `json:"-"` + + // Name: name of the snapshot. + Name *string `json:"name,omitempty"` + + // Tags: tags of the snapshot. + Tags *[]string `json:"tags,omitempty"` +} + +// UpdateSnapshotResponse: update snapshot response. +type UpdateSnapshotResponse struct { + Snapshot *Snapshot `json:"snapshot"` +} + +// UpdateVolumeRequest: update volume request. +type UpdateVolumeRequest struct { + // Zone: zone to target. If none is passed will use default zone from the config. + Zone scw.Zone `json:"-"` + + // VolumeID: UUID of the volume. + VolumeID string `json:"-"` + + // Name: volume name. + Name *string `json:"name,omitempty"` + + // Tags: tags of the volume. + Tags *[]string `json:"tags,omitempty"` + + // Size: volume disk size, must be a multiple of 512. + Size *scw.Size `json:"size,omitempty"` +} + +// UpdateVolumeResponse: update volume response. +type UpdateVolumeResponse struct { + Volume *Volume `json:"volume"` +} + +// setImageResponse: set image response. +type setImageResponse struct { + Image *Image `json:"image"` +} + +// setSecurityGroupRequest: set security group request. +type setSecurityGroupRequest struct { + // Zone: zone to target. If none is passed will use default zone from the config. + Zone scw.Zone `json:"-"` + + // ID: UUID of the security group. + ID string `json:"-"` + + // Name: name of the security group. + Name string `json:"name"` + + // Tags: tags of the security group. + Tags *[]string `json:"tags,omitempty"` + + // CreationDate: creation date of the security group (will be ignored). + CreationDate *time.Time `json:"creation_date,omitempty"` + + // ModificationDate: modification date of the security group (will be ignored). + ModificationDate *time.Time `json:"modification_date,omitempty"` + + // Description: description of the security group. + Description string `json:"description"` + + // EnableDefaultSecurity: true to block SMTP on IPv4 and IPv6. This feature is read only, please open a support ticket if you need to make it configurable. + EnableDefaultSecurity bool `json:"enable_default_security"` + + // InboundDefaultPolicy: default inbound policy. + // Default value: unknown_policy + InboundDefaultPolicy SecurityGroupPolicy `json:"inbound_default_policy"` + + // OutboundDefaultPolicy: default outbound policy. + // Default value: unknown_policy + OutboundDefaultPolicy SecurityGroupPolicy `json:"outbound_default_policy"` + + // Organization: security groups Organization ID. + Organization string `json:"organization"` + + // Project: security group Project ID. + Project string `json:"project"` + + // Deprecated: OrganizationDefault: please use project_default instead. + OrganizationDefault *bool `json:"organization_default,omitempty"` + + // ProjectDefault: true use this security group for future Instances created in this project. + ProjectDefault bool `json:"project_default"` + + // Servers: instances attached to this security group. + Servers []*ServerSummary `json:"servers"` + + // Stateful: true to set the security group as stateful. + Stateful bool `json:"stateful"` +} + +// setSecurityGroupResponse: set security group response. +type setSecurityGroupResponse struct { + SecurityGroup *SecurityGroup `json:"security_group"` +} + +// setSecurityGroupRuleRequest: set security group rule request. +type setSecurityGroupRuleRequest struct { + // Zone: zone to target. If none is passed will use default zone from the config. + Zone scw.Zone `json:"-"` + + SecurityGroupID string `json:"-"` + + SecurityGroupRuleID string `json:"-"` + + ID string `json:"id"` + + // Protocol: default value: unknown_protocol + Protocol SecurityGroupRuleProtocol `json:"protocol"` + + // Direction: default value: unknown_direction + Direction SecurityGroupRuleDirection `json:"direction"` + + // Action: default value: unknown_action + Action SecurityGroupRuleAction `json:"action"` + + IPRange scw.IPNet `json:"ip_range"` + + DestPortFrom *uint32 `json:"dest_port_from,omitempty"` + + DestPortTo *uint32 `json:"dest_port_to,omitempty"` + + Position uint32 `json:"position"` + + Editable bool `json:"editable"` +} + +// setSecurityGroupRuleResponse: set security group rule response. +type setSecurityGroupRuleResponse struct { + Rule *SecurityGroupRule `json:"rule"` +} + +// setServerRequest: set server request. +type setServerRequest struct { + // Zone: zone to target. If none is passed will use default zone from the config. + Zone scw.Zone `json:"-"` + + // ID: instance unique ID. + ID string `json:"-"` + + // Name: instance name. + Name string `json:"name"` + + // Organization: instance Organization ID. + Organization string `json:"organization"` + + // Project: instance Project ID. + Project string `json:"project"` + + // AllowedActions: provide a list of allowed actions on the server. + AllowedActions []ServerAction `json:"allowed_actions"` + + // Tags: tags associated with the Instance. + Tags *[]string `json:"tags,omitempty"` + + // CommercialType: instance commercial type (eg. GP1-M). + CommercialType string `json:"commercial_type"` + + // CreationDate: instance creation date. + CreationDate *time.Time `json:"creation_date,omitempty"` + + // DynamicIPRequired: true if a dynamic IPv4 is required. + DynamicIPRequired bool `json:"dynamic_ip_required"` + + // RoutedIPEnabled: true to configure the instance so it uses the new routed IP mode (once this is set to True you cannot set it back to False). + RoutedIPEnabled *bool `json:"routed_ip_enabled,omitempty"` + + // EnableIPv6: true if IPv6 is enabled. + EnableIPv6 bool `json:"enable_ipv6"` + + // Hostname: instance host name. + Hostname string `json:"hostname"` + + // Image: provide information on the Instance image. + Image *Image `json:"image,omitempty"` + + // Protected: instance protection option is activated. + Protected bool `json:"protected"` + + // PrivateIP: instance private IP address. + PrivateIP *string `json:"private_ip,omitempty"` + + // PublicIP: information about the public IP. + PublicIP *ServerIP `json:"public_ip,omitempty"` + + // PublicIPs: information about all the public IPs attached to the server. + PublicIPs []*ServerIP `json:"public_ips"` + + // ModificationDate: instance modification date. + ModificationDate *time.Time `json:"modification_date,omitempty"` + + // State: instance state. + // Default value: running + State ServerState `json:"state"` + + // Location: instance location. + Location *ServerLocation `json:"location,omitempty"` + + // IPv6: instance IPv6 address. + IPv6 *ServerIPv6 `json:"ipv6,omitempty"` + + // Deprecated: Bootscript: instance bootscript. + Bootscript *Bootscript `json:"bootscript,omitempty"` + + // BootType: instance boot type. + // Default value: local + BootType BootType `json:"boot_type"` + + // Volumes: instance volumes. + Volumes map[string]*Volume `json:"volumes"` + + // SecurityGroup: instance security group. + SecurityGroup *SecurityGroupSummary `json:"security_group,omitempty"` + + // Maintenances: instance planned maintenances. + Maintenances []*ServerMaintenance `json:"maintenances"` + + // StateDetail: instance state_detail. + StateDetail string `json:"state_detail"` + + // Arch: instance architecture (refers to the CPU architecture used for the Instance, e.g. x86_64, arm64). + // Default value: unknown_arch + Arch Arch `json:"arch"` + + // PlacementGroup: instance placement group. + PlacementGroup *PlacementGroup `json:"placement_group,omitempty"` + + // PrivateNics: instance private NICs. + PrivateNics []*PrivateNIC `json:"private_nics"` +} + +// setServerResponse: set server response. +type setServerResponse struct { + Server *Server `json:"server"` +} + +// setSnapshotRequest: set snapshot request. +type setSnapshotRequest struct { + // Zone: zone to target. If none is passed will use default zone from the config. + Zone scw.Zone `json:"-"` + + SnapshotID string `json:"-"` + + ID string `json:"id"` + + Name string `json:"name"` + + Organization string `json:"organization"` + + // VolumeType: default value: l_ssd + VolumeType VolumeVolumeType `json:"volume_type"` + + Size scw.Size `json:"size"` + + // State: default value: available + State SnapshotState `json:"state"` + + BaseVolume *SnapshotBaseVolume `json:"base_volume,omitempty"` + + CreationDate *time.Time `json:"creation_date,omitempty"` + + ModificationDate *time.Time `json:"modification_date,omitempty"` + + Project string `json:"project"` + + Tags *[]string `json:"tags,omitempty"` +} + +// setSnapshotResponse: set snapshot response. +type setSnapshotResponse struct { + Snapshot *Snapshot `json:"snapshot"` +} + +// Instance API. +type API struct { + client *scw.Client +} + +// NewAPI returns a API object from a Scaleway client. +func NewAPI(client *scw.Client) *API { + return &API{ + client: client, + } +} +func (s *API) Zones() []scw.Zone { + return []scw.Zone{scw.ZoneFrPar1, scw.ZoneFrPar2, scw.ZoneFrPar3, scw.ZoneNlAms1, scw.ZoneNlAms2, scw.ZoneNlAms3, scw.ZonePlWaw1, scw.ZonePlWaw2, scw.ZonePlWaw3} +} + +// GetServerTypesAvailability: Get availability for all Instance types. +func (s *API) GetServerTypesAvailability(req *GetServerTypesAvailabilityRequest, opts ...scw.RequestOption) (*GetServerTypesAvailabilityResponse, error) { + var err error + + if req.Zone == "" { + defaultZone, _ := s.client.GetDefaultZone() + req.Zone = defaultZone + } + + query := url.Values{} + parameter.AddToQuery(query, "per_page", req.PerPage) + parameter.AddToQuery(query, "page", req.Page) + + if fmt.Sprint(req.Zone) == "" { + return nil, errors.New("field Zone cannot be empty in request") + } + + scwReq := &scw.ScalewayRequest{ + Method: "GET", + Path: "/instance/v1/zones/" + fmt.Sprint(req.Zone) + "/products/servers/availability", + Query: query, + } + + var resp GetServerTypesAvailabilityResponse + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +// ListServersTypes: List available Instance types and their technical details. +func (s *API) ListServersTypes(req *ListServersTypesRequest, opts ...scw.RequestOption) (*ListServersTypesResponse, error) { + var err error + + if req.Zone == "" { + defaultZone, _ := s.client.GetDefaultZone() + req.Zone = defaultZone + } + + query := url.Values{} + parameter.AddToQuery(query, "per_page", req.PerPage) + parameter.AddToQuery(query, "page", req.Page) + + if fmt.Sprint(req.Zone) == "" { + return nil, errors.New("field Zone cannot be empty in request") + } + + scwReq := &scw.ScalewayRequest{ + Method: "GET", + Path: "/instance/v1/zones/" + fmt.Sprint(req.Zone) + "/products/servers", + Query: query, + } + + var resp ListServersTypesResponse + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +// ListVolumesTypes: List all volume types and their technical details. +func (s *API) ListVolumesTypes(req *ListVolumesTypesRequest, opts ...scw.RequestOption) (*ListVolumesTypesResponse, error) { + var err error + + if req.Zone == "" { + defaultZone, _ := s.client.GetDefaultZone() + req.Zone = defaultZone + } + + query := url.Values{} + parameter.AddToQuery(query, "per_page", req.PerPage) + parameter.AddToQuery(query, "page", req.Page) + + if fmt.Sprint(req.Zone) == "" { + return nil, errors.New("field Zone cannot be empty in request") + } + + scwReq := &scw.ScalewayRequest{ + Method: "GET", + Path: "/instance/v1/zones/" + fmt.Sprint(req.Zone) + "/products/volumes", + Query: query, + } + + var resp ListVolumesTypesResponse + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +// ListServers: List all Instances in a specified Availability Zone, e.g. `fr-par-1`. +func (s *API) ListServers(req *ListServersRequest, opts ...scw.RequestOption) (*ListServersResponse, error) { + var err error + + if req.Zone == "" { + defaultZone, _ := s.client.GetDefaultZone() + req.Zone = defaultZone + } + + query := url.Values{} + parameter.AddToQuery(query, "per_page", req.PerPage) + parameter.AddToQuery(query, "page", req.Page) + parameter.AddToQuery(query, "organization", req.Organization) + parameter.AddToQuery(query, "project", req.Project) + parameter.AddToQuery(query, "name", req.Name) + parameter.AddToQuery(query, "private_ip", req.PrivateIP) + parameter.AddToQuery(query, "without_ip", req.WithoutIP) + parameter.AddToQuery(query, "commercial_type", req.CommercialType) + parameter.AddToQuery(query, "state", req.State) + if len(req.Tags) != 0 { + parameter.AddToQuery(query, "tags", strings.Join(req.Tags, ",")) + } + parameter.AddToQuery(query, "private_network", req.PrivateNetwork) + parameter.AddToQuery(query, "order", req.Order) + if len(req.PrivateNetworks) != 0 { + parameter.AddToQuery(query, "private_networks", strings.Join(req.PrivateNetworks, ",")) + } + parameter.AddToQuery(query, "private_nic_mac_address", req.PrivateNicMacAddress) + if len(req.Servers) != 0 { + parameter.AddToQuery(query, "servers", strings.Join(req.Servers, ",")) + } + + if fmt.Sprint(req.Zone) == "" { + return nil, errors.New("field Zone cannot be empty in request") + } + + scwReq := &scw.ScalewayRequest{ + Method: "GET", + Path: "/instance/v1/zones/" + fmt.Sprint(req.Zone) + "/servers", + Query: query, + } + + var resp ListServersResponse + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +// createServer: Create a new Instance of the specified commercial type in the specified zone. Pay attention to the volumes parameter, which takes an object which can be used in different ways to achieve different behaviors. +// Get more information in the [Technical Information](#technical-information) section of the introduction. +func (s *API) createServer(req *CreateServerRequest, opts ...scw.RequestOption) (*CreateServerResponse, error) { + var err error + + if req.Zone == "" { + defaultZone, _ := s.client.GetDefaultZone() + req.Zone = defaultZone + } + + defaultProject, exist := s.client.GetDefaultProjectID() + if exist && req.Project == nil && req.Organization == nil { + req.Project = &defaultProject + } + + defaultOrganization, exist := s.client.GetDefaultOrganizationID() + if exist && req.Project == nil && req.Organization == nil { + req.Organization = &defaultOrganization + } + + if req.Name == "" { + req.Name = namegenerator.GetRandomName("srv") + } + + if fmt.Sprint(req.Zone) == "" { + return nil, errors.New("field Zone cannot be empty in request") + } + + scwReq := &scw.ScalewayRequest{ + Method: "POST", + Path: "/instance/v1/zones/" + fmt.Sprint(req.Zone) + "/servers", + } + + err = scwReq.SetBody(req) + if err != nil { + return nil, err + } + + var resp CreateServerResponse + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +// DeleteServer: Delete the Instance with the specified ID. +func (s *API) DeleteServer(req *DeleteServerRequest, opts ...scw.RequestOption) error { + var err error + + if req.Zone == "" { + defaultZone, _ := s.client.GetDefaultZone() + req.Zone = defaultZone + } + + if fmt.Sprint(req.Zone) == "" { + return errors.New("field Zone cannot be empty in request") + } + + if fmt.Sprint(req.ServerID) == "" { + return errors.New("field ServerID cannot be empty in request") + } + + scwReq := &scw.ScalewayRequest{ + Method: "DELETE", + Path: "/instance/v1/zones/" + fmt.Sprint(req.Zone) + "/servers/" + fmt.Sprint(req.ServerID) + "", + } + + err = s.client.Do(scwReq, nil, opts...) + if err != nil { + return err + } + return nil +} + +// GetServer: Get the details of a specified Instance. +func (s *API) GetServer(req *GetServerRequest, opts ...scw.RequestOption) (*GetServerResponse, error) { + var err error + + if req.Zone == "" { + defaultZone, _ := s.client.GetDefaultZone() + req.Zone = defaultZone + } + + if fmt.Sprint(req.Zone) == "" { + return nil, errors.New("field Zone cannot be empty in request") + } + + if fmt.Sprint(req.ServerID) == "" { + return nil, errors.New("field ServerID cannot be empty in request") + } + + scwReq := &scw.ScalewayRequest{ + Method: "GET", + Path: "/instance/v1/zones/" + fmt.Sprint(req.Zone) + "/servers/" + fmt.Sprint(req.ServerID) + "", + } + + var resp GetServerResponse + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +// setServer: +func (s *API) setServer(req *setServerRequest, opts ...scw.RequestOption) (*setServerResponse, error) { + var err error + + if req.Zone == "" { + defaultZone, _ := s.client.GetDefaultZone() + req.Zone = defaultZone + } + + if req.Organization == "" { + defaultOrganization, _ := s.client.GetDefaultOrganizationID() + req.Organization = defaultOrganization + } + + if req.Project == "" { + defaultProject, _ := s.client.GetDefaultProjectID() + req.Project = defaultProject + } + + if fmt.Sprint(req.Zone) == "" { + return nil, errors.New("field Zone cannot be empty in request") + } + + if fmt.Sprint(req.ID) == "" { + return nil, errors.New("field ID cannot be empty in request") + } + + scwReq := &scw.ScalewayRequest{ + Method: "PUT", + Path: "/instance/v1/zones/" + fmt.Sprint(req.Zone) + "/servers/" + fmt.Sprint(req.ID) + "", + } + + err = scwReq.SetBody(req) + if err != nil { + return nil, err + } + + var resp setServerResponse + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +// updateServer: Update the Instance information, such as name, boot mode, or tags. +func (s *API) updateServer(req *UpdateServerRequest, opts ...scw.RequestOption) (*UpdateServerResponse, error) { + var err error + + if req.Zone == "" { + defaultZone, _ := s.client.GetDefaultZone() + req.Zone = defaultZone + } + + if fmt.Sprint(req.Zone) == "" { + return nil, errors.New("field Zone cannot be empty in request") + } + + if fmt.Sprint(req.ServerID) == "" { + return nil, errors.New("field ServerID cannot be empty in request") + } + + scwReq := &scw.ScalewayRequest{ + Method: "PATCH", + Path: "/instance/v1/zones/" + fmt.Sprint(req.Zone) + "/servers/" + fmt.Sprint(req.ServerID) + "", + } + + err = scwReq.SetBody(req) + if err != nil { + return nil, err + } + + var resp UpdateServerResponse + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +// ListServerActions: List all actions (e.g. power on, power off, reboot) that can currently be performed on an Instance. +func (s *API) ListServerActions(req *ListServerActionsRequest, opts ...scw.RequestOption) (*ListServerActionsResponse, error) { + var err error + + if req.Zone == "" { + defaultZone, _ := s.client.GetDefaultZone() + req.Zone = defaultZone + } + + if fmt.Sprint(req.Zone) == "" { + return nil, errors.New("field Zone cannot be empty in request") + } + + if fmt.Sprint(req.ServerID) == "" { + return nil, errors.New("field ServerID cannot be empty in request") + } + + scwReq := &scw.ScalewayRequest{ + Method: "GET", + Path: "/instance/v1/zones/" + fmt.Sprint(req.Zone) + "/servers/" + fmt.Sprint(req.ServerID) + "/action", + } + + var resp ListServerActionsResponse + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +// ServerAction: Perform an action on an Instance. +// Available actions are: +// * `poweron`: Start a stopped Instance. +// * `poweroff`: Fully stop the Instance and release the hypervisor slot. +// * `stop_in_place`: Stop the Instance, but keep the slot on the hypervisor. +// * `reboot`: Stop the instance and restart it. +// * `backup`: Create an image with all the volumes of an Instance. +// * `terminate`: Delete the Instance along with all attached volumes. +// * `enable_routed_ip`: Migrate the Instance to the new network stack. +// +// Keep in mind that terminating an Instance will result in the deletion of all attached volumes, including local and block storage. +// If you want to preserve your local volumes, you should use the `archive` action instead of `terminate`. Similarly, if you want to keep your block storage volumes, you must first detach them before issuing the `terminate` command. +// For more information, read the [Volumes](#path-volumes-list-volumes) documentation. +func (s *API) ServerAction(req *ServerActionRequest, opts ...scw.RequestOption) (*ServerActionResponse, error) { + var err error + + if req.Zone == "" { + defaultZone, _ := s.client.GetDefaultZone() + req.Zone = defaultZone + } + + if fmt.Sprint(req.Zone) == "" { + return nil, errors.New("field Zone cannot be empty in request") + } + + if fmt.Sprint(req.ServerID) == "" { + return nil, errors.New("field ServerID cannot be empty in request") + } + + scwReq := &scw.ScalewayRequest{ + Method: "POST", + Path: "/instance/v1/zones/" + fmt.Sprint(req.Zone) + "/servers/" + fmt.Sprint(req.ServerID) + "/action", + } + + err = scwReq.SetBody(req) + if err != nil { + return nil, err + } + + var resp ServerActionResponse + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +// ListServerUserData: List all user data keys registered on a specified Instance. +func (s *API) ListServerUserData(req *ListServerUserDataRequest, opts ...scw.RequestOption) (*ListServerUserDataResponse, error) { + var err error + + if req.Zone == "" { + defaultZone, _ := s.client.GetDefaultZone() + req.Zone = defaultZone + } + + if fmt.Sprint(req.Zone) == "" { + return nil, errors.New("field Zone cannot be empty in request") + } + + if fmt.Sprint(req.ServerID) == "" { + return nil, errors.New("field ServerID cannot be empty in request") + } + + scwReq := &scw.ScalewayRequest{ + Method: "GET", + Path: "/instance/v1/zones/" + fmt.Sprint(req.Zone) + "/servers/" + fmt.Sprint(req.ServerID) + "/user_data", + } + + var resp ListServerUserDataResponse + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +// DeleteServerUserData: Delete the specified key from an Instance's user data. +func (s *API) DeleteServerUserData(req *DeleteServerUserDataRequest, opts ...scw.RequestOption) error { + var err error + + if req.Zone == "" { + defaultZone, _ := s.client.GetDefaultZone() + req.Zone = defaultZone + } + + if fmt.Sprint(req.Zone) == "" { + return errors.New("field Zone cannot be empty in request") + } + + if fmt.Sprint(req.ServerID) == "" { + return errors.New("field ServerID cannot be empty in request") + } + + if fmt.Sprint(req.Key) == "" { + return errors.New("field Key cannot be empty in request") + } + + scwReq := &scw.ScalewayRequest{ + Method: "DELETE", + Path: "/instance/v1/zones/" + fmt.Sprint(req.Zone) + "/servers/" + fmt.Sprint(req.ServerID) + "/user_data/" + fmt.Sprint(req.Key) + "", + } + + err = s.client.Do(scwReq, nil, opts...) + if err != nil { + return err + } + return nil +} + +// AttachServerVolume: +func (s *API) AttachServerVolume(req *AttachServerVolumeRequest, opts ...scw.RequestOption) (*AttachServerVolumeResponse, error) { + var err error + + if req.Zone == "" { + defaultZone, _ := s.client.GetDefaultZone() + req.Zone = defaultZone + } + + if fmt.Sprint(req.Zone) == "" { + return nil, errors.New("field Zone cannot be empty in request") + } + + if fmt.Sprint(req.ServerID) == "" { + return nil, errors.New("field ServerID cannot be empty in request") + } + + scwReq := &scw.ScalewayRequest{ + Method: "POST", + Path: "/instance/v1/zones/" + fmt.Sprint(req.Zone) + "/servers/" + fmt.Sprint(req.ServerID) + "/attach-volume", + } + + err = scwReq.SetBody(req) + if err != nil { + return nil, err + } + + var resp AttachServerVolumeResponse + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +// DetachServerVolume: +func (s *API) DetachServerVolume(req *DetachServerVolumeRequest, opts ...scw.RequestOption) (*DetachServerVolumeResponse, error) { + var err error + + if req.Zone == "" { + defaultZone, _ := s.client.GetDefaultZone() + req.Zone = defaultZone + } + + if fmt.Sprint(req.Zone) == "" { + return nil, errors.New("field Zone cannot be empty in request") + } + + if fmt.Sprint(req.ServerID) == "" { + return nil, errors.New("field ServerID cannot be empty in request") + } + + scwReq := &scw.ScalewayRequest{ + Method: "POST", + Path: "/instance/v1/zones/" + fmt.Sprint(req.Zone) + "/servers/" + fmt.Sprint(req.ServerID) + "/detach-volume", + } + + err = scwReq.SetBody(req) + if err != nil { + return nil, err + } + + var resp DetachServerVolumeResponse + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +// ListImages: List all existing Instance images. +func (s *API) ListImages(req *ListImagesRequest, opts ...scw.RequestOption) (*ListImagesResponse, error) { + var err error + + if req.Zone == "" { + defaultZone, _ := s.client.GetDefaultZone() + req.Zone = defaultZone + } + + query := url.Values{} + parameter.AddToQuery(query, "organization", req.Organization) + parameter.AddToQuery(query, "per_page", req.PerPage) + parameter.AddToQuery(query, "page", req.Page) + parameter.AddToQuery(query, "name", req.Name) + parameter.AddToQuery(query, "public", req.Public) + parameter.AddToQuery(query, "arch", req.Arch) + parameter.AddToQuery(query, "project", req.Project) + parameter.AddToQuery(query, "tags", req.Tags) + + if fmt.Sprint(req.Zone) == "" { + return nil, errors.New("field Zone cannot be empty in request") + } + + scwReq := &scw.ScalewayRequest{ + Method: "GET", + Path: "/instance/v1/zones/" + fmt.Sprint(req.Zone) + "/images", + Query: query, + } + + var resp ListImagesResponse + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +// GetImage: Get details of an image with the specified ID. +func (s *API) GetImage(req *GetImageRequest, opts ...scw.RequestOption) (*GetImageResponse, error) { + var err error + + if req.Zone == "" { + defaultZone, _ := s.client.GetDefaultZone() + req.Zone = defaultZone + } + + if fmt.Sprint(req.Zone) == "" { + return nil, errors.New("field Zone cannot be empty in request") + } + + if fmt.Sprint(req.ImageID) == "" { + return nil, errors.New("field ImageID cannot be empty in request") + } + + scwReq := &scw.ScalewayRequest{ + Method: "GET", + Path: "/instance/v1/zones/" + fmt.Sprint(req.Zone) + "/images/" + fmt.Sprint(req.ImageID) + "", + } + + var resp GetImageResponse + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +// CreateImage: Create an Instance image from the specified snapshot ID. +func (s *API) CreateImage(req *CreateImageRequest, opts ...scw.RequestOption) (*CreateImageResponse, error) { + var err error + + if req.Zone == "" { + defaultZone, _ := s.client.GetDefaultZone() + req.Zone = defaultZone + } + + defaultProject, exist := s.client.GetDefaultProjectID() + if exist && req.Project == nil && req.Organization == nil { + req.Project = &defaultProject + } + + defaultOrganization, exist := s.client.GetDefaultOrganizationID() + if exist && req.Project == nil && req.Organization == nil { + req.Organization = &defaultOrganization + } + + if req.Name == "" { + req.Name = namegenerator.GetRandomName("img") + } + + if fmt.Sprint(req.Zone) == "" { + return nil, errors.New("field Zone cannot be empty in request") + } + + scwReq := &scw.ScalewayRequest{ + Method: "POST", + Path: "/instance/v1/zones/" + fmt.Sprint(req.Zone) + "/images", + } + + err = scwReq.SetBody(req) + if err != nil { + return nil, err + } + + var resp CreateImageResponse + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +// setImage: Replace all image properties with an image message. +func (s *API) setImage(req *SetImageRequest, opts ...scw.RequestOption) (*setImageResponse, error) { + var err error + + if req.Zone == "" { + defaultZone, _ := s.client.GetDefaultZone() + req.Zone = defaultZone + } + + if req.Organization == "" { + defaultOrganization, _ := s.client.GetDefaultOrganizationID() + req.Organization = defaultOrganization + } + + if req.Project == "" { + defaultProject, _ := s.client.GetDefaultProjectID() + req.Project = defaultProject + } + + if fmt.Sprint(req.Zone) == "" { + return nil, errors.New("field Zone cannot be empty in request") + } + + if fmt.Sprint(req.ID) == "" { + return nil, errors.New("field ID cannot be empty in request") + } + + scwReq := &scw.ScalewayRequest{ + Method: "PUT", + Path: "/instance/v1/zones/" + fmt.Sprint(req.Zone) + "/images/" + fmt.Sprint(req.ID) + "", + } + + err = scwReq.SetBody(req) + if err != nil { + return nil, err + } + + var resp setImageResponse + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +// UpdateImage: Update the properties of an image. +func (s *API) UpdateImage(req *UpdateImageRequest, opts ...scw.RequestOption) (*UpdateImageResponse, error) { + var err error + + if req.Zone == "" { + defaultZone, _ := s.client.GetDefaultZone() + req.Zone = defaultZone + } + + if fmt.Sprint(req.Zone) == "" { + return nil, errors.New("field Zone cannot be empty in request") + } + + if fmt.Sprint(req.ImageID) == "" { + return nil, errors.New("field ImageID cannot be empty in request") + } + + scwReq := &scw.ScalewayRequest{ + Method: "PATCH", + Path: "/instance/v1/zones/" + fmt.Sprint(req.Zone) + "/images/" + fmt.Sprint(req.ImageID) + "", + } + + err = scwReq.SetBody(req) + if err != nil { + return nil, err + } + + var resp UpdateImageResponse + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +// DeleteImage: Delete the image with the specified ID. +func (s *API) DeleteImage(req *DeleteImageRequest, opts ...scw.RequestOption) error { + var err error + + if req.Zone == "" { + defaultZone, _ := s.client.GetDefaultZone() + req.Zone = defaultZone + } + + if fmt.Sprint(req.Zone) == "" { + return errors.New("field Zone cannot be empty in request") + } + + if fmt.Sprint(req.ImageID) == "" { + return errors.New("field ImageID cannot be empty in request") + } + + scwReq := &scw.ScalewayRequest{ + Method: "DELETE", + Path: "/instance/v1/zones/" + fmt.Sprint(req.Zone) + "/images/" + fmt.Sprint(req.ImageID) + "", + } + + err = s.client.Do(scwReq, nil, opts...) + if err != nil { + return err + } + return nil +} + +// ListSnapshots: List all snapshots of an Organization in a specified Availability Zone. +func (s *API) ListSnapshots(req *ListSnapshotsRequest, opts ...scw.RequestOption) (*ListSnapshotsResponse, error) { + var err error + + if req.Zone == "" { + defaultZone, _ := s.client.GetDefaultZone() + req.Zone = defaultZone + } + + query := url.Values{} + parameter.AddToQuery(query, "organization", req.Organization) + parameter.AddToQuery(query, "project", req.Project) + parameter.AddToQuery(query, "per_page", req.PerPage) + parameter.AddToQuery(query, "page", req.Page) + parameter.AddToQuery(query, "name", req.Name) + parameter.AddToQuery(query, "tags", req.Tags) + parameter.AddToQuery(query, "base_volume_id", req.BaseVolumeID) + + if fmt.Sprint(req.Zone) == "" { + return nil, errors.New("field Zone cannot be empty in request") + } + + scwReq := &scw.ScalewayRequest{ + Method: "GET", + Path: "/instance/v1/zones/" + fmt.Sprint(req.Zone) + "/snapshots", + Query: query, + } + + var resp ListSnapshotsResponse + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +// CreateSnapshot: Create a snapshot from a specified volume or from a QCOW2 file in a specified Availability Zone. +func (s *API) CreateSnapshot(req *CreateSnapshotRequest, opts ...scw.RequestOption) (*CreateSnapshotResponse, error) { + var err error + + if req.Zone == "" { + defaultZone, _ := s.client.GetDefaultZone() + req.Zone = defaultZone + } + + defaultProject, exist := s.client.GetDefaultProjectID() + if exist && req.Project == nil && req.Organization == nil { + req.Project = &defaultProject + } + + defaultOrganization, exist := s.client.GetDefaultOrganizationID() + if exist && req.Project == nil && req.Organization == nil { + req.Organization = &defaultOrganization + } + + if req.Name == "" { + req.Name = namegenerator.GetRandomName("snp") + } + + if fmt.Sprint(req.Zone) == "" { + return nil, errors.New("field Zone cannot be empty in request") + } + + scwReq := &scw.ScalewayRequest{ + Method: "POST", + Path: "/instance/v1/zones/" + fmt.Sprint(req.Zone) + "/snapshots", + } + + err = scwReq.SetBody(req) + if err != nil { + return nil, err + } + + var resp CreateSnapshotResponse + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +// GetSnapshot: Get details of a snapshot with the specified ID. +func (s *API) GetSnapshot(req *GetSnapshotRequest, opts ...scw.RequestOption) (*GetSnapshotResponse, error) { + var err error + + if req.Zone == "" { + defaultZone, _ := s.client.GetDefaultZone() + req.Zone = defaultZone + } + + if fmt.Sprint(req.Zone) == "" { + return nil, errors.New("field Zone cannot be empty in request") + } + + if fmt.Sprint(req.SnapshotID) == "" { + return nil, errors.New("field SnapshotID cannot be empty in request") + } + + scwReq := &scw.ScalewayRequest{ + Method: "GET", + Path: "/instance/v1/zones/" + fmt.Sprint(req.Zone) + "/snapshots/" + fmt.Sprint(req.SnapshotID) + "", + } + + var resp GetSnapshotResponse + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +// setSnapshot: Replace all the properties of a snapshot. +func (s *API) setSnapshot(req *setSnapshotRequest, opts ...scw.RequestOption) (*setSnapshotResponse, error) { + var err error + + if req.Zone == "" { + defaultZone, _ := s.client.GetDefaultZone() + req.Zone = defaultZone + } + + if req.Organization == "" { + defaultOrganization, _ := s.client.GetDefaultOrganizationID() + req.Organization = defaultOrganization + } + + if req.Project == "" { + defaultProject, _ := s.client.GetDefaultProjectID() + req.Project = defaultProject + } + + if fmt.Sprint(req.Zone) == "" { + return nil, errors.New("field Zone cannot be empty in request") + } + + if fmt.Sprint(req.SnapshotID) == "" { + return nil, errors.New("field SnapshotID cannot be empty in request") + } + + scwReq := &scw.ScalewayRequest{ + Method: "PUT", + Path: "/instance/v1/zones/" + fmt.Sprint(req.Zone) + "/snapshots/" + fmt.Sprint(req.SnapshotID) + "", + } + + err = scwReq.SetBody(req) + if err != nil { + return nil, err + } + + var resp setSnapshotResponse + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +// UpdateSnapshot: Update the properties of a snapshot. +func (s *API) UpdateSnapshot(req *UpdateSnapshotRequest, opts ...scw.RequestOption) (*UpdateSnapshotResponse, error) { + var err error + + if req.Zone == "" { + defaultZone, _ := s.client.GetDefaultZone() + req.Zone = defaultZone + } + + if fmt.Sprint(req.Zone) == "" { + return nil, errors.New("field Zone cannot be empty in request") + } + + if fmt.Sprint(req.SnapshotID) == "" { + return nil, errors.New("field SnapshotID cannot be empty in request") + } + + scwReq := &scw.ScalewayRequest{ + Method: "PATCH", + Path: "/instance/v1/zones/" + fmt.Sprint(req.Zone) + "/snapshots/" + fmt.Sprint(req.SnapshotID) + "", + } + + err = scwReq.SetBody(req) + if err != nil { + return nil, err + } + + var resp UpdateSnapshotResponse + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +// DeleteSnapshot: Delete the snapshot with the specified ID. +func (s *API) DeleteSnapshot(req *DeleteSnapshotRequest, opts ...scw.RequestOption) error { + var err error + + if req.Zone == "" { + defaultZone, _ := s.client.GetDefaultZone() + req.Zone = defaultZone + } + + if fmt.Sprint(req.Zone) == "" { + return errors.New("field Zone cannot be empty in request") + } + + if fmt.Sprint(req.SnapshotID) == "" { + return errors.New("field SnapshotID cannot be empty in request") + } + + scwReq := &scw.ScalewayRequest{ + Method: "DELETE", + Path: "/instance/v1/zones/" + fmt.Sprint(req.Zone) + "/snapshots/" + fmt.Sprint(req.SnapshotID) + "", + } + + err = s.client.Do(scwReq, nil, opts...) + if err != nil { + return err + } + return nil +} + +// ExportSnapshot: Export a snapshot to a specified S3 bucket in the same region. +func (s *API) ExportSnapshot(req *ExportSnapshotRequest, opts ...scw.RequestOption) (*ExportSnapshotResponse, error) { + var err error + + if req.Zone == "" { + defaultZone, _ := s.client.GetDefaultZone() + req.Zone = defaultZone + } + + if fmt.Sprint(req.Zone) == "" { + return nil, errors.New("field Zone cannot be empty in request") + } + + if fmt.Sprint(req.SnapshotID) == "" { + return nil, errors.New("field SnapshotID cannot be empty in request") + } + + scwReq := &scw.ScalewayRequest{ + Method: "POST", + Path: "/instance/v1/zones/" + fmt.Sprint(req.Zone) + "/snapshots/" + fmt.Sprint(req.SnapshotID) + "/export", + } + + err = scwReq.SetBody(req) + if err != nil { + return nil, err + } + + var resp ExportSnapshotResponse + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +// ListVolumes: List volumes in the specified Availability Zone. You can filter the output by volume type. +func (s *API) ListVolumes(req *ListVolumesRequest, opts ...scw.RequestOption) (*ListVolumesResponse, error) { + var err error + + if req.Zone == "" { + defaultZone, _ := s.client.GetDefaultZone() + req.Zone = defaultZone + } + + query := url.Values{} + parameter.AddToQuery(query, "volume_type", req.VolumeType) + parameter.AddToQuery(query, "per_page", req.PerPage) + parameter.AddToQuery(query, "page", req.Page) + parameter.AddToQuery(query, "organization", req.Organization) + parameter.AddToQuery(query, "project", req.Project) + if len(req.Tags) != 0 { + parameter.AddToQuery(query, "tags", strings.Join(req.Tags, ",")) + } + parameter.AddToQuery(query, "name", req.Name) + + if fmt.Sprint(req.Zone) == "" { + return nil, errors.New("field Zone cannot be empty in request") + } + + scwReq := &scw.ScalewayRequest{ + Method: "GET", + Path: "/instance/v1/zones/" + fmt.Sprint(req.Zone) + "/volumes", + Query: query, + } + + var resp ListVolumesResponse + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +// CreateVolume: Create a volume of a specified type in an Availability Zone. +func (s *API) CreateVolume(req *CreateVolumeRequest, opts ...scw.RequestOption) (*CreateVolumeResponse, error) { + var err error + + if req.Zone == "" { + defaultZone, _ := s.client.GetDefaultZone() + req.Zone = defaultZone + } + + defaultProject, exist := s.client.GetDefaultProjectID() + if exist && req.Project == nil && req.Organization == nil { + req.Project = &defaultProject + } + + defaultOrganization, exist := s.client.GetDefaultOrganizationID() + if exist && req.Project == nil && req.Organization == nil { + req.Organization = &defaultOrganization + } + + if req.Name == "" { + req.Name = namegenerator.GetRandomName("vol") + } + + if fmt.Sprint(req.Zone) == "" { + return nil, errors.New("field Zone cannot be empty in request") + } + + scwReq := &scw.ScalewayRequest{ + Method: "POST", + Path: "/instance/v1/zones/" + fmt.Sprint(req.Zone) + "/volumes", + } + + err = scwReq.SetBody(req) + if err != nil { + return nil, err + } + + var resp CreateVolumeResponse + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +// GetVolume: Get details of a volume with the specified ID. +func (s *API) GetVolume(req *GetVolumeRequest, opts ...scw.RequestOption) (*GetVolumeResponse, error) { + var err error + + if req.Zone == "" { + defaultZone, _ := s.client.GetDefaultZone() + req.Zone = defaultZone + } + + if fmt.Sprint(req.Zone) == "" { + return nil, errors.New("field Zone cannot be empty in request") + } + + if fmt.Sprint(req.VolumeID) == "" { + return nil, errors.New("field VolumeID cannot be empty in request") + } + + scwReq := &scw.ScalewayRequest{ + Method: "GET", + Path: "/instance/v1/zones/" + fmt.Sprint(req.Zone) + "/volumes/" + fmt.Sprint(req.VolumeID) + "", + } + + var resp GetVolumeResponse + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +// UpdateVolume: Replace the name and/or size properties of a volume specified by its ID, with the specified value(s). Any volume name can be changed, however only `b_ssd` volumes can currently be increased in size. +func (s *API) UpdateVolume(req *UpdateVolumeRequest, opts ...scw.RequestOption) (*UpdateVolumeResponse, error) { + var err error + + if req.Zone == "" { + defaultZone, _ := s.client.GetDefaultZone() + req.Zone = defaultZone + } + + if fmt.Sprint(req.Zone) == "" { + return nil, errors.New("field Zone cannot be empty in request") + } + + if fmt.Sprint(req.VolumeID) == "" { + return nil, errors.New("field VolumeID cannot be empty in request") + } + + scwReq := &scw.ScalewayRequest{ + Method: "PATCH", + Path: "/instance/v1/zones/" + fmt.Sprint(req.Zone) + "/volumes/" + fmt.Sprint(req.VolumeID) + "", + } + + err = scwReq.SetBody(req) + if err != nil { + return nil, err + } + + var resp UpdateVolumeResponse + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +// DeleteVolume: Delete the volume with the specified ID. +func (s *API) DeleteVolume(req *DeleteVolumeRequest, opts ...scw.RequestOption) error { + var err error + + if req.Zone == "" { + defaultZone, _ := s.client.GetDefaultZone() + req.Zone = defaultZone + } + + if fmt.Sprint(req.Zone) == "" { + return errors.New("field Zone cannot be empty in request") + } + + if fmt.Sprint(req.VolumeID) == "" { + return errors.New("field VolumeID cannot be empty in request") + } + + scwReq := &scw.ScalewayRequest{ + Method: "DELETE", + Path: "/instance/v1/zones/" + fmt.Sprint(req.Zone) + "/volumes/" + fmt.Sprint(req.VolumeID) + "", + } + + err = s.client.Do(scwReq, nil, opts...) + if err != nil { + return err + } + return nil +} + +// ListSecurityGroups: List all existing security groups. +func (s *API) ListSecurityGroups(req *ListSecurityGroupsRequest, opts ...scw.RequestOption) (*ListSecurityGroupsResponse, error) { + var err error + + if req.Zone == "" { + defaultZone, _ := s.client.GetDefaultZone() + req.Zone = defaultZone + } + + query := url.Values{} + parameter.AddToQuery(query, "name", req.Name) + parameter.AddToQuery(query, "organization", req.Organization) + parameter.AddToQuery(query, "project", req.Project) + if len(req.Tags) != 0 { + parameter.AddToQuery(query, "tags", strings.Join(req.Tags, ",")) + } + parameter.AddToQuery(query, "project_default", req.ProjectDefault) + parameter.AddToQuery(query, "per_page", req.PerPage) + parameter.AddToQuery(query, "page", req.Page) + + if fmt.Sprint(req.Zone) == "" { + return nil, errors.New("field Zone cannot be empty in request") + } + + scwReq := &scw.ScalewayRequest{ + Method: "GET", + Path: "/instance/v1/zones/" + fmt.Sprint(req.Zone) + "/security_groups", + Query: query, + } + + var resp ListSecurityGroupsResponse + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +// CreateSecurityGroup: Create a security group with a specified name and description. +func (s *API) CreateSecurityGroup(req *CreateSecurityGroupRequest, opts ...scw.RequestOption) (*CreateSecurityGroupResponse, error) { + var err error + + if req.Zone == "" { + defaultZone, _ := s.client.GetDefaultZone() + req.Zone = defaultZone + } + + defaultProject, exist := s.client.GetDefaultProjectID() + if exist && req.Project == nil && req.Organization == nil { + req.Project = &defaultProject + } + + defaultOrganization, exist := s.client.GetDefaultOrganizationID() + if exist && req.Project == nil && req.Organization == nil { + req.Organization = &defaultOrganization + } + + if req.Name == "" { + req.Name = namegenerator.GetRandomName("sg") + } + + if fmt.Sprint(req.Zone) == "" { + return nil, errors.New("field Zone cannot be empty in request") + } + + scwReq := &scw.ScalewayRequest{ + Method: "POST", + Path: "/instance/v1/zones/" + fmt.Sprint(req.Zone) + "/security_groups", + } + + err = scwReq.SetBody(req) + if err != nil { + return nil, err + } + + var resp CreateSecurityGroupResponse + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +// GetSecurityGroup: Get the details of a security group with the specified ID. +func (s *API) GetSecurityGroup(req *GetSecurityGroupRequest, opts ...scw.RequestOption) (*GetSecurityGroupResponse, error) { + var err error + + if req.Zone == "" { + defaultZone, _ := s.client.GetDefaultZone() + req.Zone = defaultZone + } + + if fmt.Sprint(req.Zone) == "" { + return nil, errors.New("field Zone cannot be empty in request") + } + + if fmt.Sprint(req.SecurityGroupID) == "" { + return nil, errors.New("field SecurityGroupID cannot be empty in request") + } + + scwReq := &scw.ScalewayRequest{ + Method: "GET", + Path: "/instance/v1/zones/" + fmt.Sprint(req.Zone) + "/security_groups/" + fmt.Sprint(req.SecurityGroupID) + "", + } + + var resp GetSecurityGroupResponse + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +// DeleteSecurityGroup: Delete a security group with the specified ID. +func (s *API) DeleteSecurityGroup(req *DeleteSecurityGroupRequest, opts ...scw.RequestOption) error { + var err error + + if req.Zone == "" { + defaultZone, _ := s.client.GetDefaultZone() + req.Zone = defaultZone + } + + if fmt.Sprint(req.Zone) == "" { + return errors.New("field Zone cannot be empty in request") + } + + if fmt.Sprint(req.SecurityGroupID) == "" { + return errors.New("field SecurityGroupID cannot be empty in request") + } + + scwReq := &scw.ScalewayRequest{ + Method: "DELETE", + Path: "/instance/v1/zones/" + fmt.Sprint(req.Zone) + "/security_groups/" + fmt.Sprint(req.SecurityGroupID) + "", + } + + err = s.client.Do(scwReq, nil, opts...) + if err != nil { + return err + } + return nil +} + +// setSecurityGroup: Replace all security group properties with a security group message. +func (s *API) setSecurityGroup(req *setSecurityGroupRequest, opts ...scw.RequestOption) (*setSecurityGroupResponse, error) { + var err error + + if req.Zone == "" { + defaultZone, _ := s.client.GetDefaultZone() + req.Zone = defaultZone + } + + if req.Organization == "" { + defaultOrganization, _ := s.client.GetDefaultOrganizationID() + req.Organization = defaultOrganization + } + + if req.Project == "" { + defaultProject, _ := s.client.GetDefaultProjectID() + req.Project = defaultProject + } + + if fmt.Sprint(req.Zone) == "" { + return nil, errors.New("field Zone cannot be empty in request") + } + + if fmt.Sprint(req.ID) == "" { + return nil, errors.New("field ID cannot be empty in request") + } + + scwReq := &scw.ScalewayRequest{ + Method: "PUT", + Path: "/instance/v1/zones/" + fmt.Sprint(req.Zone) + "/security_groups/" + fmt.Sprint(req.ID) + "", + } + + err = scwReq.SetBody(req) + if err != nil { + return nil, err + } + + var resp setSecurityGroupResponse + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +// UpdateSecurityGroup: Update the properties of security group. +func (s *API) UpdateSecurityGroup(req *UpdateSecurityGroupRequest, opts ...scw.RequestOption) (*UpdateSecurityGroupResponse, error) { + var err error + + if req.Zone == "" { + defaultZone, _ := s.client.GetDefaultZone() + req.Zone = defaultZone + } + + if fmt.Sprint(req.Zone) == "" { + return nil, errors.New("field Zone cannot be empty in request") + } + + if fmt.Sprint(req.SecurityGroupID) == "" { + return nil, errors.New("field SecurityGroupID cannot be empty in request") + } + + scwReq := &scw.ScalewayRequest{ + Method: "PATCH", + Path: "/instance/v1/zones/" + fmt.Sprint(req.Zone) + "/security_groups/" + fmt.Sprint(req.SecurityGroupID) + "", + } + + err = scwReq.SetBody(req) + if err != nil { + return nil, err + } + + var resp UpdateSecurityGroupResponse + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +// ListDefaultSecurityGroupRules: Lists the default rules applied to all the security groups. +func (s *API) ListDefaultSecurityGroupRules(req *ListDefaultSecurityGroupRulesRequest, opts ...scw.RequestOption) (*ListSecurityGroupRulesResponse, error) { + var err error + + if req.Zone == "" { + defaultZone, _ := s.client.GetDefaultZone() + req.Zone = defaultZone + } + + if fmt.Sprint(req.Zone) == "" { + return nil, errors.New("field Zone cannot be empty in request") + } + + scwReq := &scw.ScalewayRequest{ + Method: "GET", + Path: "/instance/v1/zones/" + fmt.Sprint(req.Zone) + "/security_groups/default/rules", + } + + var resp ListSecurityGroupRulesResponse + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +// ListSecurityGroupRules: List the rules of the a specified security group ID. +func (s *API) ListSecurityGroupRules(req *ListSecurityGroupRulesRequest, opts ...scw.RequestOption) (*ListSecurityGroupRulesResponse, error) { + var err error + + if req.Zone == "" { + defaultZone, _ := s.client.GetDefaultZone() + req.Zone = defaultZone + } + + query := url.Values{} + parameter.AddToQuery(query, "per_page", req.PerPage) + parameter.AddToQuery(query, "page", req.Page) + + if fmt.Sprint(req.Zone) == "" { + return nil, errors.New("field Zone cannot be empty in request") + } + + if fmt.Sprint(req.SecurityGroupID) == "" { + return nil, errors.New("field SecurityGroupID cannot be empty in request") + } + + scwReq := &scw.ScalewayRequest{ + Method: "GET", + Path: "/instance/v1/zones/" + fmt.Sprint(req.Zone) + "/security_groups/" + fmt.Sprint(req.SecurityGroupID) + "/rules", + Query: query, + } + + var resp ListSecurityGroupRulesResponse + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +// CreateSecurityGroupRule: Create a rule in the specified security group ID. +func (s *API) CreateSecurityGroupRule(req *CreateSecurityGroupRuleRequest, opts ...scw.RequestOption) (*CreateSecurityGroupRuleResponse, error) { + var err error + + if req.Zone == "" { + defaultZone, _ := s.client.GetDefaultZone() + req.Zone = defaultZone + } + + if fmt.Sprint(req.Zone) == "" { + return nil, errors.New("field Zone cannot be empty in request") + } + + if fmt.Sprint(req.SecurityGroupID) == "" { + return nil, errors.New("field SecurityGroupID cannot be empty in request") + } + + scwReq := &scw.ScalewayRequest{ + Method: "POST", + Path: "/instance/v1/zones/" + fmt.Sprint(req.Zone) + "/security_groups/" + fmt.Sprint(req.SecurityGroupID) + "/rules", + } + + err = scwReq.SetBody(req) + if err != nil { + return nil, err + } + + var resp CreateSecurityGroupRuleResponse + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +// SetSecurityGroupRules: Replaces the existing rules of the security group with the rules provided. This endpoint supports the update of existing rules, creation of new rules and deletion of existing rules when they are not passed in the request. +func (s *API) SetSecurityGroupRules(req *SetSecurityGroupRulesRequest, opts ...scw.RequestOption) (*SetSecurityGroupRulesResponse, error) { + var err error + + if req.Zone == "" { + defaultZone, _ := s.client.GetDefaultZone() + req.Zone = defaultZone + } + + if fmt.Sprint(req.Zone) == "" { + return nil, errors.New("field Zone cannot be empty in request") + } + + if fmt.Sprint(req.SecurityGroupID) == "" { + return nil, errors.New("field SecurityGroupID cannot be empty in request") + } + + scwReq := &scw.ScalewayRequest{ + Method: "PUT", + Path: "/instance/v1/zones/" + fmt.Sprint(req.Zone) + "/security_groups/" + fmt.Sprint(req.SecurityGroupID) + "/rules", + } + + err = scwReq.SetBody(req) + if err != nil { + return nil, err + } + + var resp SetSecurityGroupRulesResponse + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +// DeleteSecurityGroupRule: Delete a security group rule with the specified ID. +func (s *API) DeleteSecurityGroupRule(req *DeleteSecurityGroupRuleRequest, opts ...scw.RequestOption) error { + var err error + + if req.Zone == "" { + defaultZone, _ := s.client.GetDefaultZone() + req.Zone = defaultZone + } + + if fmt.Sprint(req.Zone) == "" { + return errors.New("field Zone cannot be empty in request") + } + + if fmt.Sprint(req.SecurityGroupID) == "" { + return errors.New("field SecurityGroupID cannot be empty in request") + } + + if fmt.Sprint(req.SecurityGroupRuleID) == "" { + return errors.New("field SecurityGroupRuleID cannot be empty in request") + } + + scwReq := &scw.ScalewayRequest{ + Method: "DELETE", + Path: "/instance/v1/zones/" + fmt.Sprint(req.Zone) + "/security_groups/" + fmt.Sprint(req.SecurityGroupID) + "/rules/" + fmt.Sprint(req.SecurityGroupRuleID) + "", + } + + err = s.client.Do(scwReq, nil, opts...) + if err != nil { + return err + } + return nil +} + +// GetSecurityGroupRule: Get details of a security group rule with the specified ID. +func (s *API) GetSecurityGroupRule(req *GetSecurityGroupRuleRequest, opts ...scw.RequestOption) (*GetSecurityGroupRuleResponse, error) { + var err error + + if req.Zone == "" { + defaultZone, _ := s.client.GetDefaultZone() + req.Zone = defaultZone + } + + if fmt.Sprint(req.Zone) == "" { + return nil, errors.New("field Zone cannot be empty in request") + } + + if fmt.Sprint(req.SecurityGroupID) == "" { + return nil, errors.New("field SecurityGroupID cannot be empty in request") + } + + if fmt.Sprint(req.SecurityGroupRuleID) == "" { + return nil, errors.New("field SecurityGroupRuleID cannot be empty in request") + } + + scwReq := &scw.ScalewayRequest{ + Method: "GET", + Path: "/instance/v1/zones/" + fmt.Sprint(req.Zone) + "/security_groups/" + fmt.Sprint(req.SecurityGroupID) + "/rules/" + fmt.Sprint(req.SecurityGroupRuleID) + "", + } + + var resp GetSecurityGroupRuleResponse + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +// setSecurityGroupRule: Replace all the properties of a rule from a specified security group. +func (s *API) setSecurityGroupRule(req *setSecurityGroupRuleRequest, opts ...scw.RequestOption) (*setSecurityGroupRuleResponse, error) { + var err error + + if req.Zone == "" { + defaultZone, _ := s.client.GetDefaultZone() + req.Zone = defaultZone + } + + if fmt.Sprint(req.Zone) == "" { + return nil, errors.New("field Zone cannot be empty in request") + } + + if fmt.Sprint(req.SecurityGroupID) == "" { + return nil, errors.New("field SecurityGroupID cannot be empty in request") + } + + if fmt.Sprint(req.SecurityGroupRuleID) == "" { + return nil, errors.New("field SecurityGroupRuleID cannot be empty in request") + } + + scwReq := &scw.ScalewayRequest{ + Method: "PUT", + Path: "/instance/v1/zones/" + fmt.Sprint(req.Zone) + "/security_groups/" + fmt.Sprint(req.SecurityGroupID) + "/rules/" + fmt.Sprint(req.SecurityGroupRuleID) + "", + } + + err = scwReq.SetBody(req) + if err != nil { + return nil, err + } + + var resp setSecurityGroupRuleResponse + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +// UpdateSecurityGroupRule: Update the properties of a rule from a specified security group. +func (s *API) UpdateSecurityGroupRule(req *UpdateSecurityGroupRuleRequest, opts ...scw.RequestOption) (*UpdateSecurityGroupRuleResponse, error) { + var err error + + if req.Zone == "" { + defaultZone, _ := s.client.GetDefaultZone() + req.Zone = defaultZone + } + + if fmt.Sprint(req.Zone) == "" { + return nil, errors.New("field Zone cannot be empty in request") + } + + if fmt.Sprint(req.SecurityGroupID) == "" { + return nil, errors.New("field SecurityGroupID cannot be empty in request") + } + + if fmt.Sprint(req.SecurityGroupRuleID) == "" { + return nil, errors.New("field SecurityGroupRuleID cannot be empty in request") + } + + scwReq := &scw.ScalewayRequest{ + Method: "PATCH", + Path: "/instance/v1/zones/" + fmt.Sprint(req.Zone) + "/security_groups/" + fmt.Sprint(req.SecurityGroupID) + "/rules/" + fmt.Sprint(req.SecurityGroupRuleID) + "", + } + + err = scwReq.SetBody(req) + if err != nil { + return nil, err + } + + var resp UpdateSecurityGroupRuleResponse + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +// ListPlacementGroups: List all placement groups in a specified Availability Zone. +func (s *API) ListPlacementGroups(req *ListPlacementGroupsRequest, opts ...scw.RequestOption) (*ListPlacementGroupsResponse, error) { + var err error + + if req.Zone == "" { + defaultZone, _ := s.client.GetDefaultZone() + req.Zone = defaultZone + } + + query := url.Values{} + parameter.AddToQuery(query, "per_page", req.PerPage) + parameter.AddToQuery(query, "page", req.Page) + parameter.AddToQuery(query, "organization", req.Organization) + parameter.AddToQuery(query, "project", req.Project) + if len(req.Tags) != 0 { + parameter.AddToQuery(query, "tags", strings.Join(req.Tags, ",")) + } + parameter.AddToQuery(query, "name", req.Name) + + if fmt.Sprint(req.Zone) == "" { + return nil, errors.New("field Zone cannot be empty in request") + } + + scwReq := &scw.ScalewayRequest{ + Method: "GET", + Path: "/instance/v1/zones/" + fmt.Sprint(req.Zone) + "/placement_groups", + Query: query, + } + + var resp ListPlacementGroupsResponse + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +// CreatePlacementGroup: Create a new placement group in a specified Availability Zone. +func (s *API) CreatePlacementGroup(req *CreatePlacementGroupRequest, opts ...scw.RequestOption) (*CreatePlacementGroupResponse, error) { + var err error + + if req.Zone == "" { + defaultZone, _ := s.client.GetDefaultZone() + req.Zone = defaultZone + } + + defaultProject, exist := s.client.GetDefaultProjectID() + if exist && req.Project == nil && req.Organization == nil { + req.Project = &defaultProject + } + + defaultOrganization, exist := s.client.GetDefaultOrganizationID() + if exist && req.Project == nil && req.Organization == nil { + req.Organization = &defaultOrganization + } + + if req.Name == "" { + req.Name = namegenerator.GetRandomName("pg") + } + + if fmt.Sprint(req.Zone) == "" { + return nil, errors.New("field Zone cannot be empty in request") + } + + scwReq := &scw.ScalewayRequest{ + Method: "POST", + Path: "/instance/v1/zones/" + fmt.Sprint(req.Zone) + "/placement_groups", + } + + err = scwReq.SetBody(req) + if err != nil { + return nil, err + } + + var resp CreatePlacementGroupResponse + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +// GetPlacementGroup: Get the specified placement group. +func (s *API) GetPlacementGroup(req *GetPlacementGroupRequest, opts ...scw.RequestOption) (*GetPlacementGroupResponse, error) { + var err error + + if req.Zone == "" { + defaultZone, _ := s.client.GetDefaultZone() + req.Zone = defaultZone + } + + if fmt.Sprint(req.Zone) == "" { + return nil, errors.New("field Zone cannot be empty in request") + } + + if fmt.Sprint(req.PlacementGroupID) == "" { + return nil, errors.New("field PlacementGroupID cannot be empty in request") + } + + scwReq := &scw.ScalewayRequest{ + Method: "GET", + Path: "/instance/v1/zones/" + fmt.Sprint(req.Zone) + "/placement_groups/" + fmt.Sprint(req.PlacementGroupID) + "", + } + + var resp GetPlacementGroupResponse + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +// SetPlacementGroup: Set all parameters of the specified placement group. +func (s *API) SetPlacementGroup(req *SetPlacementGroupRequest, opts ...scw.RequestOption) (*SetPlacementGroupResponse, error) { + var err error + + if req.Zone == "" { + defaultZone, _ := s.client.GetDefaultZone() + req.Zone = defaultZone + } + + if req.Organization == "" { + defaultOrganization, _ := s.client.GetDefaultOrganizationID() + req.Organization = defaultOrganization + } + + if req.Project == "" { + defaultProject, _ := s.client.GetDefaultProjectID() + req.Project = defaultProject + } + + if fmt.Sprint(req.Zone) == "" { + return nil, errors.New("field Zone cannot be empty in request") + } + + if fmt.Sprint(req.PlacementGroupID) == "" { + return nil, errors.New("field PlacementGroupID cannot be empty in request") + } + + scwReq := &scw.ScalewayRequest{ + Method: "PUT", + Path: "/instance/v1/zones/" + fmt.Sprint(req.Zone) + "/placement_groups/" + fmt.Sprint(req.PlacementGroupID) + "", + } + + err = scwReq.SetBody(req) + if err != nil { + return nil, err + } + + var resp SetPlacementGroupResponse + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +// UpdatePlacementGroup: Update one or more parameter of the specified placement group. +func (s *API) UpdatePlacementGroup(req *UpdatePlacementGroupRequest, opts ...scw.RequestOption) (*UpdatePlacementGroupResponse, error) { + var err error + + if req.Zone == "" { + defaultZone, _ := s.client.GetDefaultZone() + req.Zone = defaultZone + } + + if fmt.Sprint(req.Zone) == "" { + return nil, errors.New("field Zone cannot be empty in request") + } + + if fmt.Sprint(req.PlacementGroupID) == "" { + return nil, errors.New("field PlacementGroupID cannot be empty in request") + } + + scwReq := &scw.ScalewayRequest{ + Method: "PATCH", + Path: "/instance/v1/zones/" + fmt.Sprint(req.Zone) + "/placement_groups/" + fmt.Sprint(req.PlacementGroupID) + "", + } + + err = scwReq.SetBody(req) + if err != nil { + return nil, err + } + + var resp UpdatePlacementGroupResponse + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +// DeletePlacementGroup: Delete the specified placement group. +func (s *API) DeletePlacementGroup(req *DeletePlacementGroupRequest, opts ...scw.RequestOption) error { + var err error + + if req.Zone == "" { + defaultZone, _ := s.client.GetDefaultZone() + req.Zone = defaultZone + } + + if fmt.Sprint(req.Zone) == "" { + return errors.New("field Zone cannot be empty in request") + } + + if fmt.Sprint(req.PlacementGroupID) == "" { + return errors.New("field PlacementGroupID cannot be empty in request") + } + + scwReq := &scw.ScalewayRequest{ + Method: "DELETE", + Path: "/instance/v1/zones/" + fmt.Sprint(req.Zone) + "/placement_groups/" + fmt.Sprint(req.PlacementGroupID) + "", + } + + err = s.client.Do(scwReq, nil, opts...) + if err != nil { + return err + } + return nil +} + +// GetPlacementGroupServers: Get all Instances belonging to the specified placement group. +func (s *API) GetPlacementGroupServers(req *GetPlacementGroupServersRequest, opts ...scw.RequestOption) (*GetPlacementGroupServersResponse, error) { + var err error + + if req.Zone == "" { + defaultZone, _ := s.client.GetDefaultZone() + req.Zone = defaultZone + } + + if fmt.Sprint(req.Zone) == "" { + return nil, errors.New("field Zone cannot be empty in request") + } + + if fmt.Sprint(req.PlacementGroupID) == "" { + return nil, errors.New("field PlacementGroupID cannot be empty in request") + } + + scwReq := &scw.ScalewayRequest{ + Method: "GET", + Path: "/instance/v1/zones/" + fmt.Sprint(req.Zone) + "/placement_groups/" + fmt.Sprint(req.PlacementGroupID) + "/servers", + } + + var resp GetPlacementGroupServersResponse + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +// SetPlacementGroupServers: Set all Instances belonging to the specified placement group. +func (s *API) SetPlacementGroupServers(req *SetPlacementGroupServersRequest, opts ...scw.RequestOption) (*SetPlacementGroupServersResponse, error) { + var err error + + if req.Zone == "" { + defaultZone, _ := s.client.GetDefaultZone() + req.Zone = defaultZone + } + + if fmt.Sprint(req.Zone) == "" { + return nil, errors.New("field Zone cannot be empty in request") + } + + if fmt.Sprint(req.PlacementGroupID) == "" { + return nil, errors.New("field PlacementGroupID cannot be empty in request") + } + + scwReq := &scw.ScalewayRequest{ + Method: "PUT", + Path: "/instance/v1/zones/" + fmt.Sprint(req.Zone) + "/placement_groups/" + fmt.Sprint(req.PlacementGroupID) + "/servers", + } + + err = scwReq.SetBody(req) + if err != nil { + return nil, err + } + + var resp SetPlacementGroupServersResponse + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +// UpdatePlacementGroupServers: Update all Instances belonging to the specified placement group. +func (s *API) UpdatePlacementGroupServers(req *UpdatePlacementGroupServersRequest, opts ...scw.RequestOption) (*UpdatePlacementGroupServersResponse, error) { + var err error + + if req.Zone == "" { + defaultZone, _ := s.client.GetDefaultZone() + req.Zone = defaultZone + } + + if fmt.Sprint(req.Zone) == "" { + return nil, errors.New("field Zone cannot be empty in request") + } + + if fmt.Sprint(req.PlacementGroupID) == "" { + return nil, errors.New("field PlacementGroupID cannot be empty in request") + } + + scwReq := &scw.ScalewayRequest{ + Method: "PATCH", + Path: "/instance/v1/zones/" + fmt.Sprint(req.Zone) + "/placement_groups/" + fmt.Sprint(req.PlacementGroupID) + "/servers", + } + + err = scwReq.SetBody(req) + if err != nil { + return nil, err + } + + var resp UpdatePlacementGroupServersResponse + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +// ListIPs: List all flexible IPs in a specified zone. +func (s *API) ListIPs(req *ListIPsRequest, opts ...scw.RequestOption) (*ListIPsResponse, error) { + var err error + + if req.Zone == "" { + defaultZone, _ := s.client.GetDefaultZone() + req.Zone = defaultZone + } + + query := url.Values{} + parameter.AddToQuery(query, "project", req.Project) + parameter.AddToQuery(query, "organization", req.Organization) + if len(req.Tags) != 0 { + parameter.AddToQuery(query, "tags", strings.Join(req.Tags, ",")) + } + parameter.AddToQuery(query, "name", req.Name) + parameter.AddToQuery(query, "per_page", req.PerPage) + parameter.AddToQuery(query, "page", req.Page) + parameter.AddToQuery(query, "type", req.Type) + + if fmt.Sprint(req.Zone) == "" { + return nil, errors.New("field Zone cannot be empty in request") + } + + scwReq := &scw.ScalewayRequest{ + Method: "GET", + Path: "/instance/v1/zones/" + fmt.Sprint(req.Zone) + "/ips", + Query: query, + } + + var resp ListIPsResponse + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +// CreateIP: Reserve a flexible IP and attach it to the specified Instance. +func (s *API) CreateIP(req *CreateIPRequest, opts ...scw.RequestOption) (*CreateIPResponse, error) { + var err error + + if req.Zone == "" { + defaultZone, _ := s.client.GetDefaultZone() + req.Zone = defaultZone + } + + defaultProject, exist := s.client.GetDefaultProjectID() + if exist && req.Project == nil && req.Organization == nil { + req.Project = &defaultProject + } + + defaultOrganization, exist := s.client.GetDefaultOrganizationID() + if exist && req.Project == nil && req.Organization == nil { + req.Organization = &defaultOrganization + } + + if fmt.Sprint(req.Zone) == "" { + return nil, errors.New("field Zone cannot be empty in request") + } + + scwReq := &scw.ScalewayRequest{ + Method: "POST", + Path: "/instance/v1/zones/" + fmt.Sprint(req.Zone) + "/ips", + } + + err = scwReq.SetBody(req) + if err != nil { + return nil, err + } + + var resp CreateIPResponse + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +// GetIP: Get details of an IP with the specified ID or address. +func (s *API) GetIP(req *GetIPRequest, opts ...scw.RequestOption) (*GetIPResponse, error) { + var err error + + if req.Zone == "" { + defaultZone, _ := s.client.GetDefaultZone() + req.Zone = defaultZone + } + + if fmt.Sprint(req.Zone) == "" { + return nil, errors.New("field Zone cannot be empty in request") + } + + if fmt.Sprint(req.IP) == "" { + return nil, errors.New("field IP cannot be empty in request") + } + + scwReq := &scw.ScalewayRequest{ + Method: "GET", + Path: "/instance/v1/zones/" + fmt.Sprint(req.Zone) + "/ips/" + fmt.Sprint(req.IP) + "", + } + + var resp GetIPResponse + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +// UpdateIP: Update a flexible IP in the specified zone with the specified ID. +func (s *API) UpdateIP(req *UpdateIPRequest, opts ...scw.RequestOption) (*UpdateIPResponse, error) { + var err error + + if req.Zone == "" { + defaultZone, _ := s.client.GetDefaultZone() + req.Zone = defaultZone + } + + if fmt.Sprint(req.Zone) == "" { + return nil, errors.New("field Zone cannot be empty in request") + } + + if fmt.Sprint(req.IP) == "" { + return nil, errors.New("field IP cannot be empty in request") + } + + scwReq := &scw.ScalewayRequest{ + Method: "PATCH", + Path: "/instance/v1/zones/" + fmt.Sprint(req.Zone) + "/ips/" + fmt.Sprint(req.IP) + "", + } + + err = scwReq.SetBody(req) + if err != nil { + return nil, err + } + + var resp UpdateIPResponse + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +// DeleteIP: Delete the IP with the specified ID. +func (s *API) DeleteIP(req *DeleteIPRequest, opts ...scw.RequestOption) error { + var err error + + if req.Zone == "" { + defaultZone, _ := s.client.GetDefaultZone() + req.Zone = defaultZone + } + + if fmt.Sprint(req.Zone) == "" { + return errors.New("field Zone cannot be empty in request") + } + + if fmt.Sprint(req.IP) == "" { + return errors.New("field IP cannot be empty in request") + } + + scwReq := &scw.ScalewayRequest{ + Method: "DELETE", + Path: "/instance/v1/zones/" + fmt.Sprint(req.Zone) + "/ips/" + fmt.Sprint(req.IP) + "", + } + + err = s.client.Do(scwReq, nil, opts...) + if err != nil { + return err + } + return nil +} + +// ListPrivateNICs: List all private NICs of a specified Instance. +func (s *API) ListPrivateNICs(req *ListPrivateNICsRequest, opts ...scw.RequestOption) (*ListPrivateNICsResponse, error) { + var err error + + if req.Zone == "" { + defaultZone, _ := s.client.GetDefaultZone() + req.Zone = defaultZone + } + + query := url.Values{} + if len(req.Tags) != 0 { + parameter.AddToQuery(query, "tags", strings.Join(req.Tags, ",")) + } + parameter.AddToQuery(query, "per_page", req.PerPage) + parameter.AddToQuery(query, "page", req.Page) + + if fmt.Sprint(req.Zone) == "" { + return nil, errors.New("field Zone cannot be empty in request") + } + + if fmt.Sprint(req.ServerID) == "" { + return nil, errors.New("field ServerID cannot be empty in request") + } + + scwReq := &scw.ScalewayRequest{ + Method: "GET", + Path: "/instance/v1/zones/" + fmt.Sprint(req.Zone) + "/servers/" + fmt.Sprint(req.ServerID) + "/private_nics", + Query: query, + } + + var resp ListPrivateNICsResponse + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +// CreatePrivateNIC: Create a private NIC connecting an Instance to a Private Network. +func (s *API) CreatePrivateNIC(req *CreatePrivateNICRequest, opts ...scw.RequestOption) (*CreatePrivateNICResponse, error) { + var err error + + if req.Zone == "" { + defaultZone, _ := s.client.GetDefaultZone() + req.Zone = defaultZone + } + + if fmt.Sprint(req.Zone) == "" { + return nil, errors.New("field Zone cannot be empty in request") + } + + if fmt.Sprint(req.ServerID) == "" { + return nil, errors.New("field ServerID cannot be empty in request") + } + + scwReq := &scw.ScalewayRequest{ + Method: "POST", + Path: "/instance/v1/zones/" + fmt.Sprint(req.Zone) + "/servers/" + fmt.Sprint(req.ServerID) + "/private_nics", + } + + err = scwReq.SetBody(req) + if err != nil { + return nil, err + } + + var resp CreatePrivateNICResponse + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +// GetPrivateNIC: Get private NIC properties. +func (s *API) GetPrivateNIC(req *GetPrivateNICRequest, opts ...scw.RequestOption) (*GetPrivateNICResponse, error) { + var err error + + if req.Zone == "" { + defaultZone, _ := s.client.GetDefaultZone() + req.Zone = defaultZone + } + + if fmt.Sprint(req.Zone) == "" { + return nil, errors.New("field Zone cannot be empty in request") + } + + if fmt.Sprint(req.ServerID) == "" { + return nil, errors.New("field ServerID cannot be empty in request") + } + + if fmt.Sprint(req.PrivateNicID) == "" { + return nil, errors.New("field PrivateNicID cannot be empty in request") + } + + scwReq := &scw.ScalewayRequest{ + Method: "GET", + Path: "/instance/v1/zones/" + fmt.Sprint(req.Zone) + "/servers/" + fmt.Sprint(req.ServerID) + "/private_nics/" + fmt.Sprint(req.PrivateNicID) + "", + } + + var resp GetPrivateNICResponse + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +// UpdatePrivateNIC: Update one or more parameter(s) of a specified private NIC. +func (s *API) UpdatePrivateNIC(req *UpdatePrivateNICRequest, opts ...scw.RequestOption) (*PrivateNIC, error) { + var err error + + if req.Zone == "" { + defaultZone, _ := s.client.GetDefaultZone() + req.Zone = defaultZone + } + + if fmt.Sprint(req.Zone) == "" { + return nil, errors.New("field Zone cannot be empty in request") + } + + if fmt.Sprint(req.ServerID) == "" { + return nil, errors.New("field ServerID cannot be empty in request") + } + + if fmt.Sprint(req.PrivateNicID) == "" { + return nil, errors.New("field PrivateNicID cannot be empty in request") + } + + scwReq := &scw.ScalewayRequest{ + Method: "PATCH", + Path: "/instance/v1/zones/" + fmt.Sprint(req.Zone) + "/servers/" + fmt.Sprint(req.ServerID) + "/private_nics/" + fmt.Sprint(req.PrivateNicID) + "", + } + + err = scwReq.SetBody(req) + if err != nil { + return nil, err + } + + var resp PrivateNIC + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +// DeletePrivateNIC: Delete a private NIC. +func (s *API) DeletePrivateNIC(req *DeletePrivateNICRequest, opts ...scw.RequestOption) error { + var err error + + if req.Zone == "" { + defaultZone, _ := s.client.GetDefaultZone() + req.Zone = defaultZone + } + + if fmt.Sprint(req.Zone) == "" { + return errors.New("field Zone cannot be empty in request") + } + + if fmt.Sprint(req.ServerID) == "" { + return errors.New("field ServerID cannot be empty in request") + } + + if fmt.Sprint(req.PrivateNicID) == "" { + return errors.New("field PrivateNicID cannot be empty in request") + } + + scwReq := &scw.ScalewayRequest{ + Method: "DELETE", + Path: "/instance/v1/zones/" + fmt.Sprint(req.Zone) + "/servers/" + fmt.Sprint(req.ServerID) + "/private_nics/" + fmt.Sprint(req.PrivateNicID) + "", + } + + err = s.client.Do(scwReq, nil, opts...) + if err != nil { + return err + } + return nil +} + +// Deprecated: ListBootscripts: List bootscripts. +func (s *API) ListBootscripts(req *ListBootscriptsRequest, opts ...scw.RequestOption) (*ListBootscriptsResponse, error) { + var err error + + if req.Zone == "" { + defaultZone, _ := s.client.GetDefaultZone() + req.Zone = defaultZone + } + + query := url.Values{} + parameter.AddToQuery(query, "arch", req.Arch) + parameter.AddToQuery(query, "title", req.Title) + parameter.AddToQuery(query, "default", req.Default) + parameter.AddToQuery(query, "public", req.Public) + parameter.AddToQuery(query, "per_page", req.PerPage) + parameter.AddToQuery(query, "page", req.Page) + + if fmt.Sprint(req.Zone) == "" { + return nil, errors.New("field Zone cannot be empty in request") + } + + scwReq := &scw.ScalewayRequest{ + Method: "GET", + Path: "/instance/v1/zones/" + fmt.Sprint(req.Zone) + "/bootscripts", + Query: query, + } + + var resp ListBootscriptsResponse + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +// Deprecated: GetBootscript: Get details of a bootscript with the specified ID. +func (s *API) GetBootscript(req *GetBootscriptRequest, opts ...scw.RequestOption) (*GetBootscriptResponse, error) { + var err error + + if req.Zone == "" { + defaultZone, _ := s.client.GetDefaultZone() + req.Zone = defaultZone + } + + if fmt.Sprint(req.Zone) == "" { + return nil, errors.New("field Zone cannot be empty in request") + } + + if fmt.Sprint(req.BootscriptID) == "" { + return nil, errors.New("field BootscriptID cannot be empty in request") + } + + scwReq := &scw.ScalewayRequest{ + Method: "GET", + Path: "/instance/v1/zones/" + fmt.Sprint(req.Zone) + "/bootscripts/" + fmt.Sprint(req.BootscriptID) + "", + } + + var resp GetBootscriptResponse + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +// GetDashboard: +func (s *API) GetDashboard(req *GetDashboardRequest, opts ...scw.RequestOption) (*GetDashboardResponse, error) { + var err error + + if req.Zone == "" { + defaultZone, _ := s.client.GetDefaultZone() + req.Zone = defaultZone + } + + query := url.Values{} + parameter.AddToQuery(query, "organization", req.Organization) + parameter.AddToQuery(query, "project", req.Project) + + if fmt.Sprint(req.Zone) == "" { + return nil, errors.New("field Zone cannot be empty in request") + } + + scwReq := &scw.ScalewayRequest{ + Method: "GET", + Path: "/instance/v1/zones/" + fmt.Sprint(req.Zone) + "/dashboard", + Query: query, + } + + var resp GetDashboardResponse + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +// PlanBlockMigration: Given a volume or snapshot, returns the migration plan for a call to the RPC ApplyBlockMigration. This plan will include zero or one volume, and zero or more snapshots, which will need to be migrated together. This RPC does not perform the actual migration itself, ApplyBlockMigration must be used. The validation_key value returned by this call must be provided to the ApplyBlockMigration call to confirm that all resources listed in the plan should be migrated. +func (s *API) PlanBlockMigration(req *PlanBlockMigrationRequest, opts ...scw.RequestOption) (*MigrationPlan, error) { + var err error + + if req.Zone == "" { + defaultZone, _ := s.client.GetDefaultZone() + req.Zone = defaultZone + } + + if fmt.Sprint(req.Zone) == "" { + return nil, errors.New("field Zone cannot be empty in request") + } + + scwReq := &scw.ScalewayRequest{ + Method: "POST", + Path: "/instance/v1/zones/" + fmt.Sprint(req.Zone) + "/block-migration/plan", + } + + err = scwReq.SetBody(req) + if err != nil { + return nil, err + } + + var resp MigrationPlan + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +// ApplyBlockMigration: To be used, this RPC must be preceded by a call to PlanBlockMigration. To migrate all resources mentioned in the MigrationPlan, the validation_key returned in the MigrationPlan must be provided. +func (s *API) ApplyBlockMigration(req *ApplyBlockMigrationRequest, opts ...scw.RequestOption) error { + var err error + + if req.Zone == "" { + defaultZone, _ := s.client.GetDefaultZone() + req.Zone = defaultZone + } + + if fmt.Sprint(req.Zone) == "" { + return errors.New("field Zone cannot be empty in request") + } + + scwReq := &scw.ScalewayRequest{ + Method: "POST", + Path: "/instance/v1/zones/" + fmt.Sprint(req.Zone) + "/block-migration/apply", + } + + err = scwReq.SetBody(req) + if err != nil { + return err + } + + err = s.client.Do(scwReq, nil, opts...) + if err != nil { + return err + } + return nil +} diff --git a/vendor/github.com/scaleway/scaleway-sdk-go/api/instance/v1/instance_utils.go b/vendor/github.com/scaleway/scaleway-sdk-go/api/instance/v1/instance_utils.go new file mode 100644 index 000000000..0d5401df9 --- /dev/null +++ b/vendor/github.com/scaleway/scaleway-sdk-go/api/instance/v1/instance_utils.go @@ -0,0 +1,434 @@ +package instance + +import ( + "encoding/json" + "fmt" + "sync" + "time" + + "github.com/scaleway/scaleway-sdk-go/internal/async" + + "github.com/scaleway/scaleway-sdk-go/internal/errors" + "github.com/scaleway/scaleway-sdk-go/scw" +) + +var ( + resourceLock sync.Map +) + +// lockResource locks a resource from a specific resourceID +func lockResource(resourceID string) *sync.Mutex { + v, _ := resourceLock.LoadOrStore(resourceID, &sync.Mutex{}) + mutex := v.(*sync.Mutex) + mutex.Lock() + return mutex +} + +// lockServer locks a server from its zone and its ID +func lockServer(zone scw.Zone, serverID string) *sync.Mutex { + return lockResource(fmt.Sprint("server", zone, serverID)) +} + +// AttachIPRequest contains the parameters to attach an IP to a server +// +// Deprecated: UpdateIPRequest should be used instead +type AttachIPRequest struct { + Zone scw.Zone `json:"-"` + IP string `json:"-"` + ServerID string `json:"server_id"` +} + +// AttachIPResponse contains the updated IP after attaching +// +// Deprecated: UpdateIPResponse should be used instead +type AttachIPResponse struct { + IP *IP +} + +// AttachIP attaches an IP to a server. +// +// Deprecated: UpdateIP() should be used instead +func (s *API) AttachIP(req *AttachIPRequest, opts ...scw.RequestOption) (*AttachIPResponse, error) { + ipResponse, err := s.UpdateIP(&UpdateIPRequest{ + Zone: req.Zone, + IP: req.IP, + Server: &NullableStringValue{Value: req.ServerID}, + }) + if err != nil { + return nil, err + } + + return &AttachIPResponse{IP: ipResponse.IP}, nil +} + +// DetachIPRequest contains the parameters to detach an IP from a server +// +// Deprecated: UpdateIPRequest should be used instead +type DetachIPRequest struct { + Zone scw.Zone `json:"-"` + IP string `json:"-"` +} + +// DetachIPResponse contains the updated IP after detaching +// +// Deprecated: UpdateIPResponse should be used instead +type DetachIPResponse struct { + IP *IP +} + +// DetachIP detaches an IP from a server. +// +// Deprecated: UpdateIP() should be used instead +func (s *API) DetachIP(req *DetachIPRequest, opts ...scw.RequestOption) (*DetachIPResponse, error) { + ipResponse, err := s.UpdateIP(&UpdateIPRequest{ + Zone: req.Zone, + IP: req.IP, + Server: &NullableStringValue{Null: true}, + }) + if err != nil { + return nil, err + } + + return &DetachIPResponse{IP: ipResponse.IP}, nil +} + +// AttachVolumeRequest contains the parameters to attach a volume to a server +type AttachVolumeRequest struct { + Zone scw.Zone `json:"-"` + ServerID string `json:"-"` + VolumeID string `json:"-"` +} + +// AttachVolumeResponse contains the updated server after attaching a volume +type AttachVolumeResponse struct { + Server *Server `json:"-"` +} + +// volumesToVolumeTemplates converts a map of *Volume to a map of *VolumeTemplate +// so it can be used in a UpdateServer request +func volumesToVolumeTemplates(volumes map[string]*VolumeServer) map[string]*VolumeServerTemplate { + volumeTemplates := map[string]*VolumeServerTemplate{} + for key, volume := range volumes { + volumeTemplate := &VolumeServerTemplate{ + ID: &volume.ID, + } + + if volume.Name != "" { + volumeTemplate.Name = &volume.Name + } + + if volume.VolumeType == VolumeServerVolumeTypeSbsVolume { + volumeTemplate.VolumeType = VolumeVolumeTypeSbsVolume + } + + volumeTemplates[key] = volumeTemplate + } + return volumeTemplates +} + +// AttachVolume attaches a volume to a server +// +// Note: Implementation is thread-safe. +func (s *API) AttachVolume(req *AttachVolumeRequest, opts ...scw.RequestOption) (*AttachVolumeResponse, error) { + defer lockServer(req.Zone, req.ServerID).Unlock() + // check where the volume comes from + volume, err := s.getUnknownVolume(&getUnknownVolumeRequest{ + Zone: req.Zone, + VolumeID: req.VolumeID, + }) + if err != nil { + return nil, err + } + + // get server with volumes + getServerResponse, err := s.GetServer(&GetServerRequest{ + Zone: req.Zone, + ServerID: req.ServerID, + }) + if err != nil { + return nil, err + } + volumes := getServerResponse.Server.Volumes + + newVolumes := volumesToVolumeTemplates(volumes) + + // add volume to volumes list + // We loop through all the possible volume keys (0 to len(volumes)) + // to find a non existing key and assign it to the requested volume. + // A key should always be found. However we return an error if no keys were found. + found := false + for i := 0; i <= len(volumes); i++ { + key := fmt.Sprintf("%d", i) + if _, ok := newVolumes[key]; !ok { + newVolumes[key] = &VolumeServerTemplate{ + ID: &req.VolumeID, + } + if volume.Type == VolumeVolumeTypeSbsVolume { + newVolumes[key].VolumeType = VolumeVolumeTypeSbsVolume + } else { + newVolumes[key].Name = &req.VolumeID + } + + found = true + break + } + } + + if !found { + return nil, fmt.Errorf("could not find key to attach volume %s", req.VolumeID) + } + + // update server + updateServerResponse, err := s.updateServer(&UpdateServerRequest{ + Zone: req.Zone, + ServerID: req.ServerID, + Volumes: &newVolumes, + }) + if err != nil { + return nil, err + } + + return &AttachVolumeResponse{Server: updateServerResponse.Server}, nil +} + +// DetachVolumeRequest contains the parameters to detach a volume from a server +type DetachVolumeRequest struct { + Zone scw.Zone `json:"-"` + VolumeID string `json:"-"` + // IsBlockVolume should be set to true if volume is from block API, + // can be set to false if volume is from instance API, + // if left nil both API will be tried + IsBlockVolume *bool `json:"-"` +} + +// DetachVolumeResponse contains the updated server after detaching a volume +type DetachVolumeResponse struct { + Server *Server `json:"-"` +} + +// DetachVolume detaches a volume from a server +// +// Note: Implementation is thread-safe. +func (s *API) DetachVolume(req *DetachVolumeRequest, opts ...scw.RequestOption) (*DetachVolumeResponse, error) { + volume, err := s.getUnknownVolume(&getUnknownVolumeRequest{ + Zone: req.Zone, + VolumeID: req.VolumeID, + }) + if err != nil { + return nil, err + } + + if volume.ServerID == nil { + return nil, errors.New("volume should be attached to a server") + } + + defer lockServer(req.Zone, *volume.ServerID).Unlock() + // get server with volumes + getServerResponse, err := s.GetServer(&GetServerRequest{ + Zone: req.Zone, + ServerID: *volume.ServerID, + }) + if err != nil { + return nil, err + } + volumes := getServerResponse.Server.Volumes + // remove volume from volumes list + for key, volume := range volumes { + if volume.ID == req.VolumeID { + delete(volumes, key) + } + } + + newVolumes := volumesToVolumeTemplates(volumes) + + // update server + updateServerResponse, err := s.updateServer(&UpdateServerRequest{ + Zone: req.Zone, + ServerID: *volume.ServerID, + Volumes: &newVolumes, + }) + if err != nil { + return nil, err + } + + return &DetachVolumeResponse{Server: updateServerResponse.Server}, nil +} + +// UnsafeSetTotalCount should not be used +// Internal usage only +func (r *ListServersResponse) UnsafeSetTotalCount(totalCount int) { + r.TotalCount = uint32(totalCount) +} + +// UnsafeSetTotalCount should not be used +// Internal usage only +func (r *ListBootscriptsResponse) UnsafeSetTotalCount(totalCount int) { + r.TotalCount = uint32(totalCount) +} + +// UnsafeSetTotalCount should not be used +// Internal usage only +func (r *ListIPsResponse) UnsafeSetTotalCount(totalCount int) { + r.TotalCount = uint32(totalCount) +} + +// UnsafeSetTotalCount should not be used +// Internal usage only +func (r *ListSecurityGroupRulesResponse) UnsafeSetTotalCount(totalCount int) { + r.TotalCount = uint32(totalCount) +} + +// UnsafeSetTotalCount should not be used +// Internal usage only +func (r *ListSecurityGroupsResponse) UnsafeSetTotalCount(totalCount int) { + r.TotalCount = uint32(totalCount) +} + +// UnsafeSetTotalCount should not be used +// Internal usage only +func (r *ListServersTypesResponse) UnsafeSetTotalCount(totalCount int) { + r.TotalCount = uint32(totalCount) +} + +// UnsafeSetTotalCount should not be used +// Internal usage only +func (r *ListSnapshotsResponse) UnsafeSetTotalCount(totalCount int) { + r.TotalCount = uint32(totalCount) +} + +// UnsafeSetTotalCount should not be used +// Internal usage only +func (r *ListVolumesResponse) UnsafeSetTotalCount(totalCount int) { + r.TotalCount = uint32(totalCount) +} + +// UnsafeSetTotalCount should not be used +// Internal usage only +func (r *ListImagesResponse) UnsafeSetTotalCount(totalCount int) { + r.TotalCount = uint32(totalCount) +} + +func (v *NullableStringValue) UnmarshalJSON(b []byte) error { + if string(b) == "null" { + v.Null = true + return nil + } + + var tmp string + if err := json.Unmarshal(b, &tmp); err != nil { + return err + } + v.Null = false + v.Value = tmp + return nil +} + +func (v *NullableStringValue) MarshalJSON() ([]byte, error) { + if v.Null { + return []byte("null"), nil + } + return json.Marshal(v.Value) +} + +// WaitForPrivateNICRequest is used by WaitForPrivateNIC method. +type WaitForPrivateNICRequest struct { + ServerID string + PrivateNicID string + Zone scw.Zone + Timeout *time.Duration + RetryInterval *time.Duration +} + +// WaitForPrivateNIC wait for the private network to be in a "terminal state" before returning. +// This function can be used to wait for the private network to be attached for example. +func (s *API) WaitForPrivateNIC(req *WaitForPrivateNICRequest, opts ...scw.RequestOption) (*PrivateNIC, error) { + timeout := defaultTimeout + if req.Timeout != nil { + timeout = *req.Timeout + } + retryInterval := defaultRetryInterval + if req.RetryInterval != nil { + retryInterval = *req.RetryInterval + } + + terminalStatus := map[PrivateNICState]struct{}{ + PrivateNICStateAvailable: {}, + PrivateNICStateSyncingError: {}, + } + + pn, err := async.WaitSync(&async.WaitSyncConfig{ + Get: func() (interface{}, bool, error) { + res, err := s.GetPrivateNIC(&GetPrivateNICRequest{ + ServerID: req.ServerID, + Zone: req.Zone, + PrivateNicID: req.PrivateNicID, + }, opts...) + + if err != nil { + return nil, false, err + } + _, isTerminal := terminalStatus[res.PrivateNic.State] + + return res.PrivateNic, isTerminal, err + }, + Timeout: timeout, + IntervalStrategy: async.LinearIntervalStrategy(retryInterval), + }) + if err != nil { + return nil, errors.Wrap(err, "waiting for server failed") + } + return pn.(*PrivateNIC), nil +} + +// WaitForMACAddressRequest is used by WaitForMACAddress method. +type WaitForMACAddressRequest struct { + ServerID string + PrivateNicID string + Zone scw.Zone + Timeout *time.Duration + RetryInterval *time.Duration +} + +// WaitForMACAddress wait for the MAC address be assigned on instance before returning. +// This function can be used to wait for the private network to be attached for example. +func (s *API) WaitForMACAddress(req *WaitForMACAddressRequest, opts ...scw.RequestOption) (*PrivateNIC, error) { + timeout := defaultTimeout + if req.Timeout != nil { + timeout = *req.Timeout + } + retryInterval := defaultRetryInterval + if req.RetryInterval != nil { + retryInterval = *req.RetryInterval + } + + pn, err := async.WaitSync(&async.WaitSyncConfig{ + Get: func() (interface{}, bool, error) { + res, err := s.GetPrivateNIC(&GetPrivateNICRequest{ + ServerID: req.ServerID, + Zone: req.Zone, + PrivateNicID: req.PrivateNicID, + }, opts...) + if err != nil { + return nil, false, err + } + + if len(res.PrivateNic.MacAddress) > 0 { + return res.PrivateNic, true, err + } + + return res.PrivateNic, false, err + }, + Timeout: timeout, + IntervalStrategy: async.LinearIntervalStrategy(retryInterval), + }) + if err != nil { + return nil, errors.Wrap(err, "waiting for server failed") + } + return pn.(*PrivateNIC), nil +} + +// UnsafeSetTotalCount should not be used +// Internal usage only +func (r *GetServerTypesAvailabilityResponse) UnsafeSetTotalCount(totalCount int) { + r.TotalCount = uint32(totalCount) +} diff --git a/vendor/github.com/scaleway/scaleway-sdk-go/api/instance/v1/server_utils.go b/vendor/github.com/scaleway/scaleway-sdk-go/api/instance/v1/server_utils.go new file mode 100644 index 000000000..29c9c3997 --- /dev/null +++ b/vendor/github.com/scaleway/scaleway-sdk-go/api/instance/v1/server_utils.go @@ -0,0 +1,409 @@ +package instance + +import ( + "bytes" + "fmt" + "io" + "net/http" + "time" + + "github.com/scaleway/scaleway-sdk-go/api/marketplace/v1" + "github.com/scaleway/scaleway-sdk-go/internal/async" + "github.com/scaleway/scaleway-sdk-go/internal/errors" + "github.com/scaleway/scaleway-sdk-go/scw" + "github.com/scaleway/scaleway-sdk-go/validation" +) + +const ( + defaultTimeout = 5 * time.Minute + defaultRetryInterval = 5 * time.Second +) + +// CreateServer creates a server. +func (s *API) CreateServer(req *CreateServerRequest, opts ...scw.RequestOption) (*CreateServerResponse, error) { + // If image is not a UUID we try to fetch it from marketplace. + if req.Image != "" && !validation.IsUUID(req.Image) { + apiMarketplace := marketplace.NewAPI(s.client) + imageID, err := apiMarketplace.GetLocalImageIDByLabel(&marketplace.GetLocalImageIDByLabelRequest{ + ImageLabel: req.Image, + Zone: req.Zone, + CommercialType: req.CommercialType, + }) + if err != nil { + return nil, err + } + req.Image = imageID + } + + return s.createServer(req, opts...) +} + +// UpdateServer updates a server. +// +// Note: Implementation is thread-safe. +func (s *API) UpdateServer(req *UpdateServerRequest, opts ...scw.RequestOption) (*UpdateServerResponse, error) { + defer lockServer(req.Zone, req.ServerID).Unlock() + return s.updateServer(req, opts...) +} + +// WaitForServerRequest is used by WaitForServer method. +type WaitForServerRequest struct { + ServerID string + Zone scw.Zone + Timeout *time.Duration + RetryInterval *time.Duration +} + +// WaitForServer wait for the server to be in a "terminal state" before returning. +// This function can be used to wait for a server to be started for example. +func (s *API) WaitForServer(req *WaitForServerRequest, opts ...scw.RequestOption) (*Server, error) { + timeout := defaultTimeout + if req.Timeout != nil { + timeout = *req.Timeout + } + retryInterval := defaultRetryInterval + if req.RetryInterval != nil { + retryInterval = *req.RetryInterval + } + + terminalStatus := map[ServerState]struct{}{ + ServerStateStopped: {}, + ServerStateStoppedInPlace: {}, + ServerStateLocked: {}, + ServerStateRunning: {}, + } + + server, err := async.WaitSync(&async.WaitSyncConfig{ + Get: func() (interface{}, bool, error) { + res, err := s.GetServer(&GetServerRequest{ + ServerID: req.ServerID, + Zone: req.Zone, + }, opts...) + + if err != nil { + return nil, false, err + } + _, isTerminal := terminalStatus[res.Server.State] + + return res.Server, isTerminal, err + }, + Timeout: timeout, + IntervalStrategy: async.LinearIntervalStrategy(retryInterval), + }) + if err != nil { + return nil, errors.Wrap(err, "waiting for server failed") + } + return server.(*Server), nil +} + +// ServerActionAndWaitRequest is used by ServerActionAndWait method. +type ServerActionAndWaitRequest struct { + ServerID string + Zone scw.Zone + Action ServerAction + + // Timeout: maximum time to wait before (default: 5 minutes) + Timeout *time.Duration + RetryInterval *time.Duration +} + +// ServerActionAndWait start an action and wait for the server to be in the correct "terminal state" +// expected by this action. +func (s *API) ServerActionAndWait(req *ServerActionAndWaitRequest, opts ...scw.RequestOption) error { + timeout := defaultTimeout + if req.Timeout != nil { + timeout = *req.Timeout + } + retryInterval := defaultRetryInterval + if req.RetryInterval != nil { + retryInterval = *req.RetryInterval + } + + _, err := s.ServerAction(&ServerActionRequest{ + Zone: req.Zone, + ServerID: req.ServerID, + Action: req.Action, + }, opts...) + if err != nil { + return err + } + + finalServer, err := s.WaitForServer(&WaitForServerRequest{ + Zone: req.Zone, + ServerID: req.ServerID, + Timeout: &timeout, + RetryInterval: &retryInterval, + }, opts...) + if err != nil { + return err + } + + // check the action was properly executed + expectedState := ServerState("unknown") + switch req.Action { + case ServerActionPoweron, ServerActionReboot: + expectedState = ServerStateRunning + case ServerActionPoweroff: + expectedState = ServerStateStopped + case ServerActionStopInPlace: + expectedState = ServerStateStoppedInPlace + } + + // backup can be performed from any state + if expectedState != ServerState("unknown") && finalServer.State != expectedState { + return errors.New("expected state %s but found %s: %s", expectedState, finalServer.State, finalServer.StateDetail) + } + + return nil +} + +// GetServerTypeRequest is used by GetServerType. +type GetServerTypeRequest struct { + Zone scw.Zone + Name string +} + +// GetServerType get server type info by it's name. +func (s *API) GetServerType(req *GetServerTypeRequest) (*ServerType, error) { + res, err := s.ListServersTypes(&ListServersTypesRequest{ + Zone: req.Zone, + }, scw.WithAllPages()) + + if err != nil { + return nil, err + } + + if serverType, exist := res.Servers[req.Name]; exist { + return serverType, nil + } + + return nil, errors.New("could not find server type %q", req.Name) +} + +// GetServerUserDataRequest is used by GetServerUserData method. +type GetServerUserDataRequest struct { + Zone scw.Zone `json:"-"` + ServerID string `json:"-"` + + // Key defines the user data key to get. + Key string `json:"-"` +} + +// GetServerUserData gets the content of a user data on a server for the given key. +func (s *API) GetServerUserData(req *GetServerUserDataRequest, opts ...scw.RequestOption) (io.Reader, error) { + var err error + + if req.Zone == "" { + defaultZone, _ := s.client.GetDefaultZone() + req.Zone = defaultZone + } + + if fmt.Sprint(req.Zone) == "" { + return nil, errors.New("field Zone cannot be empty in request") + } + + if fmt.Sprint(req.ServerID) == "" { + return nil, errors.New("field ServerID cannot be empty in request") + } + + if fmt.Sprint(req.Key) == "" { + return nil, errors.New("field Key cannot be empty in request") + } + + scwReq := &scw.ScalewayRequest{ + Method: "GET", + Path: "/instance/v1/zones/" + fmt.Sprint(req.Zone) + "/servers/" + fmt.Sprint(req.ServerID) + "/user_data/" + fmt.Sprint(req.Key), + Headers: http.Header{}, + } + + res := &bytes.Buffer{} + + err = s.client.Do(scwReq, res, opts...) + if err != nil { + return nil, err + } + + return res, nil +} + +// SetServerUserDataRequest is used by SetServerUserData method. +type SetServerUserDataRequest struct { + Zone scw.Zone `json:"-"` + ServerID string `json:"-"` + + // Key defines the user data key to set. + Key string `json:"-"` + + // Content defines the data to set. + Content io.Reader +} + +// SetServerUserData sets the content of a user data on a server for the given key. +func (s *API) SetServerUserData(req *SetServerUserDataRequest, opts ...scw.RequestOption) error { + var err error + + if req.Zone == "" { + defaultZone, _ := s.client.GetDefaultZone() + req.Zone = defaultZone + } + + if fmt.Sprint(req.Zone) == "" { + return errors.New("field Zone cannot be empty in request") + } + + if fmt.Sprint(req.ServerID) == "" { + return errors.New("field ServerID cannot be empty in request") + } + + if fmt.Sprint(req.Key) == "" { + return errors.New("field Key cannot be empty in request") + } + + if req.Content == nil { + return errors.New("field Content cannot be nil in request") + } + + scwReq := &scw.ScalewayRequest{ + Method: "PATCH", + Path: "/instance/v1/zones/" + fmt.Sprint(req.Zone) + "/servers/" + fmt.Sprint(req.ServerID) + "/user_data/" + fmt.Sprint(req.Key), + Headers: http.Header{}, + } + + err = scwReq.SetBody(req.Content) + if err != nil { + return err + } + + err = s.client.Do(scwReq, nil, opts...) + if err != nil { + return err + } + + return nil +} + +// GetAllServerUserDataRequest is used by GetAllServerUserData method. +type GetAllServerUserDataRequest struct { + Zone scw.Zone `json:"-"` + ServerID string `json:"-"` +} + +// GetAllServerUserDataResponse is used by GetAllServerUserData method. +type GetAllServerUserDataResponse struct { + UserData map[string]io.Reader `json:"-"` +} + +// GetAllServerUserData gets all user data on a server. +func (s *API) GetAllServerUserData(req *GetAllServerUserDataRequest, opts ...scw.RequestOption) (*GetAllServerUserDataResponse, error) { + var err error + + if req.Zone == "" { + defaultZone, _ := s.client.GetDefaultZone() + req.Zone = defaultZone + } + + if fmt.Sprint(req.Zone) == "" { + return nil, errors.New("field Zone cannot be empty in request") + } + + if fmt.Sprint(req.ServerID) == "" { + return nil, errors.New("field ServerID cannot be empty in request") + } + + // get all user data keys + allUserDataRes, err := s.ListServerUserData(&ListServerUserDataRequest{ + Zone: req.Zone, + ServerID: req.ServerID, + }) + if err != nil { + return nil, err + } + + res := &GetAllServerUserDataResponse{ + UserData: make(map[string]io.Reader, len(allUserDataRes.UserData)), + } + + // build a map with all user data + for _, key := range allUserDataRes.UserData { + value, err := s.GetServerUserData(&GetServerUserDataRequest{ + Zone: req.Zone, + ServerID: req.ServerID, + Key: key, + }) + if err != nil { + return nil, err + } + res.UserData[key] = value + } + + return res, nil +} + +// SetAllServerUserDataRequest is used by SetAllServerUserData method. +type SetAllServerUserDataRequest struct { + Zone scw.Zone `json:"-"` + ServerID string `json:"-"` + + // UserData defines all user data that will be set to the server. + // This map is idempotent, it means that all the current data will be overwritten and + // all keys not present in this map will be deleted.. All data will be removed if this map is nil. + UserData map[string]io.Reader `json:"-"` +} + +// SetAllServerUserData sets all user data on a server, it deletes every keys previously set. +func (s *API) SetAllServerUserData(req *SetAllServerUserDataRequest, opts ...scw.RequestOption) error { + var err error + + if req.Zone == "" { + defaultZone, _ := s.client.GetDefaultZone() + req.Zone = defaultZone + } + + if fmt.Sprint(req.Zone) == "" { + return errors.New("field Zone cannot be empty in request") + } + + if fmt.Sprint(req.ServerID) == "" { + return errors.New("field ServerID cannot be empty in request") + } + + // get all current user data keys + allUserDataRes, err := s.ListServerUserData(&ListServerUserDataRequest{ + Zone: req.Zone, + ServerID: req.ServerID, + }) + if err != nil { + return err + } + + // delete all current user data + for _, key := range allUserDataRes.UserData { + _, exist := req.UserData[key] + if exist { + continue + } + err := s.DeleteServerUserData(&DeleteServerUserDataRequest{ + Zone: req.Zone, + ServerID: req.ServerID, + Key: key, + }) + if err != nil { + return err + } + } + + // set all new user data + for key, value := range req.UserData { + err := s.SetServerUserData(&SetServerUserDataRequest{ + Zone: req.Zone, + ServerID: req.ServerID, + Key: key, + Content: value, + }) + if err != nil { + return err + } + } + + return nil +} diff --git a/vendor/github.com/scaleway/scaleway-sdk-go/api/instance/v1/snapshot_utils.go b/vendor/github.com/scaleway/scaleway-sdk-go/api/instance/v1/snapshot_utils.go new file mode 100644 index 000000000..6efc419d3 --- /dev/null +++ b/vendor/github.com/scaleway/scaleway-sdk-go/api/instance/v1/snapshot_utils.go @@ -0,0 +1,56 @@ +package instance + +import ( + "time" + + "github.com/scaleway/scaleway-sdk-go/internal/async" + "github.com/scaleway/scaleway-sdk-go/internal/errors" + "github.com/scaleway/scaleway-sdk-go/scw" +) + +// WaitForImageRequest is used by WaitForImage method. +type WaitForSnapshotRequest struct { + SnapshotID string + Zone scw.Zone + Timeout *time.Duration + RetryInterval *time.Duration +} + +// WaitForSnapshot wait for the snapshot to be in a "terminal state" before returning. +func (s *API) WaitForSnapshot(req *WaitForSnapshotRequest, opts ...scw.RequestOption) (*Snapshot, error) { + timeout := defaultTimeout + if req.Timeout != nil { + timeout = *req.Timeout + } + retryInterval := defaultRetryInterval + if req.RetryInterval != nil { + retryInterval = *req.RetryInterval + } + + terminalStatus := map[SnapshotState]struct{}{ + SnapshotStateAvailable: {}, + SnapshotStateError: {}, + } + + snapshot, err := async.WaitSync(&async.WaitSyncConfig{ + Get: func() (interface{}, bool, error) { + res, err := s.GetSnapshot(&GetSnapshotRequest{ + SnapshotID: req.SnapshotID, + Zone: req.Zone, + }, opts...) + + if err != nil { + return nil, false, err + } + _, isTerminal := terminalStatus[res.Snapshot.State] + + return res.Snapshot, isTerminal, err + }, + Timeout: timeout, + IntervalStrategy: async.LinearIntervalStrategy(retryInterval), + }) + if err != nil { + return nil, errors.Wrap(err, "waiting for snapshot failed") + } + return snapshot.(*Snapshot), nil +} diff --git a/vendor/github.com/scaleway/scaleway-sdk-go/api/instance/v1/volume_utils.go b/vendor/github.com/scaleway/scaleway-sdk-go/api/instance/v1/volume_utils.go new file mode 100644 index 000000000..30b98c6e9 --- /dev/null +++ b/vendor/github.com/scaleway/scaleway-sdk-go/api/instance/v1/volume_utils.go @@ -0,0 +1,113 @@ +package instance + +import ( + goerrors "errors" + "time" + + block "github.com/scaleway/scaleway-sdk-go/api/block/v1alpha1" + "github.com/scaleway/scaleway-sdk-go/internal/async" + "github.com/scaleway/scaleway-sdk-go/internal/errors" + "github.com/scaleway/scaleway-sdk-go/scw" +) + +// WaitForImageRequest is used by WaitForImage method. +type WaitForVolumeRequest struct { + VolumeID string + Zone scw.Zone + Timeout *time.Duration + RetryInterval *time.Duration +} + +// WaitForSnapshot wait for the snapshot to be in a "terminal state" before returning. +func (s *API) WaitForVolume(req *WaitForVolumeRequest, opts ...scw.RequestOption) (*Volume, error) { + timeout := defaultTimeout + if req.Timeout != nil { + timeout = *req.Timeout + } + retryInterval := defaultRetryInterval + if req.RetryInterval != nil { + retryInterval = *req.RetryInterval + } + + terminalStatus := map[VolumeState]struct{}{ + VolumeStateAvailable: {}, + VolumeStateError: {}, + } + + volume, err := async.WaitSync(&async.WaitSyncConfig{ + Get: func() (interface{}, bool, error) { + res, err := s.GetVolume(&GetVolumeRequest{ + VolumeID: req.VolumeID, + Zone: req.Zone, + }, opts...) + + if err != nil { + return nil, false, err + } + _, isTerminal := terminalStatus[res.Volume.State] + + return res.Volume, isTerminal, err + }, + Timeout: timeout, + IntervalStrategy: async.LinearIntervalStrategy(retryInterval), + }) + if err != nil { + return nil, errors.Wrap(err, "waiting for volume failed") + } + return volume.(*Volume), nil +} + +type unknownVolume struct { + ID string + ServerID *string + Type VolumeVolumeType +} + +type getUnknownVolumeRequest struct { + Zone scw.Zone + VolumeID string + IsBlockVolume *bool +} + +// getUnknownVolume is used to get a volume that can be either from instance or block API +func (s *API) getUnknownVolume(req *getUnknownVolumeRequest, opts ...scw.RequestOption) (*unknownVolume, error) { + volume := &unknownVolume{ + ID: req.VolumeID, + } + + // Try instance API + if req.IsBlockVolume == nil || *req.IsBlockVolume == false { + getVolumeResponse, err := s.GetVolume(&GetVolumeRequest{ + Zone: req.Zone, + VolumeID: req.VolumeID, + }) + notFoundErr := &scw.ResourceNotFoundError{} + if err != nil && !goerrors.As(err, ¬FoundErr) { + return nil, err + } + + if getVolumeResponse != nil { + if getVolumeResponse.Volume != nil && getVolumeResponse.Volume.Server != nil { + volume.ServerID = &getVolumeResponse.Volume.Server.ID + } + volume.Type = getVolumeResponse.Volume.VolumeType + } + } + if volume.Type == "" && (req.IsBlockVolume == nil || *req.IsBlockVolume == true) { + getVolumeResponse, err := block.NewAPI(s.client).GetVolume(&block.GetVolumeRequest{ + Zone: req.Zone, + VolumeID: req.VolumeID, + }) + if err != nil { + return nil, err + } + for _, reference := range getVolumeResponse.References { + if reference.ProductResourceType == "instance_server" { + volume.ServerID = &reference.ProductResourceID + } + } + volume.Type = VolumeVolumeTypeSbsVolume + } + + return volume, nil +} diff --git a/vendor/github.com/scaleway/scaleway-sdk-go/api/marketplace/v1/marketplace_sdk.go b/vendor/github.com/scaleway/scaleway-sdk-go/api/marketplace/v1/marketplace_sdk.go new file mode 100644 index 000000000..b430fe8b0 --- /dev/null +++ b/vendor/github.com/scaleway/scaleway-sdk-go/api/marketplace/v1/marketplace_sdk.go @@ -0,0 +1,221 @@ +// This file was automatically generated. DO NOT EDIT. +// If you have any remark or suggestion do not hesitate to open an issue. + +// Package marketplace provides methods and message types of the marketplace v1 API. +package marketplace + +import ( + "bytes" + "encoding/json" + "fmt" + "net" + "net/http" + "net/url" + "strings" + "time" + + "github.com/scaleway/scaleway-sdk-go/internal/errors" + "github.com/scaleway/scaleway-sdk-go/internal/marshaler" + "github.com/scaleway/scaleway-sdk-go/internal/parameter" + "github.com/scaleway/scaleway-sdk-go/namegenerator" + "github.com/scaleway/scaleway-sdk-go/scw" +) + +// always import dependencies +var ( + _ fmt.Stringer + _ json.Unmarshaler + _ url.URL + _ net.IP + _ http.Header + _ bytes.Reader + _ time.Time + _ = strings.Join + + _ scw.ScalewayRequest + _ marshaler.Duration + _ scw.File + _ = parameter.AddToQuery + _ = namegenerator.GetRandomName +) + +// LocalImage: local image. +type LocalImage struct { + // ID: version you will typically use to define an image in an API call. + ID string `json:"id"` + + // CompatibleCommercialTypes: list of all commercial types that are compatible with this local image. + CompatibleCommercialTypes []string `json:"compatible_commercial_types"` + + // Arch: supported architecture for this local image. + Arch string `json:"arch"` + + // Zone: availability Zone where this local image is available. + Zone scw.Zone `json:"zone"` +} + +// Organization: organization. +type Organization struct { + ID string `json:"id"` + + Name string `json:"name"` +} + +// Version: version. +type Version struct { + // ID: UUID of this version. + ID string `json:"id"` + + // Name: name of this version. + Name string `json:"name"` + + // CreationDate: creation date of this image version. + CreationDate *time.Time `json:"creation_date"` + + // ModificationDate: date of the last modification of this version. + ModificationDate *time.Time `json:"modification_date"` + + // LocalImages: list of local images available in this version. + LocalImages []*LocalImage `json:"local_images"` +} + +// Image: image. +type Image struct { + // ID: UUID of this image. + ID string `json:"id"` + + // Name: name of the image. + Name string `json:"name"` + + // Description: text description of this image. + Description string `json:"description"` + + // Logo: URL of this image's logo. + Logo string `json:"logo"` + + // Categories: list of categories this image belongs to. + Categories []string `json:"categories"` + + // CreationDate: creation date of this image. + CreationDate *time.Time `json:"creation_date"` + + // ModificationDate: date of the last modification of this image. + ModificationDate *time.Time `json:"modification_date"` + + // ValidUntil: expiration date of this image. + ValidUntil *time.Time `json:"valid_until"` + + // Label: typically an identifier for a distribution (ex. "ubuntu_focal"). + Label string `json:"label"` + + // Versions: list of versions of this image. + Versions []*Version `json:"versions"` + + // Organization: organization this image belongs to. + Organization *Organization `json:"organization"` + + CurrentPublicVersion string `json:"current_public_version"` +} + +// GetImageRequest: get image request. +type GetImageRequest struct { + // ImageID: display the image name. + ImageID string `json:"-"` +} + +// GetImageResponse: get image response. +type GetImageResponse struct { + Image *Image `json:"image"` +} + +// ListImagesRequest: list images request. +type ListImagesRequest struct { + // PerPage: a positive integer lower or equal to 100 to select the number of items to display. + PerPage *uint32 `json:"-"` + + // Page: a positive integer to choose the page to display. + Page *int32 `json:"-"` +} + +// ListImagesResponse: list images response. +type ListImagesResponse struct { + Images []*Image `json:"images"` + + TotalCount uint32 `json:"total_count"` +} + +// UnsafeGetTotalCount should not be used +// Internal usage only +func (r *ListImagesResponse) UnsafeGetTotalCount() uint32 { + return r.TotalCount +} + +// UnsafeAppend should not be used +// Internal usage only +func (r *ListImagesResponse) UnsafeAppend(res interface{}) (uint32, error) { + results, ok := res.(*ListImagesResponse) + if !ok { + return 0, errors.New("%T type cannot be appended to type %T", res, r) + } + + r.Images = append(r.Images, results.Images...) + r.TotalCount += uint32(len(results.Images)) + return uint32(len(results.Images)), nil +} + +// Marketplace API. +type API struct { + client *scw.Client +} + +// NewAPI returns a API object from a Scaleway client. +func NewAPI(client *scw.Client) *API { + return &API{ + client: client, + } +} + +// ListImages: List marketplace images. +func (s *API) ListImages(req *ListImagesRequest, opts ...scw.RequestOption) (*ListImagesResponse, error) { + var err error + + query := url.Values{} + parameter.AddToQuery(query, "per_page", req.PerPage) + parameter.AddToQuery(query, "page", req.Page) + + scwReq := &scw.ScalewayRequest{ + Method: "GET", + Path: "/marketplace/v1/images", + Query: query, + } + + var resp ListImagesResponse + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +// GetImage: Get a specific marketplace image. +func (s *API) GetImage(req *GetImageRequest, opts ...scw.RequestOption) (*GetImageResponse, error) { + var err error + + if fmt.Sprint(req.ImageID) == "" { + return nil, errors.New("field ImageID cannot be empty in request") + } + + scwReq := &scw.ScalewayRequest{ + Method: "GET", + Path: "/marketplace/v1/images/" + fmt.Sprint(req.ImageID) + "", + } + + var resp GetImageResponse + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} diff --git a/vendor/github.com/scaleway/scaleway-sdk-go/api/marketplace/v1/marketplace_utils.go b/vendor/github.com/scaleway/scaleway-sdk-go/api/marketplace/v1/marketplace_utils.go new file mode 100644 index 000000000..f84175e49 --- /dev/null +++ b/vendor/github.com/scaleway/scaleway-sdk-go/api/marketplace/v1/marketplace_utils.go @@ -0,0 +1,93 @@ +package marketplace + +import ( + "fmt" + "strings" + + "github.com/scaleway/scaleway-sdk-go/internal/errors" + "github.com/scaleway/scaleway-sdk-go/scw" +) + +// getLocalImage returns the correct local version of an image matching +// the current zone and the compatible commercial type +func (version *Version) getLocalImage(zone scw.Zone, commercialType string) (*LocalImage, error) { + for _, localImage := range version.LocalImages { + // Check if in correct zone + if localImage.Zone != zone { + continue + } + + // Check if compatible with wanted commercial type + for _, compatibleCommercialType := range localImage.CompatibleCommercialTypes { + if compatibleCommercialType == commercialType { + return localImage, nil + } + } + } + + return nil, fmt.Errorf("couldn't find compatible local image for this image version (%s)", version.ID) +} + +// getLatestVersion returns the current/latest version on an image, +// or an error in case the image doesn't have a public version. +func (image *Image) getLatestVersion() (*Version, error) { + for _, version := range image.Versions { + if version.ID == image.CurrentPublicVersion { + return version, nil + } + } + + return nil, errors.New("latest version could not be found for image %s", image.Label) +} + +// GetLocalImageIDByLabelRequest is used by GetLocalImageIDByLabel +type GetLocalImageIDByLabelRequest struct { + ImageLabel string + Zone scw.Zone + CommercialType string +} + +// GetLocalImageIDByLabel search for an image with the given label (exact match) in the given region +// it returns the latest version of this specific image. +func (s *API) GetLocalImageIDByLabel(req *GetLocalImageIDByLabelRequest, opts ...scw.RequestOption) (string, error) { + if req.Zone == "" { + defaultZone, _ := s.client.GetDefaultZone() + req.Zone = defaultZone + } + + listImageRequest := &ListImagesRequest{} + opts = append(opts, scw.WithAllPages()) + listImageResponse, err := s.ListImages(listImageRequest, opts...) + if err != nil { + return "", err + } + + images := listImageResponse.Images + label := strings.Replace(req.ImageLabel, "-", "_", -1) + commercialType := strings.ToUpper(req.CommercialType) + + for _, image := range images { + // Match label of the image + if label == image.Label { + latestVersion, err := image.getLatestVersion() + if err != nil { + return "", errors.Wrap(err, "couldn't find a matching image for the given label (%s), zone (%s) and commercial type (%s)", req.ImageLabel, req.Zone, req.CommercialType) + } + + localImage, err := latestVersion.getLocalImage(req.Zone, commercialType) + if err != nil { + return "", errors.Wrap(err, "couldn't find a matching image for the given label (%s), zone (%s) and commercial type (%s)", req.ImageLabel, req.Zone, req.CommercialType) + } + + return localImage.ID, nil + } + } + + return "", errors.New("couldn't find a matching image for the given label (%s), zone (%s) and commercial type (%s)", req.ImageLabel, req.Zone, req.CommercialType) +} + +// UnsafeSetTotalCount should not be used +// Internal usage only +func (r *ListImagesResponse) UnsafeSetTotalCount(totalCount int) { + r.TotalCount = uint32(totalCount) +} diff --git a/vendor/github.com/scaleway/scaleway-sdk-go/internal/async/wait.go b/vendor/github.com/scaleway/scaleway-sdk-go/internal/async/wait.go new file mode 100644 index 000000000..7e0d2158f --- /dev/null +++ b/vendor/github.com/scaleway/scaleway-sdk-go/internal/async/wait.go @@ -0,0 +1,90 @@ +package async + +import ( + "fmt" + "time" +) + +var ( + defaultInterval = time.Second + defaultTimeout = time.Minute * 5 +) + +type IntervalStrategy func() <-chan time.Time + +// WaitSyncConfig defines the waiting options. +type WaitSyncConfig struct { + // This method will be called from another goroutine. + Get func() (value interface{}, isTerminal bool, err error) + IntervalStrategy IntervalStrategy + Timeout time.Duration +} + +// LinearIntervalStrategy defines a linear interval duration. +func LinearIntervalStrategy(interval time.Duration) IntervalStrategy { + return func() <-chan time.Time { + return time.After(interval) + } +} + +// FibonacciIntervalStrategy defines an interval duration who follow the Fibonacci sequence. +func FibonacciIntervalStrategy(base time.Duration, factor float32) IntervalStrategy { + var x, y float32 = 0, 1 + + return func() <-chan time.Time { + x, y = y, x+(y*factor) + return time.After(time.Duration(x) * base) + } +} + +// WaitSync waits and returns when a given stop condition is true or if an error occurs. +func WaitSync(config *WaitSyncConfig) (terminalValue interface{}, err error) { + // initialize configuration + if config.IntervalStrategy == nil { + config.IntervalStrategy = LinearIntervalStrategy(defaultInterval) + } + + if config.Timeout == 0 { + config.Timeout = defaultTimeout + } + + resultValue := make(chan interface{}) + resultErr := make(chan error) + timeout := make(chan bool) + + go func() { + for { + // get the payload + value, stopCondition, err := config.Get() + + // send the payload + if err != nil { + resultErr <- err + return + } + if stopCondition { + resultValue <- value + return + } + + // waiting for an interval before next get() call or a timeout + select { + case <-timeout: + return + case <-config.IntervalStrategy(): + // sleep + } + } + }() + + // waiting for a result or a timeout + select { + case val := <-resultValue: + return val, nil + case err := <-resultErr: + return nil, err + case <-time.After(config.Timeout): + timeout <- true + return nil, fmt.Errorf("timeout after %v", config.Timeout) + } +} diff --git a/vendor/github.com/scaleway/scaleway-sdk-go/internal/auth/access_key.go b/vendor/github.com/scaleway/scaleway-sdk-go/internal/auth/access_key.go new file mode 100644 index 000000000..4f6b14d35 --- /dev/null +++ b/vendor/github.com/scaleway/scaleway-sdk-go/internal/auth/access_key.go @@ -0,0 +1,21 @@ +package auth + +import "net/http" + +type AccessKeyOnly struct { + // auth config may contain an access key without being authenticated + AccessKey string +} + +// NewNoAuth return an auth with no authentication method +func NewAccessKeyOnly(accessKey string) *AccessKeyOnly { + return &AccessKeyOnly{accessKey} +} + +func (t *AccessKeyOnly) Headers() http.Header { + return http.Header{} +} + +func (t *AccessKeyOnly) AnonymizedHeaders() http.Header { + return http.Header{} +} diff --git a/vendor/github.com/scaleway/scaleway-sdk-go/internal/auth/auth.go b/vendor/github.com/scaleway/scaleway-sdk-go/internal/auth/auth.go new file mode 100644 index 000000000..527504687 --- /dev/null +++ b/vendor/github.com/scaleway/scaleway-sdk-go/internal/auth/auth.go @@ -0,0 +1,29 @@ +package auth + +import "net/http" + +// Auth implement methods required for authentication. +// Valid authentication are currently a token or no auth. +type Auth interface { + // Headers returns headers that must be add to the http request + Headers() http.Header + + // AnonymizedHeaders returns an anonymised version of Headers() + // This method could be use for logging purpose. + AnonymizedHeaders() http.Header +} + +type headerAnonymizer func(header http.Header) http.Header + +var headerAnonymizers = []headerAnonymizer{ + AnonymizeTokenHeaders, + AnonymizeJWTHeaders, +} + +func AnonymizeHeaders(headers http.Header) http.Header { + for _, anonymizer := range headerAnonymizers { + headers = anonymizer(headers) + } + + return headers +} diff --git a/vendor/github.com/scaleway/scaleway-sdk-go/internal/auth/jwt.go b/vendor/github.com/scaleway/scaleway-sdk-go/internal/auth/jwt.go new file mode 100644 index 000000000..19c81d413 --- /dev/null +++ b/vendor/github.com/scaleway/scaleway-sdk-go/internal/auth/jwt.go @@ -0,0 +1,55 @@ +package auth + +import ( + "net/http" + "strings" +) + +// JWT is the session token used in browser. +type JWT struct { + Token string +} + +// XSessionTokenHeader is Scaleway auth header for browser +const XSessionTokenHeader = "x-session-token" // #nosec G101 + +// NewJWT create a token authentication from a jwt +func NewJWT(token string) *JWT { + return &JWT{Token: token} +} + +// Headers returns headers that must be added to the http request +func (j *JWT) Headers() http.Header { + headers := http.Header{} + headers.Set(XSessionTokenHeader, j.Token) + return headers +} + +func AnonymizeJWTHeaders(headers http.Header) http.Header { + token := headers.Get(XSessionTokenHeader) + + if token != "" { + headers.Set(XSessionTokenHeader, HideJWT(token)) + } + + return headers +} + +// AnonymizedHeaders returns an anonymized version of Headers() +// This method could be used for logging purpose. +func (j *JWT) AnonymizedHeaders() http.Header { + return AnonymizeJWTHeaders(j.Headers()) +} + +func HideJWT(token string) string { + if len(token) == 0 { + return "" + } + // token should be (header).(payload).(signature) + lastDot := strings.LastIndex(token, ".") + if lastDot != -1 { + token = token[:lastDot] + } + + return token +} diff --git a/vendor/github.com/scaleway/scaleway-sdk-go/internal/auth/no_auth.go b/vendor/github.com/scaleway/scaleway-sdk-go/internal/auth/no_auth.go new file mode 100644 index 000000000..2181c57c2 --- /dev/null +++ b/vendor/github.com/scaleway/scaleway-sdk-go/internal/auth/no_auth.go @@ -0,0 +1,19 @@ +package auth + +import "net/http" + +type NoAuth struct { +} + +// NewNoAuth return an auth with no authentication method +func NewNoAuth() *NoAuth { + return &NoAuth{} +} + +func (t *NoAuth) Headers() http.Header { + return http.Header{} +} + +func (t *NoAuth) AnonymizedHeaders() http.Header { + return http.Header{} +} diff --git a/vendor/github.com/scaleway/scaleway-sdk-go/internal/auth/token.go b/vendor/github.com/scaleway/scaleway-sdk-go/internal/auth/token.go new file mode 100644 index 000000000..ab49b56fb --- /dev/null +++ b/vendor/github.com/scaleway/scaleway-sdk-go/internal/auth/token.go @@ -0,0 +1,51 @@ +package auth + +import "net/http" + +// Token is the pair accessKey + secretKey. +// This type is public because it's an internal package. +type Token struct { + AccessKey string + SecretKey string +} + +// XAuthTokenHeader is Scaleway standard auth header +const XAuthTokenHeader = "X-Auth-Token" // #nosec G101 + +// NewToken create a token authentication from an +// access key and a secret key +func NewToken(accessKey, secretKey string) *Token { + return &Token{AccessKey: accessKey, SecretKey: secretKey} +} + +// Headers returns headers that must be add to the http request +func (t *Token) Headers() http.Header { + headers := http.Header{} + headers.Set(XAuthTokenHeader, t.SecretKey) + return headers +} + +func AnonymizeTokenHeaders(headers http.Header) http.Header { + key := headers.Get(XAuthTokenHeader) + if key != "" { + headers.Set(XAuthTokenHeader, HideSecretKey(key)) + } + return headers +} + +// AnonymizedHeaders returns an anonymized version of Headers() +// This method could be use for logging purpose. +func (t *Token) AnonymizedHeaders() http.Header { + return AnonymizeTokenHeaders(t.Headers()) +} + +func HideSecretKey(k string) string { + switch { + case len(k) == 0: + return "" + case len(k) > 8: + return k[0:8] + "-xxxx-xxxx-xxxx-xxxxxxxxxxxx" + default: + return "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" + } +} diff --git a/vendor/github.com/scaleway/scaleway-sdk-go/internal/errors/error.go b/vendor/github.com/scaleway/scaleway-sdk-go/internal/errors/error.go new file mode 100644 index 000000000..351dd7084 --- /dev/null +++ b/vendor/github.com/scaleway/scaleway-sdk-go/internal/errors/error.go @@ -0,0 +1,41 @@ +package errors + +import "fmt" + +// Error is a base error that implement scw.SdkError +type Error struct { + Str string + Err error +} + +// Error implement standard xerror.Wrapper interface +func (e *Error) Unwrap() error { + return e.Err +} + +// Error implement standard error interface +func (e *Error) Error() string { + str := "scaleway-sdk-go: " + e.Str + if e.Err != nil { + str += ": " + e.Err.Error() + } + return str +} + +// IsScwSdkError implement SdkError interface +func (e *Error) IsScwSdkError() {} + +// New creates a new error with that same interface as fmt.Errorf +func New(format string, args ...interface{}) *Error { + return &Error{ + Str: fmt.Sprintf(format, args...), + } +} + +// Wrap an error with additional information +func Wrap(err error, format string, args ...interface{}) *Error { + return &Error{ + Err: err, + Str: fmt.Sprintf(format, args...), + } +} diff --git a/vendor/github.com/scaleway/scaleway-sdk-go/internal/generic/fields.go b/vendor/github.com/scaleway/scaleway-sdk-go/internal/generic/fields.go new file mode 100644 index 000000000..dc8011aec --- /dev/null +++ b/vendor/github.com/scaleway/scaleway-sdk-go/internal/generic/fields.go @@ -0,0 +1,17 @@ +package generic + +import "reflect" + +// HasField returns true if given struct has a field with given name +// Also allow a slice, it will use the underlying type +func HasField(i interface{}, fieldName string) bool { + value := reflect.Indirect(reflect.ValueOf(i)) + typ := value.Type() + + if value.Kind() == reflect.Slice { + typ = indirectType(typ.Elem()) + } + + _, fieldExists := typ.FieldByName(fieldName) + return fieldExists +} diff --git a/vendor/github.com/scaleway/scaleway-sdk-go/internal/generic/ptr.go b/vendor/github.com/scaleway/scaleway-sdk-go/internal/generic/ptr.go new file mode 100644 index 000000000..a7c932058 --- /dev/null +++ b/vendor/github.com/scaleway/scaleway-sdk-go/internal/generic/ptr.go @@ -0,0 +1,11 @@ +package generic + +import "reflect" + +func indirectType(typ reflect.Type) reflect.Type { + if typ.Kind() == reflect.Ptr { + return typ.Elem() + } + + return typ +} diff --git a/vendor/github.com/scaleway/scaleway-sdk-go/internal/generic/sort.go b/vendor/github.com/scaleway/scaleway-sdk-go/internal/generic/sort.go new file mode 100644 index 000000000..7e05ec584 --- /dev/null +++ b/vendor/github.com/scaleway/scaleway-sdk-go/internal/generic/sort.go @@ -0,0 +1,17 @@ +package generic + +import ( + "reflect" + "sort" +) + +// SortSliceByField sorts given slice of struct by passing the specified field to given compare function +// given slice must be a slice of Ptr +func SortSliceByField(list interface{}, field string, compare func(interface{}, interface{}) bool) { + listValue := reflect.ValueOf(list) + sort.SliceStable(list, func(i, j int) bool { + field1 := listValue.Index(i).Elem().FieldByName(field).Interface() + field2 := listValue.Index(j).Elem().FieldByName(field).Interface() + return compare(field1, field2) + }) +} diff --git a/vendor/github.com/scaleway/scaleway-sdk-go/internal/marshaler/duration.go b/vendor/github.com/scaleway/scaleway-sdk-go/internal/marshaler/duration.go new file mode 100644 index 000000000..0eaf70e56 --- /dev/null +++ b/vendor/github.com/scaleway/scaleway-sdk-go/internal/marshaler/duration.go @@ -0,0 +1,160 @@ +package marshaler + +import ( + "encoding/json" + "time" +) + +// Duration implements a JSON Marshaler to encode a time.Duration in milliseconds. +type Duration int64 + +const milliSec = Duration(time.Millisecond) + +// NewDuration converts a *time.Duration to a *Duration type. +func NewDuration(t *time.Duration) *Duration { + if t == nil { + return nil + } + d := Duration(t.Nanoseconds()) + return &d +} + +// Standard converts a *Duration to a *time.Duration type. +func (d *Duration) Standard() *time.Duration { + return (*time.Duration)(d) +} + +// MarshalJSON encodes the Duration in milliseconds. +func (d Duration) MarshalJSON() ([]byte, error) { + return json.Marshal(int64(d / milliSec)) +} + +// UnmarshalJSON decodes milliseconds to Duration. +func (d *Duration) UnmarshalJSON(b []byte) error { + var tmp int64 + err := json.Unmarshal(b, &tmp) + if err != nil { + return err + } + *d = Duration(tmp) * milliSec + return nil +} + +// DurationSlice is a slice of *Duration +type DurationSlice []*Duration + +// NewDurationSlice converts a []*time.Duration to a DurationSlice type. +func NewDurationSlice(t []*time.Duration) DurationSlice { + ds := make([]*Duration, len(t)) + for i := range ds { + ds[i] = NewDuration(t[i]) + } + return ds +} + +// Standard converts a DurationSlice to a []*time.Duration type. +func (ds *DurationSlice) Standard() []*time.Duration { + t := make([]*time.Duration, len(*ds)) + for i := range t { + t[i] = (*ds)[i].Standard() + } + return t +} + +// Durationint32Map is a int32 map of *Duration +type Durationint32Map map[int32]*Duration + +// NewDurationint32Map converts a map[int32]*time.Duration to a Durationint32Map type. +func NewDurationint32Map(t map[int32]*time.Duration) Durationint32Map { + dm := make(Durationint32Map, len(t)) + for i := range t { + dm[i] = NewDuration(t[i]) + } + return dm +} + +// Standard converts a Durationint32Map to a map[int32]*time.Duration type. +func (dm *Durationint32Map) Standard() map[int32]*time.Duration { + t := make(map[int32]*time.Duration, len(*dm)) + for key, value := range *dm { + t[key] = value.Standard() + } + return t +} + +// LongDuration implements a JSON Marshaler to encode a time.Duration in days. +type LongDuration int64 + +const day = LongDuration(time.Hour) * 24 + +// NewLongDuration converts a *time.Duration to a *LongDuration type. +func NewLongDuration(t *time.Duration) *LongDuration { + if t == nil { + return nil + } + d := LongDuration(t.Nanoseconds()) + return &d +} + +// Standard converts a *LongDuration to a *time.Duration type. +func (d *LongDuration) Standard() *time.Duration { + return (*time.Duration)(d) +} + +// MarshalJSON encodes the LongDuration in days. +func (d LongDuration) MarshalJSON() ([]byte, error) { + return json.Marshal(int64(d / day)) +} + +// UnmarshalJSON decodes days to LongDuration. +func (d *LongDuration) UnmarshalJSON(b []byte) error { + var tmp int64 + err := json.Unmarshal(b, &tmp) + if err != nil { + return err + } + *d = LongDuration(tmp) * day + return nil +} + +// LongDurationSlice is a slice of *LongDuration +type LongDurationSlice []*LongDuration + +// NewLongDurationSlice converts a []*time.Duration to a LongDurationSlice type. +func NewLongDurationSlice(t []*time.Duration) LongDurationSlice { + ds := make([]*LongDuration, len(t)) + for i := range ds { + ds[i] = NewLongDuration(t[i]) + } + return ds +} + +// Standard converts a LongDurationSlice to a []*time.Duration type. +func (ds *LongDurationSlice) Standard() []*time.Duration { + t := make([]*time.Duration, len(*ds)) + for i := range t { + t[i] = (*ds)[i].Standard() + } + return t +} + +// LongDurationint32Map is a int32 map of *LongDuration +type LongDurationint32Map map[int32]*LongDuration + +// NewLongDurationint32Map converts a map[int32]*time.LongDuration to a LongDurationint32Map type. +func NewLongDurationint32Map(t map[int32]*time.Duration) LongDurationint32Map { + dm := make(LongDurationint32Map, len(t)) + for i := range t { + dm[i] = NewLongDuration(t[i]) + } + return dm +} + +// Standard converts a LongDurationint32Map to a map[int32]*time.LongDuration type. +func (dm *LongDurationint32Map) Standard() map[int32]*time.Duration { + t := make(map[int32]*time.Duration, len(*dm)) + for key, value := range *dm { + t[key] = value.Standard() + } + return t +} diff --git a/vendor/github.com/scaleway/scaleway-sdk-go/internal/parameter/query.go b/vendor/github.com/scaleway/scaleway-sdk-go/internal/parameter/query.go new file mode 100644 index 000000000..4bbfd20b6 --- /dev/null +++ b/vendor/github.com/scaleway/scaleway-sdk-go/internal/parameter/query.go @@ -0,0 +1,42 @@ +package parameter + +import ( + "fmt" + "net" + "net/url" + "reflect" + "time" + + "github.com/scaleway/scaleway-sdk-go/scw" +) + +// AddToQuery add a key/value pair to an URL query +func AddToQuery(query url.Values, key string, value interface{}) { + elemValue := reflect.ValueOf(value) + + if elemValue.Kind() == reflect.Invalid || elemValue.Kind() == reflect.Ptr && elemValue.IsNil() { + return + } + + for elemValue.Kind() == reflect.Ptr { + elemValue = reflect.ValueOf(value).Elem() + } + + elemType := elemValue.Type() + switch { + case elemType == reflect.TypeOf(net.IP{}): + query.Add(key, value.(*net.IP).String()) + case elemType == reflect.TypeOf(net.IPNet{}): + query.Add(key, value.(*net.IPNet).String()) + case elemType == reflect.TypeOf(scw.IPNet{}): + query.Add(key, value.(*scw.IPNet).String()) + case elemType.Kind() == reflect.Slice: + for i := 0; i < elemValue.Len(); i++ { + query.Add(key, fmt.Sprint(elemValue.Index(i).Interface())) + } + case elemType == reflect.TypeOf(time.Time{}): + query.Add(key, value.(*time.Time).Format(time.RFC3339)) + default: + query.Add(key, fmt.Sprint(elemValue.Interface())) + } +} diff --git a/vendor/github.com/scaleway/scaleway-sdk-go/logger/default_logger.go b/vendor/github.com/scaleway/scaleway-sdk-go/logger/default_logger.go new file mode 100644 index 000000000..bdf1de8d0 --- /dev/null +++ b/vendor/github.com/scaleway/scaleway-sdk-go/logger/default_logger.go @@ -0,0 +1,112 @@ +package logger + +import ( + "fmt" + "io" + "io/ioutil" + "log" + "os" + "strconv" +) + +var DefaultLogger = newLogger(os.Stderr, LogLevelWarning) +var logger Logger = DefaultLogger + +// loggerT is the default logger used by scaleway-sdk-go. +type loggerT struct { + m [4]*log.Logger + v LogLevel +} + +// Init create a new default logger. +// Not mutex-protected, should be called before any scaleway-sdk-go functions. +func (g *loggerT) Init(w io.Writer, level LogLevel) { + g.m = newLogger(w, level).m + g.v = level +} + +// Debugf logs to the DEBUG log. Arguments are handled in the manner of fmt.Printf. +func Debugf(format string, args ...interface{}) { logger.Debugf(format, args...) } +func (g *loggerT) Debugf(format string, args ...interface{}) { + g.m[LogLevelDebug].Printf(format, args...) +} + +// Infof logs to the INFO log. Arguments are handled in the manner of fmt.Printf. +func Infof(format string, args ...interface{}) { logger.Infof(format, args...) } +func (g *loggerT) Infof(format string, args ...interface{}) { + g.m[LogLevelInfo].Printf(format, args...) +} + +// Warningf logs to the WARNING log. Arguments are handled in the manner of fmt.Printf. +func Warningf(format string, args ...interface{}) { logger.Warningf(format, args...) } +func (g *loggerT) Warningf(format string, args ...interface{}) { + g.m[LogLevelWarning].Printf(format, args...) +} + +// Errorf logs to the ERROR log. Arguments are handled in the manner of fmt.Printf. +func Errorf(format string, args ...interface{}) { logger.Errorf(format, args...) } +func (g *loggerT) Errorf(format string, args ...interface{}) { + g.m[LogLevelError].Printf(format, args...) +} + +// ShouldLog reports whether verbosity level l is at least the requested verbose level. +func ShouldLog(level LogLevel) bool { return logger.ShouldLog(level) } +func (g *loggerT) ShouldLog(level LogLevel) bool { + return level >= g.v +} + +func isEnabled(envKey string) bool { + env, exist := os.LookupEnv(envKey) + if !exist { + return false + } + + value, err := strconv.ParseBool(env) + if err != nil { + fmt.Fprintf(os.Stderr, "ERROR: environment variable %s has invalid boolean value\n", envKey) + } + + return value +} + +// newLogger creates a logger to be used as default logger. +// All logs are written to w. +func newLogger(w io.Writer, level LogLevel) *loggerT { + errorW := ioutil.Discard + warningW := ioutil.Discard + infoW := ioutil.Discard + debugW := ioutil.Discard + if isEnabled(DebugEnv) { + level = LogLevelDebug + } + switch level { + case LogLevelDebug: + debugW = w + case LogLevelInfo: + infoW = w + case LogLevelWarning: + warningW = w + case LogLevelError: + errorW = w + } + + // Error logs will be written to errorW, warningW, infoW and debugW. + // Warning logs will be written to warningW, infoW and debugW. + // Info logs will be written to infoW and debugW. + // Debug logs will be written to debugW. + var m [4]*log.Logger + + m[LogLevelError] = log.New(io.MultiWriter(debugW, infoW, warningW, errorW), + severityName[LogLevelError]+": ", log.LstdFlags) + + m[LogLevelWarning] = log.New(io.MultiWriter(debugW, infoW, warningW), + severityName[LogLevelWarning]+": ", log.LstdFlags) + + m[LogLevelInfo] = log.New(io.MultiWriter(debugW, infoW), + severityName[LogLevelInfo]+": ", log.LstdFlags) + + m[LogLevelDebug] = log.New(debugW, + severityName[LogLevelDebug]+": ", log.LstdFlags) + + return &loggerT{m: m, v: level} +} diff --git a/vendor/github.com/scaleway/scaleway-sdk-go/logger/logger.go b/vendor/github.com/scaleway/scaleway-sdk-go/logger/logger.go new file mode 100644 index 000000000..98006145e --- /dev/null +++ b/vendor/github.com/scaleway/scaleway-sdk-go/logger/logger.go @@ -0,0 +1,52 @@ +package logger + +import "os" + +type LogLevel int + +const DebugEnv = "SCW_DEBUG" + +const ( + // LogLevelDebug indicates Debug severity. + LogLevelDebug LogLevel = iota + // LogLevelInfo indicates Info severity. + LogLevelInfo + // LogLevelWarning indicates Warning severity. + LogLevelWarning + // LogLevelError indicates Error severity. + LogLevelError +) + +// severityName contains the string representation of each severity. +var severityName = []string{ + LogLevelDebug: "DEBUG", + LogLevelInfo: "INFO", + LogLevelWarning: "WARNING", + LogLevelError: "ERROR", +} + +// Logger does underlying logging work for scaleway-sdk-go. +type Logger interface { + // Debugf logs to DEBUG log. Arguments are handled in the manner of fmt.Printf. + Debugf(format string, args ...interface{}) + // Infof logs to INFO log. Arguments are handled in the manner of fmt.Printf. + Infof(format string, args ...interface{}) + // Warningf logs to WARNING log. Arguments are handled in the manner of fmt.Printf. + Warningf(format string, args ...interface{}) + // Errorf logs to ERROR log. Arguments are handled in the manner of fmt.Printf. + Errorf(format string, args ...interface{}) + // ShouldLog reports whether verbosity level l is at least the requested verbose level. + ShouldLog(level LogLevel) bool +} + +// SetLogger sets logger that is used in by the SDK. +// Not mutex-protected, should be called before any scaleway-sdk-go functions. +func SetLogger(l Logger) { + logger = l +} + +// EnableDebugMode enable LogLevelDebug on the default logger. +// If a custom logger was provided with SetLogger this method has no effect. +func EnableDebugMode() { + DefaultLogger.Init(os.Stderr, LogLevelDebug) +} diff --git a/vendor/github.com/scaleway/scaleway-sdk-go/namegenerator/name_generator.go b/vendor/github.com/scaleway/scaleway-sdk-go/namegenerator/name_generator.go new file mode 100644 index 000000000..11fda4532 --- /dev/null +++ b/vendor/github.com/scaleway/scaleway-sdk-go/namegenerator/name_generator.go @@ -0,0 +1,863 @@ +// Source: github.com/docker/docker/pkg/namesgenerator + +package namegenerator + +import ( + "math/rand" + "strings" + "time" +) + +var r *rand.Rand + +func init() { + source := rand.NewSource(time.Now().UnixNano()) + r = rand.New(source) +} + +var ( + left = [...]string{ + "admiring", + "adoring", + "affectionate", + "agitated", + "amazing", + "angry", + "awesome", + "beautiful", + "blissful", + "bold", + "boring", + "brave", + "busy", + "charming", + "clever", + "cocky", + "cool", + "compassionate", + "competent", + "condescending", + "confident", + "cranky", + "crazy", + "dazzling", + "determined", + "distracted", + "dreamy", + "eager", + "ecstatic", + "elastic", + "elated", + "elegant", + "eloquent", + "epic", + "exciting", + "fervent", + "festive", + "flamboyant", + "focused", + "friendly", + "frosty", + "funny", + "gallant", + "gifted", + "goofy", + "gracious", + "great", + "happy", + "hardcore", + "heuristic", + "hopeful", + "hungry", + "infallible", + "inspiring", + "interesting", + "intelligent", + "jolly", + "jovial", + "keen", + "kind", + "laughing", + "loving", + "lucid", + "magical", + "mystifying", + "modest", + "musing", + "naughty", + "nervous", + "nice", + "nifty", + "nostalgic", + "objective", + "optimistic", + "peaceful", + "pedantic", + "pensive", + "practical", + "priceless", + "quirky", + "quizzical", + "recursing", + "relaxed", + "reverent", + "romantic", + "sad", + "serene", + "sharp", + "silly", + "sleepy", + "stoic", + "strange", + "stupefied", + "suspicious", + "sweet", + "tender", + "thirsty", + "trusting", + "unruffled", + "upbeat", + "vibrant", + "vigilant", + "vigorous", + "wizardly", + "wonderful", + "xenodochial", + "youthful", + "zealous", + "zen", + } + + // Docker, starting from 0.7.x, generates names from notable scientists and hackers. + // Please, for any amazing man that you add to the list, consider adding an equally amazing woman to it, and vice versa. + right = [...]string{ + // Muhammad ibn Jābir al-Ḥarrānī al-Battānī was a founding father of astronomy. https://en.wikipedia.org/wiki/Mu%E1%B8%A5ammad_ibn_J%C4%81bir_al-%E1%B8%A4arr%C4%81n%C4%AB_al-Batt%C4%81n%C4%AB + "albattani", + + // Frances E. Allen, became the first female IBM Fellow in 1989. In 2006, she became the first female recipient of the ACM's Turing Award. https://en.wikipedia.org/wiki/Frances_E._Allen + "allen", + + // June Almeida - Scottish virologist who took the first pictures of the rubella virus - https://en.wikipedia.org/wiki/June_Almeida + "almeida", + + // Kathleen Antonelli, American computer programmer and one of the six original programmers of the ENIAC - https://en.wikipedia.org/wiki/Kathleen_Antonelli + "antonelli", + + // Maria Gaetana Agnesi - Italian mathematician, philosopher, theologian and humanitarian. She was the first woman to write a mathematics handbook and the first woman appointed as a Mathematics Professor at a University. https://en.wikipedia.org/wiki/Maria_Gaetana_Agnesi + "agnesi", + + // Archimedes was a physicist, engineer and mathematician who invented too many things to list them here. https://en.wikipedia.org/wiki/Archimedes + "archimedes", + + // Maria Ardinghelli - Italian translator, mathematician and physicist - https://en.wikipedia.org/wiki/Maria_Ardinghelli + "ardinghelli", + + // Aryabhata - Ancient Indian mathematician-astronomer during 476-550 CE https://en.wikipedia.org/wiki/Aryabhata + "aryabhata", + + // Wanda Austin - Wanda Austin is the President and CEO of The Aerospace Corporation, a leading architect for the US security space programs. https://en.wikipedia.org/wiki/Wanda_Austin + "austin", + + // Charles Babbage invented the concept of a programmable computer. https://en.wikipedia.org/wiki/Charles_Babbage. + "babbage", + + // Stefan Banach - Polish mathematician, was one of the founders of modern functional analysis. https://en.wikipedia.org/wiki/Stefan_Banach + "banach", + + // Buckaroo Banzai and his mentor Dr. Hikita perfectd the "oscillation overthruster", a device that allows one to pass through solid matter. - https://en.wikipedia.org/wiki/The_Adventures_of_Buckaroo_Banzai_Across_the_8th_Dimension + "banzai", + + // John Bardeen co-invented the transistor - https://en.wikipedia.org/wiki/John_Bardeen + "bardeen", + + // Jean Bartik, born Betty Jean Jennings, was one of the original programmers for the ENIAC computer. https://en.wikipedia.org/wiki/Jean_Bartik + "bartik", + + // Laura Bassi, the world's first female professor https://en.wikipedia.org/wiki/Laura_Bassi + "bassi", + + // Hugh Beaver, British engineer, founder of the Guinness Book of World Records https://en.wikipedia.org/wiki/Hugh_Beaver + "beaver", + + // Alexander Graham Bell - an eminent Scottish-born scientist, inventor, engineer and innovator who is credited with inventing the first practical telephone - https://en.wikipedia.org/wiki/Alexander_Graham_Bell + "bell", + + // Karl Friedrich Benz - a German automobile engineer. Inventor of the first practical motorcar. https://en.wikipedia.org/wiki/Karl_Benz + "benz", + + // Homi J Bhabha - was an Indian nuclear physicist, founding director, and professor of physics at the Tata Institute of Fundamental Research. Colloquially known as "father of Indian nuclear programme"- https://en.wikipedia.org/wiki/Homi_J._Bhabha + "bhabha", + + // Bhaskara II - Ancient Indian mathematician-astronomer whose work on calculus predates Newton and Leibniz by over half a millennium - https://en.wikipedia.org/wiki/Bh%C4%81skara_II#Calculus + "bhaskara", + + // Sue Black - British computer scientist and campaigner. She has been instrumental in saving Bletchley Park, the site of World War II codebreaking - https://en.wikipedia.org/wiki/Sue_Black_(computer_scientist) + "black", + + // Elizabeth Helen Blackburn - Australian-American Nobel laureate; best known for co-discovering telomerase. https://en.wikipedia.org/wiki/Elizabeth_Blackburn + "blackburn", + + // Elizabeth Blackwell - American doctor and first American woman to receive a medical degree - https://en.wikipedia.org/wiki/Elizabeth_Blackwell + "blackwell", + + // Niels Bohr is the father of quantum theory. https://en.wikipedia.org/wiki/Niels_Bohr. + "bohr", + + // Kathleen Booth, she's credited with writing the first assembly language. https://en.wikipedia.org/wiki/Kathleen_Booth + "booth", + + // Anita Borg - Anita Borg was the founding director of the Institute for Women and Technology (IWT). https://en.wikipedia.org/wiki/Anita_Borg + "borg", + + // Satyendra Nath Bose - He provided the foundation for Bose–Einstein statistics and the theory of the Bose–Einstein condensate. - https://en.wikipedia.org/wiki/Satyendra_Nath_Bose + "bose", + + // Katherine Louise Bouman is an imaging scientist and Assistant Professor of Computer Science at the California Institute of Technology. She researches computational methods for imaging, and developed an algorithm that made possible the picture first visualization of a black hole using the Event Horizon Telescope. - https://en.wikipedia.org/wiki/Katie_Bouman + "bouman", + + // Evelyn Boyd Granville - She was one of the first African-American woman to receive a Ph.D. in mathematics; she earned it in 1949 from Yale University. https://en.wikipedia.org/wiki/Evelyn_Boyd_Granville + "boyd", + + // Brahmagupta - Ancient Indian mathematician during 598-670 CE who gave rules to compute with zero - https://en.wikipedia.org/wiki/Brahmagupta#Zero + "brahmagupta", + + // Walter Houser Brattain co-invented the transistor - https://en.wikipedia.org/wiki/Walter_Houser_Brattain + "brattain", + + // Emmett Brown invented time travel. https://en.wikipedia.org/wiki/Emmett_Brown (thanks Brian Goff) + "brown", + + // Linda Brown Buck - American biologist and Nobel laureate best known for her genetic and molecular analyses of the mechanisms of smell. https://en.wikipedia.org/wiki/Linda_B._Buck + "buck", + + // Dame Susan Jocelyn Bell Burnell - Northern Irish astrophysicist who discovered radio pulsars and was the first to analyse them. https://en.wikipedia.org/wiki/Jocelyn_Bell_Burnell + "burnell", + + // Annie Jump Cannon - pioneering female astronomer who classified hundreds of thousands of stars and created the system we use to understand stars today. https://en.wikipedia.org/wiki/Annie_Jump_Cannon + "cannon", + + // Rachel Carson - American marine biologist and conservationist, her book Silent Spring and other writings are credited with advancing the global environmental movement. https://en.wikipedia.org/wiki/Rachel_Carson + "carson", + + // Dame Mary Lucy Cartwright - British mathematician who was one of the first to study what is now known as chaos theory. Also known for Cartwright's theorem which finds applications in signal processing. https://en.wikipedia.org/wiki/Mary_Cartwright + "cartwright", + + // Vinton Gray Cerf - American Internet pioneer, recognised as one of "the fathers of the Internet". With Robert Elliot Kahn, he designed TCP and IP, the primary data communication protocols of the Internet and other computer networks. https://en.wikipedia.org/wiki/Vint_Cerf + "cerf", + + // Subrahmanyan Chandrasekhar - Astrophysicist known for his mathematical theory on different stages and evolution in structures of the stars. He has won nobel prize for physics - https://en.wikipedia.org/wiki/Subrahmanyan_Chandrasekhar + "chandrasekhar", + + // Sergey Alexeyevich Chaplygin (Russian: Серге́й Алексе́евич Чаплы́гин; April 5, 1869 – October 8, 1942) was a Russian and Soviet physicist, mathematician, and mechanical engineer. He is known for mathematical formulas such as Chaplygin's equation and for a hypothetical substance in cosmology called Chaplygin gas, named after him. https://en.wikipedia.org/wiki/Sergey_Chaplygin + "chaplygin", + + // Émilie du Châtelet - French natural philosopher, mathematician, physicist, and author during the early 1730s, known for her translation of and commentary on Isaac Newton's book Principia containing basic laws of physics. https://en.wikipedia.org/wiki/%C3%89milie_du_Ch%C3%A2telet + "chatelet", + + // Asima Chatterjee was an Indian organic chemist noted for her research on vinca alkaloids, development of drugs for treatment of epilepsy and malaria - https://en.wikipedia.org/wiki/Asima_Chatterjee + "chatterjee", + + // Pafnuty Chebyshev - Russian mathematician. He is known fo his works on probability, statistics, mechanics, analytical geometry and number theory https://en.wikipedia.org/wiki/Pafnuty_Chebyshev + "chebyshev", + + // Bram Cohen - American computer programmer and author of the BitTorrent peer-to-peer protocol. https://en.wikipedia.org/wiki/Bram_Cohen + "cohen", + + // David Lee Chaum - American computer scientist and cryptographer. Known for his seminal contributions in the field of anonymous communication. https://en.wikipedia.org/wiki/David_Chaum + "chaum", + + // Joan Clarke - Bletchley Park code breaker during the Second World War who pioneered techniques that remained top secret for decades. Also an accomplished numismatist https://en.wikipedia.org/wiki/Joan_Clarke + "clarke", + + // Jane Colden - American botanist widely considered the first female American botanist - https://en.wikipedia.org/wiki/Jane_Colden + "colden", + + // Gerty Theresa Cori - American biochemist who became the third woman—and first American woman—to win a Nobel Prize in science, and the first woman to be awarded the Nobel Prize in Physiology or Medicine. Cori was born in Prague. https://en.wikipedia.org/wiki/Gerty_Cori + "cori", + + // Seymour Roger Cray was an American electrical engineer and supercomputer architect who designed a series of computers that were the fastest in the world for decades. https://en.wikipedia.org/wiki/Seymour_Cray + "cray", + + // This entry reflects a husband and wife team who worked together: + // Joan Curran was a Welsh scientist who developed radar and invented chaff, a radar countermeasure. https://en.wikipedia.org/wiki/Joan_Curran + // Samuel Curran was an Irish physicist who worked alongside his wife during WWII and invented the proximity fuse. https://en.wikipedia.org/wiki/Samuel_Curran + "curran", + + // Marie Curie discovered radioactivity. https://en.wikipedia.org/wiki/Marie_Curie. + "curie", + + // Charles Darwin established the principles of natural evolution. https://en.wikipedia.org/wiki/Charles_Darwin. + "darwin", + + // Leonardo Da Vinci invented too many things to list here. https://en.wikipedia.org/wiki/Leonardo_da_Vinci. + "davinci", + + // A. K. (Alexander Keewatin) Dewdney, Canadian mathematician, computer scientist, author and filmmaker. Contributor to Scientific American's "Computer Recreations" from 1984 to 1991. Author of Core War (program), The Planiverse, The Armchair Universe, The Magic Machine, The New Turing Omnibus, and more. https://en.wikipedia.org/wiki/Alexander_Dewdney + "dewdney", + + // Satish Dhawan - Indian mathematician and aerospace engineer, known for leading the successful and indigenous development of the Indian space programme. https://en.wikipedia.org/wiki/Satish_Dhawan + "dhawan", + + // Bailey Whitfield Diffie - American cryptographer and one of the pioneers of public-key cryptography. https://en.wikipedia.org/wiki/Whitfield_Diffie + "diffie", + + // Edsger Wybe Dijkstra was a Dutch computer scientist and mathematical scientist. https://en.wikipedia.org/wiki/Edsger_W._Dijkstra. + "dijkstra", + + // Paul Adrien Maurice Dirac - English theoretical physicist who made fundamental contributions to the early development of both quantum mechanics and quantum electrodynamics. https://en.wikipedia.org/wiki/Paul_Dirac + "dirac", + + // Agnes Meyer Driscoll - American cryptanalyst during World Wars I and II who successfully cryptanalysed a number of Japanese ciphers. She was also the co-developer of one of the cipher machines of the US Navy, the CM. https://en.wikipedia.org/wiki/Agnes_Meyer_Driscoll + "driscoll", + + // Donna Dubinsky - played an integral role in the development of personal digital assistants (PDAs) serving as CEO of Palm, Inc. and co-founding Handspring. https://en.wikipedia.org/wiki/Donna_Dubinsky + "dubinsky", + + // Annie Easley - She was a leading member of the team which developed software for the Centaur rocket stage and one of the first African-Americans in her field. https://en.wikipedia.org/wiki/Annie_Easley + "easley", + + // Thomas Alva Edison, prolific inventor https://en.wikipedia.org/wiki/Thomas_Edison + "edison", + + // Albert Einstein invented the general theory of relativity. https://en.wikipedia.org/wiki/Albert_Einstein + "einstein", + + // Alexandra Asanovna Elbakyan (Russian: Алекса́ндра Аса́новна Элбакя́н) is a Kazakhstani graduate student, computer programmer, internet pirate in hiding, and the creator of the site Sci-Hub. Nature has listed her in 2016 in the top ten people that mattered in science, and Ars Technica has compared her to Aaron Swartz. - https://en.wikipedia.org/wiki/Alexandra_Elbakyan + "elbakyan", + + // Taher A. ElGamal - Egyptian cryptographer best known for the ElGamal discrete log cryptosystem and the ElGamal digital signature scheme. https://en.wikipedia.org/wiki/Taher_Elgamal + "elgamal", + + // Gertrude Elion - American biochemist, pharmacologist and the 1988 recipient of the Nobel Prize in Medicine - https://en.wikipedia.org/wiki/Gertrude_Elion + "elion", + + // James Henry Ellis - British engineer and cryptographer employed by the GCHQ. Best known for conceiving for the first time, the idea of public-key cryptography. https://en.wikipedia.org/wiki/James_H._Ellis + "ellis", + + // Douglas Engelbart gave the mother of all demos: https://en.wikipedia.org/wiki/Douglas_Engelbart + "engelbart", + + // Euclid invented geometry. https://en.wikipedia.org/wiki/Euclid + "euclid", + + // Leonhard Euler invented large parts of modern mathematics. https://de.wikipedia.org/wiki/Leonhard_Euler + "euler", + + // Michael Faraday - British scientist who contributed to the study of electromagnetism and electrochemistry. https://en.wikipedia.org/wiki/Michael_Faraday + "faraday", + + // Horst Feistel - German-born American cryptographer who was one of the earliest non-government researchers to study the design and theory of block ciphers. Co-developer of DES and Lucifer. Feistel networks, a symmetric structure used in the construction of block ciphers are named after him. https://en.wikipedia.org/wiki/Horst_Feistel + "feistel", + + // Pierre de Fermat pioneered several aspects of modern mathematics. https://en.wikipedia.org/wiki/Pierre_de_Fermat + "fermat", + + // Enrico Fermi invented the first nuclear reactor. https://en.wikipedia.org/wiki/Enrico_Fermi. + "fermi", + + // Richard Feynman was a key contributor to quantum mechanics and particle physics. https://en.wikipedia.org/wiki/Richard_Feynman + "feynman", + + // Benjamin Franklin is famous for his experiments in electricity and the invention of the lightning rod. + "franklin", + + // Yuri Alekseyevich Gagarin - Soviet pilot and cosmonaut, best known as the first human to journey into outer space. https://en.wikipedia.org/wiki/Yuri_Gagarin + "gagarin", + + // Galileo was a founding father of modern astronomy, and faced politics and obscurantism to establish scientific truth. https://en.wikipedia.org/wiki/Galileo_Galilei + "galileo", + + // Évariste Galois - French mathematician whose work laid the foundations of Galois theory and group theory, two major branches of abstract algebra, and the subfield of Galois connections, all while still in his late teens. https://en.wikipedia.org/wiki/%C3%89variste_Galois + "galois", + + // Kadambini Ganguly - Indian physician, known for being the first South Asian female physician, trained in western medicine, to graduate in South Asia. https://en.wikipedia.org/wiki/Kadambini_Ganguly + "ganguly", + + // William Henry "Bill" Gates III is an American business magnate, philanthropist, investor, computer programmer, and inventor. https://en.wikipedia.org/wiki/Bill_Gates + "gates", + + // Johann Carl Friedrich Gauss - German mathematician who made significant contributions to many fields, including number theory, algebra, statistics, analysis, differential geometry, geodesy, geophysics, mechanics, electrostatics, magnetic fields, astronomy, matrix theory, and optics. https://en.wikipedia.org/wiki/Carl_Friedrich_Gauss + "gauss", + + // Marie-Sophie Germain - French mathematician, physicist and philosopher. Known for her work on elasticity theory, number theory and philosophy. https://en.wikipedia.org/wiki/Sophie_Germain + "germain", + + // Adele Goldberg, was one of the designers and developers of the Smalltalk language. https://en.wikipedia.org/wiki/Adele_Goldberg_(computer_scientist) + "goldberg", + + // Adele Goldstine, born Adele Katz, wrote the complete technical description for the first electronic digital computer, ENIAC. https://en.wikipedia.org/wiki/Adele_Goldstine + "goldstine", + + // Shafi Goldwasser is a computer scientist known for creating theoretical foundations of modern cryptography. Winner of 2012 ACM Turing Award. https://en.wikipedia.org/wiki/Shafi_Goldwasser + "goldwasser", + + // James Golick, all around gangster. + "golick", + + // Jane Goodall - British primatologist, ethologist, and anthropologist who is considered to be the world's foremost expert on chimpanzees - https://en.wikipedia.org/wiki/Jane_Goodall + "goodall", + + // Stephen Jay Gould was was an American paleontologist, evolutionary biologist, and historian of science. He is most famous for the theory of punctuated equilibrium - https://en.wikipedia.org/wiki/Stephen_Jay_Gould + "gould", + + // Carolyn Widney Greider - American molecular biologist and joint winner of the 2009 Nobel Prize for Physiology or Medicine for the discovery of telomerase. https://en.wikipedia.org/wiki/Carol_W._Greider + "greider", + + // Alexander Grothendieck - German-born French mathematician who became a leading figure in the creation of modern algebraic geometry. https://en.wikipedia.org/wiki/Alexander_Grothendieck + "grothendieck", + + // Lois Haibt - American computer scientist, part of the team at IBM that developed FORTRAN - https://en.wikipedia.org/wiki/Lois_Haibt + "haibt", + + // Margaret Hamilton - Director of the Software Engineering Division of the MIT Instrumentation Laboratory, which developed on-board flight software for the Apollo space program. https://en.wikipedia.org/wiki/Margaret_Hamilton_(scientist) + "hamilton", + + // Caroline Harriet Haslett - English electrical engineer, electricity industry administrator and champion of women's rights. Co-author of British Standard 1363 that specifies AC power plugs and sockets used across the United Kingdom (which is widely considered as one of the safest designs). https://en.wikipedia.org/wiki/Caroline_Haslett + "haslett", + + // Stephen Hawking pioneered the field of cosmology by combining general relativity and quantum mechanics. https://en.wikipedia.org/wiki/Stephen_Hawking + "hawking", + + // Martin Edward Hellman - American cryptologist, best known for his invention of public-key cryptography in co-operation with Whitfield Diffie and Ralph Merkle. https://en.wikipedia.org/wiki/Martin_Hellman + "hellman", + + // Werner Heisenberg was a founding father of quantum mechanics. https://en.wikipedia.org/wiki/Werner_Heisenberg + "heisenberg", + + // Grete Hermann was a German philosopher noted for her philosophical work on the foundations of quantum mechanics. https://en.wikipedia.org/wiki/Grete_Hermann + "hermann", + + // Caroline Lucretia Herschel - German astronomer and discoverer of several comets. https://en.wikipedia.org/wiki/Caroline_Herschel + "herschel", + + // Heinrich Rudolf Hertz - German physicist who first conclusively proved the existence of the electromagnetic waves. https://en.wikipedia.org/wiki/Heinrich_Hertz + "hertz", + + // Jaroslav Heyrovský was the inventor of the polarographic method, father of the electroanalytical method, and recipient of the Nobel Prize in 1959. His main field of work was polarography. https://en.wikipedia.org/wiki/Jaroslav_Heyrovsk%C3%BD + "heyrovsky", + + // Dorothy Hodgkin was a British biochemist, credited with the development of protein crystallography. She was awarded the Nobel Prize in Chemistry in 1964. https://en.wikipedia.org/wiki/Dorothy_Hodgkin + "hodgkin", + + // Douglas R. Hofstadter is an American professor of cognitive science and author of the Pulitzer Prize and American Book Award-winning work Goedel, Escher, Bach: An Eternal Golden Braid in 1979. A mind-bending work which coined Hofstadter's Law: "It always takes longer than you expect, even when you take into account Hofstadter's Law." https://en.wikipedia.org/wiki/Douglas_Hofstadter + "hofstadter", + + // Erna Schneider Hoover revolutionized modern communication by inventing a computerized telephone switching method. https://en.wikipedia.org/wiki/Erna_Schneider_Hoover + "hoover", + + // Grace Hopper developed the first compiler for a computer programming language and is credited with popularizing the term "debugging" for fixing computer glitches. https://en.wikipedia.org/wiki/Grace_Hopper + "hopper", + + // Frances Hugle, she was an American scientist, engineer, and inventor who contributed to the understanding of semiconductors, integrated circuitry, and the unique electrical principles of microscopic materials. https://en.wikipedia.org/wiki/Frances_Hugle + "hugle", + + // Hypatia - Greek Alexandrine Neoplatonist philosopher in Egypt who was one of the earliest mothers of mathematics - https://en.wikipedia.org/wiki/Hypatia + "hypatia", + + // Teruko Ishizaka - Japanese scientist and immunologist who co-discovered the antibody class Immunoglobulin E. https://en.wikipedia.org/wiki/Teruko_Ishizaka + "ishizaka", + + // Mary Jackson, American mathematician and aerospace engineer who earned the highest title within NASA's engineering department - https://en.wikipedia.org/wiki/Mary_Jackson_(engineer) + "jackson", + + // Yeong-Sil Jang was a Korean scientist and astronomer during the Joseon Dynasty; he invented the first metal printing press and water gauge. https://en.wikipedia.org/wiki/Jang_Yeong-sil + "jang", + + // Betty Jennings - one of the original programmers of the ENIAC. https://en.wikipedia.org/wiki/ENIAC - https://en.wikipedia.org/wiki/Jean_Bartik + "jennings", + + // Mary Lou Jepsen, was the founder and chief technology officer of One Laptop Per Child (OLPC), and the founder of Pixel Qi. https://en.wikipedia.org/wiki/Mary_Lou_Jepsen + "jepsen", + + // Katherine Coleman Goble Johnson - American physicist and mathematician contributed to the NASA. https://en.wikipedia.org/wiki/Katherine_Johnson + "johnson", + + // Irène Joliot-Curie - French scientist who was awarded the Nobel Prize for Chemistry in 1935. Daughter of Marie and Pierre Curie. https://en.wikipedia.org/wiki/Ir%C3%A8ne_Joliot-Curie + "joliot", + + // Karen Spärck Jones came up with the concept of inverse document frequency, which is used in most search engines today. https://en.wikipedia.org/wiki/Karen_Sp%C3%A4rck_Jones + "jones", + + // A. P. J. Abdul Kalam - is an Indian scientist aka Missile Man of India for his work on the development of ballistic missile and launch vehicle technology - https://en.wikipedia.org/wiki/A._P._J._Abdul_Kalam + "kalam", + + // Sergey Petrovich Kapitsa (Russian: Серге́й Петро́вич Капи́ца; 14 February 1928 – 14 August 2012) was a Russian physicist and demographer. He was best known as host of the popular and long-running Russian scientific TV show, Evident, but Incredible. His father was the Nobel laureate Soviet-era physicist Pyotr Kapitsa, and his brother was the geographer and Antarctic explorer Andrey Kapitsa. - https://en.wikipedia.org/wiki/Sergey_Kapitsa + "kapitsa", + + // Susan Kare, created the icons and many of the interface elements for the original Apple Macintosh in the 1980s, and was an original employee of NeXT, working as the Creative Director. https://en.wikipedia.org/wiki/Susan_Kare + "kare", + + // Mstislav Keldysh - a Soviet scientist in the field of mathematics and mechanics, academician of the USSR Academy of Sciences (1946), President of the USSR Academy of Sciences (1961–1975), three times Hero of Socialist Labor (1956, 1961, 1971), fellow of the Royal Society of Edinburgh (1968). https://en.wikipedia.org/wiki/Mstislav_Keldysh + "keldysh", + + // Mary Kenneth Keller, Sister Mary Kenneth Keller became the first American woman to earn a PhD in Computer Science in 1965. https://en.wikipedia.org/wiki/Mary_Kenneth_Keller + "keller", + + // Johannes Kepler, German astronomer known for his three laws of planetary motion - https://en.wikipedia.org/wiki/Johannes_Kepler + "kepler", + + // Omar Khayyam - Persian mathematician, astronomer and poet. Known for his work on the classification and solution of cubic equations, for his contribution to the understanding of Euclid's fifth postulate and for computing the length of a year very accurately. https://en.wikipedia.org/wiki/Omar_Khayyam + "khayyam", + + // Har Gobind Khorana - Indian-American biochemist who shared the 1968 Nobel Prize for Physiology - https://en.wikipedia.org/wiki/Har_Gobind_Khorana + "khorana", + + // Jack Kilby invented silicone integrated circuits and gave Silicon Valley its name. - https://en.wikipedia.org/wiki/Jack_Kilby + "kilby", + + // Maria Kirch - German astronomer and first woman to discover a comet - https://en.wikipedia.org/wiki/Maria_Margarethe_Kirch + "kirch", + + // Donald Knuth - American computer scientist, author of "The Art of Computer Programming" and creator of the TeX typesetting system. https://en.wikipedia.org/wiki/Donald_Knuth + "knuth", + + // Sophie Kowalevski - Russian mathematician responsible for important original contributions to analysis, differential equations and mechanics - https://en.wikipedia.org/wiki/Sofia_Kovalevskaya + "kowalevski", + + // Marie-Jeanne de Lalande - French astronomer, mathematician and cataloguer of stars - https://en.wikipedia.org/wiki/Marie-Jeanne_de_Lalande + "lalande", + + // Hedy Lamarr - Actress and inventor. The principles of her work are now incorporated into modern Wi-Fi, CDMA and Bluetooth technology. https://en.wikipedia.org/wiki/Hedy_Lamarr + "lamarr", + + // Leslie B. Lamport - American computer scientist. Lamport is best known for his seminal work in distributed systems and was the winner of the 2013 Turing Award. https://en.wikipedia.org/wiki/Leslie_Lamport + "lamport", + + // Mary Leakey - British paleoanthropologist who discovered the first fossilized Proconsul skull - https://en.wikipedia.org/wiki/Mary_Leakey + "leakey", + + // Henrietta Swan Leavitt - she was an American astronomer who discovered the relation between the luminosity and the period of Cepheid variable stars. https://en.wikipedia.org/wiki/Henrietta_Swan_Leavitt + "leavitt", + + // Esther Miriam Zimmer Lederberg - American microbiologist and a pioneer of bacterial genetics. https://en.wikipedia.org/wiki/Esther_Lederberg + "lederberg", + + // Inge Lehmann - Danish seismologist and geophysicist. Known for discovering in 1936 that the Earth has a solid inner core inside a molten outer core. https://en.wikipedia.org/wiki/Inge_Lehmann + "lehmann", + + // Daniel Lewin - Mathematician, Akamai co-founder, soldier, 9/11 victim-- Developed optimization techniques for routing traffic on the internet. Died attempting to stop the 9-11 hijackers. https://en.wikipedia.org/wiki/Daniel_Lewin + "lewin", + + // Ruth Lichterman - one of the original programmers of the ENIAC. https://en.wikipedia.org/wiki/ENIAC - https://en.wikipedia.org/wiki/Ruth_Teitelbaum + "lichterman", + + // Barbara Liskov - co-developed the Liskov substitution principle. Liskov was also the winner of the Turing Prize in 2008. - https://en.wikipedia.org/wiki/Barbara_Liskov + "liskov", + + // Ada Lovelace invented the first algorithm. https://en.wikipedia.org/wiki/Ada_Lovelace (thanks James Turnbull) + "lovelace", + + // Auguste and Louis Lumière - the first filmmakers in history - https://en.wikipedia.org/wiki/Auguste_and_Louis_Lumi%C3%A8re + "lumiere", + + // Mahavira - Ancient Indian mathematician during 9th century AD who discovered basic algebraic identities - https://en.wikipedia.org/wiki/Mah%C4%81v%C4%ABra_(mathematician) + "mahavira", + + // Lynn Margulis (b. Lynn Petra Alexander) - an American evolutionary theorist and biologist, science author, educator, and popularizer, and was the primary modern proponent for the significance of symbiosis in evolution. - https://en.wikipedia.org/wiki/Lynn_Margulis + "margulis", + + // Yukihiro Matsumoto - Japanese computer scientist and software programmer best known as the chief designer of the Ruby programming language. https://en.wikipedia.org/wiki/Yukihiro_Matsumoto + "matsumoto", + + // James Clerk Maxwell - Scottish physicist, best known for his formulation of electromagnetic theory. https://en.wikipedia.org/wiki/James_Clerk_Maxwell + "maxwell", + + // Maria Mayer - American theoretical physicist and Nobel laureate in Physics for proposing the nuclear shell model of the atomic nucleus - https://en.wikipedia.org/wiki/Maria_Mayer + "mayer", + + // John McCarthy invented LISP: https://en.wikipedia.org/wiki/John_McCarthy_(computer_scientist) + "mccarthy", + + // Barbara McClintock - a distinguished American cytogeneticist, 1983 Nobel Laureate in Physiology or Medicine for discovering transposons. https://en.wikipedia.org/wiki/Barbara_McClintock + "mcclintock", + + // Anne Laura Dorinthea McLaren - British developmental biologist whose work helped lead to human in-vitro fertilisation. https://en.wikipedia.org/wiki/Anne_McLaren + "mclaren", + + // Malcolm McLean invented the modern shipping container: https://en.wikipedia.org/wiki/Malcom_McLean + "mclean", + + // Kay McNulty - one of the original programmers of the ENIAC. https://en.wikipedia.org/wiki/ENIAC - https://en.wikipedia.org/wiki/Kathleen_Antonelli + "mcnulty", + + // Gregor Johann Mendel - Czech scientist and founder of genetics. https://en.wikipedia.org/wiki/Gregor_Mendel + "mendel", + + // Dmitri Mendeleev - a chemist and inventor. He formulated the Periodic Law, created a farsighted version of the periodic table of elements, and used it to correct the properties of some already discovered elements and also to predict the properties of eight elements yet to be discovered. https://en.wikipedia.org/wiki/Dmitri_Mendeleev + "mendeleev", + + // Lise Meitner - Austrian/Swedish physicist who was involved in the discovery of nuclear fission. The element meitnerium is named after her - https://en.wikipedia.org/wiki/Lise_Meitner + "meitner", + + // Carla Meninsky, was the game designer and programmer for Atari 2600 games Dodge 'Em and Warlords. https://en.wikipedia.org/wiki/Carla_Meninsky + "meninsky", + + // Ralph C. Merkle - American computer scientist, known for devising Merkle's puzzles - one of the very first schemes for public-key cryptography. Also, inventor of Merkle trees and co-inventor of the Merkle-Damgård construction for building collision-resistant cryptographic hash functions and the Merkle-Hellman knapsack cryptosystem. https://en.wikipedia.org/wiki/Ralph_Merkle + "merkle", + + // Johanna Mestorf - German prehistoric archaeologist and first female museum director in Germany - https://en.wikipedia.org/wiki/Johanna_Mestorf + "mestorf", + + // Marvin Minsky - Pioneer in Artificial Intelligence, co-founder of the MIT's AI Lab, won the Turing Award in 1969. https://en.wikipedia.org/wiki/Marvin_Minsky + "minsky", + + // Maryam Mirzakhani - an Iranian mathematician and the first woman to win the Fields Medal. https://en.wikipedia.org/wiki/Maryam_Mirzakhani + "mirzakhani", + + // Gordon Earle Moore - American engineer, Silicon Valley founding father, author of Moore's law. https://en.wikipedia.org/wiki/Gordon_Moore + "moore", + + // Samuel Morse - contributed to the invention of a single-wire telegraph system based on European telegraphs and was a co-developer of the Morse code - https://en.wikipedia.org/wiki/Samuel_Morse + "morse", + + // Ian Murdock - founder of the Debian project - https://en.wikipedia.org/wiki/Ian_Murdock + "murdock", + + // May-Britt Moser - Nobel prize winner neuroscientist who contributed to the discovery of grid cells in the brain. https://en.wikipedia.org/wiki/May-Britt_Moser + "moser", + + // John Napier of Merchiston - Scottish landowner known as an astronomer, mathematician and physicist. Best known for his discovery of logarithms. https://en.wikipedia.org/wiki/John_Napier + "napier", + + // John Forbes Nash, Jr. - American mathematician who made fundamental contributions to game theory, differential geometry, and the study of partial differential equations. https://en.wikipedia.org/wiki/John_Forbes_Nash_Jr. + "nash", + + // John von Neumann - todays computer architectures are based on the von Neumann architecture. https://en.wikipedia.org/wiki/Von_Neumann_architecture + "neumann", + + // Isaac Newton invented classic mechanics and modern optics. https://en.wikipedia.org/wiki/Isaac_Newton + "newton", + + // Xavier Niel - ;) https://en.wikipedia.org/wiki/Xavier_Niel + "niel", + + // Florence Nightingale, more prominently known as a nurse, was also the first female member of the Royal Statistical Society and a pioneer in statistical graphics https://en.wikipedia.org/wiki/Florence_Nightingale#Statistics_and_sanitary_reform + "nightingale", + + // Alfred Nobel - a Swedish chemist, engineer, innovator, and armaments manufacturer (inventor of dynamite) - https://en.wikipedia.org/wiki/Alfred_Nobel + "nobel", + + // Emmy Noether, German mathematician. Noether's Theorem is named after her. https://en.wikipedia.org/wiki/Emmy_Noether + "noether", + + // Poppy Northcutt. Poppy Northcutt was the first woman to work as part of NASA’s Mission Control. http://www.businessinsider.com/poppy-northcutt-helped-apollo-astronauts-2014-12?op=1 + "northcutt", + + // Robert Noyce invented silicone integrated circuits and gave Silicon Valley its name. - https://en.wikipedia.org/wiki/Robert_Noyce + "noyce", + + // Panini - Ancient Indian linguist and grammarian from 4th century CE who worked on the world's first formal system - https://en.wikipedia.org/wiki/P%C4%81%E1%B9%87ini#Comparison_with_modern_formal_systems + "panini", + + // Ambroise Pare invented modern surgery. https://en.wikipedia.org/wiki/Ambroise_Par%C3%A9 + "pare", + + // Blaise Pascal, French mathematician, physicist, and inventor - https://en.wikipedia.org/wiki/Blaise_Pascal + "pascal", + + // Louis Pasteur discovered vaccination, fermentation and pasteurization. https://en.wikipedia.org/wiki/Louis_Pasteur. + "pasteur", + + // Cecilia Payne-Gaposchkin was an astronomer and astrophysicist who, in 1925, proposed in her Ph.D. thesis an explanation for the composition of stars in terms of the relative abundances of hydrogen and helium. https://en.wikipedia.org/wiki/Cecilia_Payne-Gaposchkin + "payne", + + // Radia Perlman is a software designer and network engineer and most famous for her invention of the spanning-tree protocol (STP). https://en.wikipedia.org/wiki/Radia_Perlman + "perlman", + + // Rob Pike was a key contributor to Unix, Plan 9, the X graphic system, utf-8, and the Go programming language. https://en.wikipedia.org/wiki/Rob_Pike + "pike", + + // Henri Poincaré made fundamental contributions in several fields of mathematics. https://en.wikipedia.org/wiki/Henri_Poincar%C3%A9 + "poincare", + + // Laura Poitras is a director and producer whose work, made possible by open source crypto tools, advances the causes of truth and freedom of information by reporting disclosures by whistleblowers such as Edward Snowden. https://en.wikipedia.org/wiki/Laura_Poitras + "poitras", + + // Tat’yana Avenirovna Proskuriakova (Russian: Татья́на Авени́ровна Проскуряко́ва) (January 23 [O.S. January 10] 1909 – August 30, 1985) was a Russian-American Mayanist scholar and archaeologist who contributed significantly to the deciphering of Maya hieroglyphs, the writing system of the pre-Columbian Maya civilization of Mesoamerica. https://en.wikipedia.org/wiki/Tatiana_Proskouriakoff + "proskuriakova", + + // Claudius Ptolemy - a Greco-Egyptian writer of Alexandria, known as a mathematician, astronomer, geographer, astrologer, and poet of a single epigram in the Greek Anthology - https://en.wikipedia.org/wiki/Ptolemy + "ptolemy", + + // C. V. Raman - Indian physicist who won the Nobel Prize in 1930 for proposing the Raman effect. - https://en.wikipedia.org/wiki/C._V._Raman + "raman", + + // Srinivasa Ramanujan - Indian mathematician and autodidact who made extraordinary contributions to mathematical analysis, number theory, infinite series, and continued fractions. - https://en.wikipedia.org/wiki/Srinivasa_Ramanujan + "ramanujan", + + // Sally Kristen Ride was an American physicist and astronaut. She was the first American woman in space, and the youngest American astronaut. https://en.wikipedia.org/wiki/Sally_Ride + "ride", + + // Rita Levi-Montalcini - Won Nobel Prize in Physiology or Medicine jointly with colleague Stanley Cohen for the discovery of nerve growth factor (https://en.wikipedia.org/wiki/Rita_Levi-Montalcini) + "montalcini", + + // Dennis Ritchie - co-creator of UNIX and the C programming language. - https://en.wikipedia.org/wiki/Dennis_Ritchie + "ritchie", + + // Ida Rhodes - American pioneer in computer programming, designed the first computer used for Social Security. https://en.wikipedia.org/wiki/Ida_Rhodes + "rhodes", + + // Julia Hall Bowman Robinson - American mathematician renowned for her contributions to the fields of computability theory and computational complexity theory. https://en.wikipedia.org/wiki/Julia_Robinson + "robinson", + + // Wilhelm Conrad Röntgen - German physicist who was awarded the first Nobel Prize in Physics in 1901 for the discovery of X-rays (Röntgen rays). https://en.wikipedia.org/wiki/Wilhelm_R%C3%B6ntgen + "roentgen", + + // Rosalind Franklin - British biophysicist and X-ray crystallographer whose research was critical to the understanding of DNA - https://en.wikipedia.org/wiki/Rosalind_Franklin + "rosalind", + + // Vera Rubin - American astronomer who pioneered work on galaxy rotation rates. https://en.wikipedia.org/wiki/Vera_Rubin + "rubin", + + // Meghnad Saha - Indian astrophysicist best known for his development of the Saha equation, used to describe chemical and physical conditions in stars - https://en.wikipedia.org/wiki/Meghnad_Saha + "saha", + + // Jean E. Sammet developed FORMAC, the first widely used computer language for symbolic manipulation of mathematical formulas. https://en.wikipedia.org/wiki/Jean_E._Sammet + "sammet", + + // Mildred Sanderson - American mathematician best known for Sanderson's theorem concerning modular invariants. https://en.wikipedia.org/wiki/Mildred_Sanderson + "sanderson", + + // Satoshi Nakamoto is the name used by the unknown person or group of people who developed bitcoin, authored the bitcoin white paper, and created and deployed bitcoin's original reference implementation. https://en.wikipedia.org/wiki/Satoshi_Nakamoto + "satoshi", + + // Adi Shamir - Israeli cryptographer whose numerous inventions and contributions to cryptography include the Ferge Fiat Shamir identification scheme, the Rivest Shamir Adleman (RSA) public-key cryptosystem, the Shamir's secret sharing scheme, the breaking of the Merkle-Hellman cryptosystem, the TWINKLE and TWIRL factoring devices and the discovery of differential cryptanalysis (with Eli Biham). https://en.wikipedia.org/wiki/Adi_Shamir + "shamir", + + // Claude Shannon - The father of information theory and founder of digital circuit design theory. (https://en.wikipedia.org/wiki/Claude_Shannon) + "shannon", + + // Carol Shaw - Originally an Atari employee, Carol Shaw is said to be the first female video game designer. https://en.wikipedia.org/wiki/Carol_Shaw_(video_game_designer) + "shaw", + + // Dame Stephanie "Steve" Shirley - Founded a software company in 1962 employing women working from home. https://en.wikipedia.org/wiki/Steve_Shirley + "shirley", + + // William Shockley co-invented the transistor - https://en.wikipedia.org/wiki/William_Shockley + "shockley", + + // Lina Solomonovna Stern (or Shtern; Russian: Лина Соломоновна Штерн; 26 August 1878 – 7 March 1968) was a Soviet biochemist, physiologist and humanist whose medical discoveries saved thousands of lives at the fronts of World War II. She is best known for her pioneering work on blood–brain barrier, which she described as hemato-encephalic barrier in 1921. https://en.wikipedia.org/wiki/Lina_Stern + "shtern", + + // Françoise Barré-Sinoussi - French virologist and Nobel Prize Laureate in Physiology or Medicine; her work was fundamental in identifying HIV as the cause of AIDS. https://en.wikipedia.org/wiki/Fran%C3%A7oise_Barr%C3%A9-Sinoussi + "sinoussi", + + // Betty Snyder - one of the original programmers of the ENIAC. https://en.wikipedia.org/wiki/ENIAC - https://en.wikipedia.org/wiki/Betty_Holberton + "snyder", + + // Cynthia Solomon - Pioneer in the fields of artificial intelligence, computer science and educational computing. Known for creation of Logo, an educational programming language. https://en.wikipedia.org/wiki/Cynthia_Solomon + "solomon", + + // Frances Spence - one of the original programmers of the ENIAC. https://en.wikipedia.org/wiki/ENIAC - https://en.wikipedia.org/wiki/Frances_Spence + "spence", + + // Richard Matthew Stallman - the founder of the Free Software movement, the GNU project, the Free Software Foundation, and the League for Programming Freedom. He also invented the concept of copyleft to protect the ideals of this movement, and enshrined this concept in the widely-used GPL (General Public License) for software. https://en.wikiquote.org/wiki/Richard_Stallman + "stallman", + + // Michael Stonebraker is a database research pioneer and architect of Ingres, Postgres, VoltDB and SciDB. Winner of 2014 ACM Turing Award. https://en.wikipedia.org/wiki/Michael_Stonebraker + "stonebraker", + + // Ivan Edward Sutherland - American computer scientist and Internet pioneer, widely regarded as the father of computer graphics. https://en.wikipedia.org/wiki/Ivan_Sutherland + "sutherland", + + // Janese Swanson (with others) developed the first of the Carmen Sandiego games. She went on to found Girl Tech. https://en.wikipedia.org/wiki/Janese_Swanson + "swanson", + + // Aaron Swartz was influential in creating RSS, Markdown, Creative Commons, Reddit, and much of the internet as we know it today. He was devoted to freedom of information on the web. https://en.wikiquote.org/wiki/Aaron_Swartz + "swartz", + + // Bertha Swirles was a theoretical physicist who made a number of contributions to early quantum theory. https://en.wikipedia.org/wiki/Bertha_Swirles + "swirles", + + // Helen Brooke Taussig - American cardiologist and founder of the field of paediatric cardiology. https://en.wikipedia.org/wiki/Helen_B._Taussig + "taussig", + + // Valentina Tereshkova is a Russian engineer, cosmonaut and politician. She was the first woman to fly to space in 1963. In 2013, at the age of 76, she offered to go on a one-way mission to Mars. https://en.wikipedia.org/wiki/Valentina_Tereshkova + "tereshkova", + + // Nikola Tesla invented the AC electric system and every gadget ever used by a James Bond villain. https://en.wikipedia.org/wiki/Nikola_Tesla + "tesla", + + // Marie Tharp - American geologist and oceanic cartographer who co-created the first scientific map of the Atlantic Ocean floor. Her work led to the acceptance of the theories of plate tectonics and continental drift. https://en.wikipedia.org/wiki/Marie_Tharp + "tharp", + + // Ken Thompson - co-creator of UNIX and the C programming language - https://en.wikipedia.org/wiki/Ken_Thompson + "thompson", + + // Linus Torvalds invented Linux and Git. https://en.wikipedia.org/wiki/Linus_Torvalds + "torvalds", + + // Youyou Tu - Chinese pharmaceutical chemist and educator known for discovering artemisinin and dihydroartemisinin, used to treat malaria, which has saved millions of lives. Joint winner of the 2015 Nobel Prize in Physiology or Medicine. https://en.wikipedia.org/wiki/Tu_Youyou + "tu", + + // Alan Turing was a founding father of computer science. https://en.wikipedia.org/wiki/Alan_Turing. + "turing", + + // Varahamihira - Ancient Indian mathematician who discovered trigonometric formulae during 505-587 CE - https://en.wikipedia.org/wiki/Var%C4%81hamihira#Contributions + "varahamihira", + + // Dorothy Vaughan was a NASA mathematician and computer programmer on the SCOUT launch vehicle program that put America's first satellites into space - https://en.wikipedia.org/wiki/Dorothy_Vaughan + "vaughan", + + // Sir Mokshagundam Visvesvaraya - is a notable Indian engineer. He is a recipient of the Indian Republic's highest honour, the Bharat Ratna, in 1955. On his birthday, 15 September is celebrated as Engineer's Day in India in his memory - https://en.wikipedia.org/wiki/Visvesvaraya + "visvesvaraya", + + // Christiane Nüsslein-Volhard - German biologist, won Nobel Prize in Physiology or Medicine in 1995 for research on the genetic control of embryonic development. https://en.wikipedia.org/wiki/Christiane_N%C3%BCsslein-Volhard + "volhard", + + // Cédric Villani - French mathematician, won Fields Medal, Fermat Prize and Poincaré Price for his work in differential geometry and statistical mechanics. https://en.wikipedia.org/wiki/C%C3%A9dric_Villani + "villani", + + // Marlyn Wescoff - one of the original programmers of the ENIAC. https://en.wikipedia.org/wiki/ENIAC - https://en.wikipedia.org/wiki/Marlyn_Meltzer + "wescoff", + + // Sylvia B. Wilbur - British computer scientist who helped develop the ARPANET, was one of the first to exchange email in the UK and a leading researcher in computer-supported collaborative work. https://en.wikipedia.org/wiki/Sylvia_Wilbur + "wilbur", + + // Andrew Wiles - Notable British mathematician who proved the enigmatic Fermat's Last Theorem - https://en.wikipedia.org/wiki/Andrew_Wiles + "wiles", + + // Roberta Williams, did pioneering work in graphical adventure games for personal computers, particularly the King's Quest series. https://en.wikipedia.org/wiki/Roberta_Williams + "williams", + + // Malcolm John Williamson - British mathematician and cryptographer employed by the GCHQ. Developed in 1974 what is now known as Diffie-Hellman key exchange (Diffie and Hellman first published the scheme in 1976). https://en.wikipedia.org/wiki/Malcolm_J._Williamson + "williamson", + + // Sophie Wilson designed the first Acorn Micro-Computer and the instruction set for ARM processors. https://en.wikipedia.org/wiki/Sophie_Wilson + "wilson", + + // Jeannette Wing - co-developed the Liskov substitution principle. - https://en.wikipedia.org/wiki/Jeannette_Wing + "wing", + + // Steve Wozniak invented the Apple I and Apple II. https://en.wikipedia.org/wiki/Steve_Wozniak + "wozniak", + + // The Wright brothers, Orville and Wilbur - credited with inventing and building the world's first successful airplane and making the first controlled, powered and sustained heavier-than-air human flight - https://en.wikipedia.org/wiki/Wright_brothers + "wright", + + // Chien-Shiung Wu - Chinese-American experimental physicist who made significant contributions to nuclear physics. https://en.wikipedia.org/wiki/Chien-Shiung_Wu + "wu", + + // Rosalyn Sussman Yalow - Rosalyn Sussman Yalow was an American medical physicist, and a co-winner of the 1977 Nobel Prize in Physiology or Medicine for development of the radioimmunoassay technique. https://en.wikipedia.org/wiki/Rosalyn_Sussman_Yalow + "yalow", + + // Ada Yonath - an Israeli crystallographer, the first woman from the Middle East to win a Nobel prize in the sciences. https://en.wikipedia.org/wiki/Ada_Yonath + "yonath", + + // Nikolay Yegorovich Zhukovsky (Russian: Никола́й Его́рович Жуко́вский, January 17 1847 – March 17, 1921) was a Russian scientist, mathematician and engineer, and a founding father of modern aero- and hydrodynamics. Whereas contemporary scientists scoffed at the idea of human flight, Zhukovsky was the first to undertake the study of airflow. He is often called the Father of Russian Aviation. https://en.wikipedia.org/wiki/Nikolay_Yegorovich_Zhukovsky + "zhukovsky", + } +) + +// GetRandomName generates a random name from the list of adjectives and surnames in this package +// formatted as "scw-adjective-surname". For example 'scw-focused-turing'. +func GetRandomName(prefixes ...string) string { +begin: + parts := append(prefixes, left[r.Intn(len(left))], right[r.Intn(len(right))]) + name := strings.Join(parts, "-") + if strings.Contains(name, "boring-wozniak") /* Steve Wozniak is not boring */ { + goto begin + } + + return name +} diff --git a/vendor/github.com/scaleway/scaleway-sdk-go/scw/README.md b/vendor/github.com/scaleway/scaleway-sdk-go/scw/README.md new file mode 100644 index 000000000..e35e05df7 --- /dev/null +++ b/vendor/github.com/scaleway/scaleway-sdk-go/scw/README.md @@ -0,0 +1,52 @@ +# Scaleway config + +## TL;DR + +Recommended config file: + +```yaml +# Get your credentials on https://console.scaleway.com/project/credentials +access_key: SCWXXXXXXXXXXXXXXXXX +secret_key: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx +default_organization_id: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx +default_project_id: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx +default_region: fr-par +default_zone: fr-par-1 +``` + +## Config file path + +The function [`GetConfigPath`](https://godoc.org/github.com/scaleway/scaleway-sdk-go/scw#GetConfigPath) will try to locate the config file in the following ways: + +1. Custom directory: `$SCW_CONFIG_PATH` +2. [XDG base directory](https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html): `$XDG_CONFIG_HOME/scw/config.yaml` +3. Unix home directory: `$HOME/.config/scw/config.yaml` +4. Windows home directory: `%USERPROFILE%/.config/scw/config.yaml` + +## Reading config order + +[ClientOption](https://godoc.org/github.com/scaleway/scaleway-sdk-go/scw#ClientOption) ordering will decide the order in which the config should apply: + +```go +p, _ := scw.MustLoadConfig().GetActiveProfile() + +scw.NewClient( + scw.WithProfile(p), // active profile applies first + scw.WithEnv(), // existing env variables may overwrite active profile + scw.WithDefaultRegion(scw.RegionFrPar) // any prior region set will be discarded to usr the new one +) +``` + +## Environment variables + +| Variable | Description | Legacy variables | +| :----------------------------- | :----------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------ | +| `$SCW_ACCESS_KEY` | Access key of a token ([get yours](https://console.scaleway.com/project/credentials)) | `$SCALEWAY_ACCESS_KEY` (used by terraform) | +| `$SCW_SECRET_KEY` | Secret key of a token ([get yours](https://console.scaleway.com/project/credentials)) | `$SCW_TOKEN` (used by cli), `$SCALEWAY_TOKEN` (used by terraform), `$SCALEWAY_ACCESS_KEY` (used by terraform) | +| `$SCW_DEFAULT_ORGANIZATION_ID` | Your default organization ID ([get yours](https://console.scaleway.com/project/credentials)) | `$SCW_ORGANIZATION` (used by cli),`$SCALEWAY_ORGANIZATION` (used by terraform) | +| `$SCW_DEFAULT_PROJECT_ID` | Your default project ID ([get yours](https://console.scaleway.com/project/credentials)) | | +| `$SCW_DEFAULT_REGION` | Your default [region](https://developers.scaleway.com/en/quickstart/#region-and-zone) | `$SCW_REGION` (used by cli),`$SCALEWAY_REGION` (used by terraform) | +| `$SCW_DEFAULT_ZONE` | Your default [availability zone](https://developers.scaleway.com/en/quickstart/#region-and-zone) | `$SCW_ZONE` (used by cli),`$SCALEWAY_ZONE` (used by terraform) | +| `$SCW_API_URL` | Url of the API | - | +| `$SCW_INSECURE` | Set this to `true` to enable the insecure mode | `$SCW_TLSVERIFY` (inverse flag used by the cli) | +| `$SCW_PROFILE` | Set the config profile to use | - | diff --git a/vendor/github.com/scaleway/scaleway-sdk-go/scw/client.go b/vendor/github.com/scaleway/scaleway-sdk-go/scw/client.go new file mode 100644 index 000000000..b5dc7a378 --- /dev/null +++ b/vendor/github.com/scaleway/scaleway-sdk-go/scw/client.go @@ -0,0 +1,583 @@ +package scw + +import ( + "crypto/tls" + "encoding/json" + "fmt" + "io" + "math" + "net/http" + "reflect" + "strconv" + "strings" + "sync" + "time" + + "github.com/scaleway/scaleway-sdk-go/internal/auth" + "github.com/scaleway/scaleway-sdk-go/internal/errors" + "github.com/scaleway/scaleway-sdk-go/internal/generic" + "github.com/scaleway/scaleway-sdk-go/logger" +) + +// Client is the Scaleway client which performs API requests. +// +// This client should be passed in the `NewApi` functions whenever an API instance is created. +// Creating a Client is done with the `NewClient` function. +type Client struct { + httpClient httpClient + auth auth.Auth + apiURL string + userAgent string + defaultOrganizationID *string + defaultProjectID *string + defaultRegion *Region + defaultZone *Zone + defaultPageSize *uint32 +} + +func defaultOptions() []ClientOption { + return []ClientOption{ + WithoutAuth(), + WithAPIURL("https://api.scaleway.com"), + withDefaultUserAgent(userAgent), + } +} + +// NewClient instantiate a new Client object. +// +// Zero or more ClientOption object can be passed as a parameter. +// These options will then be applied to the client. +func NewClient(opts ...ClientOption) (*Client, error) { + s := newSettings() + + // apply options + s.apply(append(defaultOptions(), opts...)) + + // validate settings + err := s.validate() + if err != nil { + return nil, err + } + + // dial the API + if s.httpClient == nil { + s.httpClient = newHTTPClient() + } + + // insecure mode + if s.insecure { + logger.Debugf("client: using insecure mode") + setInsecureMode(s.httpClient) + } + + if logger.ShouldLog(logger.LogLevelDebug) { + logger.Debugf("client: using request logger") + setRequestLogging(s.httpClient) + } + + logger.Debugf("client: using sdk version " + version) + + return &Client{ + auth: s.token, + httpClient: s.httpClient, + apiURL: s.apiURL, + userAgent: s.userAgent, + defaultOrganizationID: s.defaultOrganizationID, + defaultProjectID: s.defaultProjectID, + defaultRegion: s.defaultRegion, + defaultZone: s.defaultZone, + defaultPageSize: s.defaultPageSize, + }, nil +} + +// GetDefaultOrganizationID returns the default organization ID +// of the client. This value can be set in the client option +// WithDefaultOrganizationID(). Be aware this value can be empty. +func (c *Client) GetDefaultOrganizationID() (organizationID string, exists bool) { + if c.defaultOrganizationID != nil { + return *c.defaultOrganizationID, true + } + return "", false +} + +// GetDefaultProjectID returns the default project ID +// of the client. This value can be set in the client option +// WithDefaultProjectID(). Be aware this value can be empty. +func (c *Client) GetDefaultProjectID() (projectID string, exists bool) { + if c.defaultProjectID != nil { + return *c.defaultProjectID, true + } + return "", false +} + +// GetDefaultRegion returns the default region of the client. +// This value can be set in the client option +// WithDefaultRegion(). Be aware this value can be empty. +func (c *Client) GetDefaultRegion() (region Region, exists bool) { + if c.defaultRegion != nil { + return *c.defaultRegion, true + } + return Region(""), false +} + +// GetDefaultZone returns the default zone of the client. +// This value can be set in the client option +// WithDefaultZone(). Be aware this value can be empty. +func (c *Client) GetDefaultZone() (zone Zone, exists bool) { + if c.defaultZone != nil { + return *c.defaultZone, true + } + return Zone(""), false +} + +func (c *Client) GetSecretKey() (secretKey string, exists bool) { + if token, isToken := c.auth.(*auth.Token); isToken { + return token.SecretKey, isToken + } + return "", false +} + +func (c *Client) GetAccessKey() (accessKey string, exists bool) { + if token, isToken := c.auth.(*auth.Token); isToken { + return token.AccessKey, isToken + } else if token, isAccessKey := c.auth.(*auth.AccessKeyOnly); isAccessKey { + return token.AccessKey, isAccessKey + } + + return "", false +} + +// GetDefaultPageSize returns the default page size of the client. +// This value can be set in the client option +// WithDefaultPageSize(). Be aware this value can be empty. +func (c *Client) GetDefaultPageSize() (pageSize uint32, exists bool) { + if c.defaultPageSize != nil { + return *c.defaultPageSize, true + } + return 0, false +} + +// Do performs HTTP request(s) based on the ScalewayRequest object. +// RequestOptions are applied prior to doing the request. +func (c *Client) Do(req *ScalewayRequest, res interface{}, opts ...RequestOption) (err error) { + // apply request options + req.apply(opts) + + // validate request options + err = req.validate() + if err != nil { + return err + } + + if req.auth == nil { + req.auth = c.auth + } + + if req.zones != nil { + return c.doListZones(req, res, req.zones) + } + if req.regions != nil { + return c.doListRegions(req, res, req.regions) + } + + if req.allPages { + return c.doListAll(req, res) + } + + return c.do(req, res) +} + +// do performs a single HTTP request based on the ScalewayRequest object. +func (c *Client) do(req *ScalewayRequest, res interface{}) (sdkErr error) { + + if req == nil { + return errors.New("request must be non-nil") + } + + // build url + url, sdkErr := req.getURL(c.apiURL) + if sdkErr != nil { + return sdkErr + } + logger.Debugf("creating %s request on %s", req.Method, url.String()) + + // build request + httpRequest, err := http.NewRequest(req.Method, url.String(), req.Body) + if err != nil { + return errors.Wrap(err, "could not create request") + } + + httpRequest.Header = req.getAllHeaders(req.auth, c.userAgent, false) + + if req.ctx != nil { + httpRequest = httpRequest.WithContext(req.ctx) + } + + // execute request + httpResponse, err := c.httpClient.Do(httpRequest) + if err != nil { + return errors.Wrap(err, "error executing request") + } + + defer func() { + closeErr := httpResponse.Body.Close() + if sdkErr == nil && closeErr != nil { + sdkErr = errors.Wrap(closeErr, "could not close http response") + } + }() + + sdkErr = hasResponseError(httpResponse) + if sdkErr != nil { + return sdkErr + } + + if res != nil { + contentType := httpResponse.Header.Get("Content-Type") + + if strings.HasPrefix(contentType, "application/json") { + err = json.NewDecoder(httpResponse.Body).Decode(&res) + if err != nil { + return errors.Wrap(err, "could not parse %s response body", contentType) + } + } else { + buffer, isBuffer := res.(io.Writer) + if !isBuffer { + return errors.Wrap(err, "could not handle %s response body with %T result type", contentType, buffer) + } + + _, err := io.Copy(buffer, httpResponse.Body) + if err != nil { + return errors.Wrap(err, "could not copy %s response body", contentType) + } + } + + // Handle instance API X-Total-Count header + xTotalCountStr := httpResponse.Header.Get("X-Total-Count") + if legacyLister, isLegacyLister := res.(legacyLister); isLegacyLister && xTotalCountStr != "" { + xTotalCount, err := strconv.ParseInt(xTotalCountStr, 10, 32) + if err != nil { + return errors.Wrap(err, "could not parse X-Total-Count header") + } + legacyLister.UnsafeSetTotalCount(int(xTotalCount)) + } + } + + return nil +} + +type lister interface { + UnsafeGetTotalCount() uint64 + UnsafeAppend(interface{}) (uint64, error) +} + +// Old lister for uint32 +// Used for retro-compatibility with response that use uint32 +type lister32 interface { + UnsafeGetTotalCount() uint32 + UnsafeAppend(interface{}) (uint32, error) +} + +type legacyLister interface { + UnsafeSetTotalCount(totalCount int) +} + +func listerGetTotalCount(i interface{}) uint64 { + if l, isLister := i.(lister); isLister { + return l.UnsafeGetTotalCount() + } + if l32, isLister32 := i.(lister32); isLister32 { + return uint64(l32.UnsafeGetTotalCount()) + } + panic(fmt.Errorf("%T does not support pagination but checks failed, should not happen", i)) +} + +func listerAppend(recv interface{}, elems interface{}) (uint64, error) { + if l, isLister := recv.(lister); isLister { + return l.UnsafeAppend(elems) + } else if l32, isLister32 := recv.(lister32); isLister32 { + total, err := l32.UnsafeAppend(elems) + return uint64(total), err + } + + panic(fmt.Errorf("%T does not support pagination but checks failed, should not happen", recv)) +} + +func isLister(i interface{}) bool { + switch i.(type) { + case lister: + return true + case lister32: + return true + default: + return false + } +} + +const maxPageCount uint64 = math.MaxUint32 + +// doListAll collects all pages of a List request and aggregate all results on a single response. +func (c *Client) doListAll(req *ScalewayRequest, res interface{}) (err error) { + // check for lister interface + if isLister(res) { + pageCount := maxPageCount + for page := uint64(1); page <= pageCount; page++ { + // set current page + req.Query.Set("page", strconv.FormatUint(page, 10)) + + // request the next page + nextPage := newVariableFromType(res) + err := c.do(req, nextPage) + if err != nil { + return err + } + + // append results + pageSize, err := listerAppend(res, nextPage) + if err != nil { + return err + } + + if pageSize == 0 { + return nil + } + + // set total count on first request + if pageCount == maxPageCount { + totalCount := listerGetTotalCount(nextPage) + pageCount = (totalCount + pageSize - 1) / pageSize + } + } + return nil + } + + return errors.New("%T does not support pagination", res) +} + +// doListLocalities collects all localities using mutliple list requests and aggregate all results on a lister response +// results is sorted by locality +func (c *Client) doListLocalities(req *ScalewayRequest, res interface{}, localities []string) (err error) { + path := req.Path + if !strings.Contains(path, "%locality%") { + return fmt.Errorf("request is not a valid locality request") + } + // Requests are parallelized + responseMutex := sync.Mutex{} + requestGroup := sync.WaitGroup{} + errChan := make(chan error, len(localities)) + + requestGroup.Add(len(localities)) + for _, locality := range localities { + go func(locality string) { + defer requestGroup.Done() + // Request is cloned as doListAll will change header + // We remove zones as it would recurse in the same function + req := req.clone() + req.zones = []Zone(nil) + req.Path = strings.ReplaceAll(path, "%locality%", locality) + + // We create a new response that we append to main response + zoneResponse := newVariableFromType(res) + err := c.Do(req, zoneResponse) + if err != nil { + errChan <- err + } + responseMutex.Lock() + _, err = listerAppend(res, zoneResponse) + responseMutex.Unlock() + if err != nil { + errChan <- err + } + }(locality) + } + requestGroup.Wait() + +L: // We gather potential errors and return them all together + for { + select { + case newErr := <-errChan: + err = errors.Wrap(err, newErr.Error()) + default: + break L + } + } + close(errChan) + if err != nil { + return err + } + return nil +} + +// doListZones collects all zones using multiple list requests and aggregate all results on a single response. +// result is sorted by zone +func (c *Client) doListZones(req *ScalewayRequest, res interface{}, zones []Zone) (err error) { + if isLister(res) { + // Prepare request with %zone% that can be replaced with actual zone + for _, zone := range AllZones { + if strings.Contains(req.Path, string(zone)) { + req.Path = strings.ReplaceAll(req.Path, string(zone), "%locality%") + break + } + } + if !strings.Contains(req.Path, "%locality%") { + return fmt.Errorf("request is not a valid zoned request") + } + localities := make([]string, 0, len(zones)) + for _, zone := range zones { + localities = append(localities, string(zone)) + } + + err := c.doListLocalities(req, res, localities) + if err != nil { + return fmt.Errorf("failed to list localities: %w", err) + } + + sortResponseByZones(res, zones) + return nil + } + + return errors.New("%T does not support pagination", res) +} + +// doListRegions collects all regions using multiple list requests and aggregate all results on a single response. +// result is sorted by region +func (c *Client) doListRegions(req *ScalewayRequest, res interface{}, regions []Region) (err error) { + if isLister(res) { + // Prepare request with %locality% that can be replaced with actual region + for _, region := range AllRegions { + if strings.Contains(req.Path, string(region)) { + req.Path = strings.ReplaceAll(req.Path, string(region), "%locality%") + break + } + } + if !strings.Contains(req.Path, "%locality%") { + return fmt.Errorf("request is not a valid zoned request") + } + localities := make([]string, 0, len(regions)) + for _, region := range regions { + localities = append(localities, string(region)) + } + + err := c.doListLocalities(req, res, localities) + if err != nil { + return fmt.Errorf("failed to list localities: %w", err) + } + + sortResponseByRegions(res, regions) + return nil + } + + return errors.New("%T does not support pagination", res) +} + +// sortSliceByZones sorts a slice of struct using a Zone field that should exist +func sortSliceByZones(list interface{}, zones []Zone) { + if !generic.HasField(list, "Zone") { + return + } + + zoneMap := map[Zone]int{} + for i, zone := range zones { + zoneMap[zone] = i + } + generic.SortSliceByField(list, "Zone", func(i interface{}, i2 interface{}) bool { + return zoneMap[i.(Zone)] < zoneMap[i2.(Zone)] + }) +} + +// sortSliceByRegions sorts a slice of struct using a Region field that should exist +func sortSliceByRegions(list interface{}, regions []Region) { + if !generic.HasField(list, "Region") { + return + } + + regionMap := map[Region]int{} + for i, region := range regions { + regionMap[region] = i + } + generic.SortSliceByField(list, "Region", func(i interface{}, i2 interface{}) bool { + return regionMap[i.(Region)] < regionMap[i2.(Region)] + }) +} + +// sortResponseByZones find first field that is a slice in a struct and sort it by zone +func sortResponseByZones(res interface{}, zones []Zone) { + // res may be ListServersResponse + // + // type ListServersResponse struct { + // TotalCount uint32 `json:"total_count"` + // Servers []*Server `json:"servers"` + // } + // We iterate over fields searching for the slice one to sort it + resType := reflect.TypeOf(res).Elem() + fields := reflect.VisibleFields(resType) + for _, field := range fields { + if field.Type.Kind() == reflect.Slice { + sortSliceByZones(reflect.ValueOf(res).Elem().FieldByName(field.Name).Interface(), zones) + return + } + } +} + +// sortResponseByRegions find first field that is a slice in a struct and sort it by region +func sortResponseByRegions(res interface{}, regions []Region) { + // res may be ListServersResponse + // + // type ListServersResponse struct { + // TotalCount uint32 `json:"total_count"` + // Servers []*Server `json:"servers"` + // } + // We iterate over fields searching for the slice one to sort it + resType := reflect.TypeOf(res).Elem() + fields := reflect.VisibleFields(resType) + for _, field := range fields { + if field.Type.Kind() == reflect.Slice { + sortSliceByRegions(reflect.ValueOf(res).Elem().FieldByName(field.Name).Interface(), regions) + return + } + } +} + +// newVariableFromType returns a variable set to the zero value of the given type +func newVariableFromType(t interface{}) interface{} { + // reflect.New always create a pointer, that's why we use reflect.Indirect before + return reflect.New(reflect.Indirect(reflect.ValueOf(t)).Type()).Interface() +} + +func newHTTPClient() *http.Client { + return &http.Client{ + Timeout: 30 * time.Second, + Transport: http.DefaultTransport.(*http.Transport).Clone(), + } +} + +func setInsecureMode(c httpClient) { + standardHTTPClient, ok := c.(*http.Client) + if !ok { + logger.Warningf("client: cannot use insecure mode with HTTP client of type %T", c) + return + } + transportClient, ok := standardHTTPClient.Transport.(*http.Transport) + if !ok { + logger.Warningf("client: cannot use insecure mode with Transport client of type %T", standardHTTPClient.Transport) + return + } + if transportClient.TLSClientConfig == nil { + transportClient.TLSClientConfig = &tls.Config{} + } + transportClient.TLSClientConfig.InsecureSkipVerify = true +} + +func setRequestLogging(c httpClient) { + standardHTTPClient, ok := c.(*http.Client) + if !ok { + logger.Warningf("client: cannot use request logger with HTTP client of type %T", c) + return + } + // Do not wrap transport if it is already a logger + // As client is a pointer, changing transport will change given client + // If the same httpClient is used in multiple scwClient, it would add multiple logger transports + _, isLogger := standardHTTPClient.Transport.(*requestLoggerTransport) + if !isLogger { + standardHTTPClient.Transport = &requestLoggerTransport{rt: standardHTTPClient.Transport} + } +} diff --git a/vendor/github.com/scaleway/scaleway-sdk-go/scw/client_option.go b/vendor/github.com/scaleway/scaleway-sdk-go/scw/client_option.go new file mode 100644 index 000000000..482259c61 --- /dev/null +++ b/vendor/github.com/scaleway/scaleway-sdk-go/scw/client_option.go @@ -0,0 +1,276 @@ +package scw + +import ( + "net/http" + "strings" + + "github.com/scaleway/scaleway-sdk-go/internal/auth" + "github.com/scaleway/scaleway-sdk-go/internal/errors" + "github.com/scaleway/scaleway-sdk-go/validation" +) + +// ClientOption is a function which applies options to a settings object. +type ClientOption func(*settings) + +// httpClient wraps the net/http Client Do method +type httpClient interface { + Do(*http.Request) (*http.Response, error) +} + +// WithHTTPClient client option allows passing a custom http.Client which will be used for all requests. +func WithHTTPClient(httpClient httpClient) ClientOption { + return func(s *settings) { + s.httpClient = httpClient + } +} + +// WithoutAuth client option sets the client token to an empty token. +func WithoutAuth() ClientOption { + return func(s *settings) { + s.token = auth.NewNoAuth() + } +} + +// WithAuth client option sets the client access key and secret key. +func WithAuth(accessKey, secretKey string) ClientOption { + return func(s *settings) { + s.token = auth.NewToken(accessKey, secretKey) + } +} + +// WithJWT client option sets the client session token. +func WithJWT(token string) ClientOption { + return func(s *settings) { + s.token = auth.NewJWT(token) + } +} + +// WithAPIURL client option overrides the API URL of the Scaleway API to the given URL. +func WithAPIURL(apiURL string) ClientOption { + return func(s *settings) { + s.apiURL = apiURL + } +} + +// WithInsecure client option enables insecure transport on the client. +func WithInsecure() ClientOption { + return func(s *settings) { + s.insecure = true + } +} + +// WithUserAgent client option append a user agent to the default user agent of the SDK. +func WithUserAgent(ua string) ClientOption { + return func(s *settings) { + if s.userAgent != "" && ua != "" { + s.userAgent += " " + } + s.userAgent += ua + } +} + +// withDefaultUserAgent client option overrides the default user agent of the SDK. +func withDefaultUserAgent(ua string) ClientOption { + return func(s *settings) { + s.userAgent = ua + } +} + +// WithProfile client option configures a client from the given profile. +func WithProfile(p *Profile) ClientOption { + return func(s *settings) { + accessKey := "" + if p.AccessKey != nil { + accessKey = *p.AccessKey + s.token = auth.NewAccessKeyOnly(accessKey) + } + + if p.SecretKey != nil { + s.token = auth.NewToken(accessKey, *p.SecretKey) + } + + if p.APIURL != nil { + s.apiURL = *p.APIURL + } + + if p.Insecure != nil { + s.insecure = *p.Insecure + } + + if p.DefaultOrganizationID != nil { + organizationID := *p.DefaultOrganizationID + s.defaultOrganizationID = &organizationID + } + + if p.DefaultProjectID != nil { + projectID := *p.DefaultProjectID + s.defaultProjectID = &projectID + } + + if p.DefaultRegion != nil { + defaultRegion := Region(*p.DefaultRegion) + s.defaultRegion = &defaultRegion + } + + if p.DefaultZone != nil { + defaultZone := Zone(*p.DefaultZone) + s.defaultZone = &defaultZone + } + } +} + +// WithEnv client option configures a client from the environment variables. +func WithEnv() ClientOption { + return WithProfile(LoadEnvProfile()) +} + +// WithDefaultOrganizationID client option sets the client default organization ID. +// +// It will be used as the default value of the organization_id field in all requests made with this client. +func WithDefaultOrganizationID(organizationID string) ClientOption { + return func(s *settings) { + s.defaultOrganizationID = &organizationID + } +} + +// WithDefaultProjectID client option sets the client default project ID. +// +// It will be used as the default value of the projectID field in all requests made with this client. +func WithDefaultProjectID(projectID string) ClientOption { + return func(s *settings) { + s.defaultProjectID = &projectID + } +} + +// WithDefaultRegion client option sets the client default region. +// +// It will be used as the default value of the region field in all requests made with this client. +func WithDefaultRegion(region Region) ClientOption { + return func(s *settings) { + s.defaultRegion = ®ion + } +} + +// WithDefaultZone client option sets the client default zone. +// +// It will be used as the default value of the zone field in all requests made with this client. +func WithDefaultZone(zone Zone) ClientOption { + return func(s *settings) { + s.defaultZone = &zone + } +} + +// WithDefaultPageSize client option overrides the default page size of the SDK. +// +// It will be used as the default value of the page_size field in all requests made with this client. +func WithDefaultPageSize(pageSize uint32) ClientOption { + return func(s *settings) { + s.defaultPageSize = &pageSize + } +} + +// settings hold the values of all client options +type settings struct { + apiURL string + token auth.Auth + userAgent string + httpClient httpClient + insecure bool + defaultOrganizationID *string + defaultProjectID *string + defaultRegion *Region + defaultZone *Zone + defaultPageSize *uint32 +} + +func newSettings() *settings { + return &settings{} +} + +func (s *settings) apply(opts []ClientOption) { + for _, opt := range opts { + opt(s) + } +} + +func (s *settings) validate() error { + // Auth. + if s.token == nil { + // It should not happen, WithoutAuth option is used by default. + panic(errors.New("no credential option provided")) + } + if token, isToken := s.token.(*auth.Token); isToken { + if token.AccessKey == "" { + return NewInvalidClientOptionError("access key cannot be empty") + } + if !validation.IsAccessKey(token.AccessKey) { + return NewInvalidClientOptionError("invalid access key format '%s', expected SCWXXXXXXXXXXXXXXXXX format", token.AccessKey) + } + if token.SecretKey == "" { + return NewInvalidClientOptionError("secret key cannot be empty") + } + if !validation.IsSecretKey(token.SecretKey) { + return NewInvalidClientOptionError("invalid secret key format '%s', expected a UUID: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", token.SecretKey) + } + } + + // Default Organization ID. + if s.defaultOrganizationID != nil { + if *s.defaultOrganizationID == "" { + return NewInvalidClientOptionError("default organization ID cannot be empty") + } + if !validation.IsOrganizationID(*s.defaultOrganizationID) { + return NewInvalidClientOptionError("invalid organization ID format '%s', expected a UUID: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", *s.defaultOrganizationID) + } + } + + // Default Project ID. + if s.defaultProjectID != nil { + if *s.defaultProjectID == "" { + return NewInvalidClientOptionError("default project ID cannot be empty") + } + if !validation.IsProjectID(*s.defaultProjectID) { + return NewInvalidClientOptionError("invalid project ID format '%s', expected a UUID: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", *s.defaultProjectID) + } + } + + // Default Region. + if s.defaultRegion != nil { + if *s.defaultRegion == "" { + return NewInvalidClientOptionError("default region cannot be empty") + } + if !validation.IsRegion(string(*s.defaultRegion)) { + regions := []string(nil) + for _, r := range AllRegions { + regions = append(regions, string(r)) + } + return NewInvalidClientOptionError("invalid default region format '%s', available regions are: %s", *s.defaultRegion, strings.Join(regions, ", ")) + } + } + + // Default Zone. + if s.defaultZone != nil { + if *s.defaultZone == "" { + return NewInvalidClientOptionError("default zone cannot be empty") + } + if !validation.IsZone(string(*s.defaultZone)) { + zones := []string(nil) + for _, z := range AllZones { + zones = append(zones, string(z)) + } + return NewInvalidClientOptionError("invalid default zone format '%s', available zones are: %s", *s.defaultZone, strings.Join(zones, ", ")) + } + } + + // API URL. + if !validation.IsURL(s.apiURL) { + return NewInvalidClientOptionError("invalid url %s", s.apiURL) + } + if s.apiURL[len(s.apiURL)-1:] == "/" { + return NewInvalidClientOptionError("invalid url %s it should not have a trailing slash", s.apiURL) + } + + // TODO: check for max s.defaultPageSize + + return nil +} diff --git a/vendor/github.com/scaleway/scaleway-sdk-go/scw/config.go b/vendor/github.com/scaleway/scaleway-sdk-go/scw/config.go new file mode 100644 index 000000000..71d79d5f5 --- /dev/null +++ b/vendor/github.com/scaleway/scaleway-sdk-go/scw/config.go @@ -0,0 +1,361 @@ +package scw + +import ( + "bytes" + goerrors "errors" + "io/ioutil" + "os" + "path/filepath" + "strings" + "text/template" + + "gopkg.in/yaml.v2" + + "github.com/scaleway/scaleway-sdk-go/internal/auth" + "github.com/scaleway/scaleway-sdk-go/internal/errors" + "github.com/scaleway/scaleway-sdk-go/logger" +) + +const ( + documentationLink = "https://github.com/scaleway/scaleway-sdk-go/blob/master/scw/README.md" + defaultConfigPermission = 0600 + + // Reserved name for the default profile. + DefaultProfileName = "default" +) + +const configFileTemplate = `# Scaleway configuration file +# https://github.com/scaleway/scaleway-sdk-go/tree/master/scw#scaleway-config + +# This configuration file can be used with: +# - Scaleway SDK Go (https://github.com/scaleway/scaleway-sdk-go) +# - Scaleway CLI (>2.0.0) (https://github.com/scaleway/scaleway-cli) +# - Scaleway Terraform Provider (https://www.terraform.io/docs/providers/scaleway/index.html) + +# You need an access key and a secret key to connect to Scaleway API. +# Generate your token at the following address: https://console.scaleway.com/iam/api-keys + +# An access key is a secret key identifier. +{{ if .AccessKey }}access_key: {{.AccessKey}}{{ else }}# access_key: SCW11111111111111111{{ end }} + +# The secret key is the value that can be used to authenticate against the API (the value used in X-Auth-Token HTTP-header). +# The secret key MUST remain secret and not given to anyone or published online. +{{ if .SecretKey }}secret_key: {{ .SecretKey }}{{ else }}# secret_key: 11111111-1111-1111-1111-111111111111{{ end }} + +# Your organization ID is the identifier of your account inside Scaleway infrastructure. +{{ if .DefaultOrganizationID }}default_organization_id: {{ .DefaultOrganizationID }}{{ else }}# default_organization_id: 11111111-1111-1111-1111-111111111111{{ end }} + +# Your project ID is the identifier of the project your resources are attached to (beta). +{{ if .DefaultProjectID }}default_project_id: {{ .DefaultProjectID }}{{ else }}# default_project_id: 11111111-1111-1111-1111-111111111111{{ end }} + +# A region is represented as a geographical area such as France (Paris) or the Netherlands (Amsterdam). +# It can contain multiple availability zones. +# Example of region: fr-par, nl-ams +{{ if .DefaultRegion }}default_region: {{ .DefaultRegion }}{{ else }}# default_region: fr-par{{ end }} + +# A region can be split into many availability zones (AZ). +# Latency between multiple AZ of the same region are low as they have a common network layer. +# Example of zones: fr-par-1, nl-ams-1 +{{ if .DefaultZone }}default_zone: {{.DefaultZone}}{{ else }}# default_zone: fr-par-1{{ end }} + +# APIURL overrides the API URL of the Scaleway API to the given URL. +# Change that if you want to direct requests to a different endpoint. +{{ if .APIURL }}apiurl: {{ .APIURL }}{{ else }}# api_url: https://api.scaleway.com{{ end }} + +# Insecure enables insecure transport on the client. +# Default to false +{{ if .Insecure }}insecure: {{ .Insecure }}{{ else }}# insecure: false{{ end }} + +# A configuration is a named set of Scaleway properties. +# Starting off with a Scaleway SDK or Scaleway CLI, you’ll work with a single configuration named default. +# You can set properties of the default profile by running either scw init or scw config set. +# This single default configuration is suitable for most use cases. +{{ if .ActiveProfile }}active_profile: {{ .ActiveProfile }}{{ else }}# active_profile: myProfile{{ end }} + +# To improve the Scaleway CLI we rely on diagnostic and usage data. +# Sending such data is optional and can be disable at any time by setting send_telemetry variable to false. +{{ if .SendTelemetry }}send_telemetry: {{ .SendTelemetry }}{{ else }}# send_telemetry: false{{ end }} + +# To work with multiple projects or authorization accounts, you can set up multiple configurations with scw config configurations create and switch among them accordingly. +# You can use a profile by either: +# - Define the profile you want to use as the SCW_PROFILE environment variable +# - Use the GetActiveProfile() function in the SDK +# - Use the --profile flag with the CLI + +# You can define a profile using the following syntax: +{{ if gt (len .Profiles) 0 }} +profiles: +{{- range $k,$v := .Profiles }} + {{ $k }}: + {{ if $v.AccessKey }}access_key: {{ $v.AccessKey }}{{ else }}# access_key: SCW11111111111111111{{ end }} + {{ if $v.SecretKey }}secret_key: {{ $v.SecretKey }}{{ else }}# secret_key: 11111111-1111-1111-1111-111111111111{{ end }} + {{ if $v.DefaultOrganizationID }}default_organization_id: {{ $v.DefaultOrganizationID }}{{ else }}# default_organization_id: 11111111-1111-1111-1111-111111111111{{ end }} + {{ if $v.DefaultProjectID }}default_project_id: {{ $v.DefaultProjectID }}{{ else }}# default_project_id: 11111111-1111-1111-1111-111111111111{{ end }} + {{ if $v.DefaultZone }}default_zone: {{ $v.DefaultZone }}{{ else }}# default_zone: fr-par-1{{ end }} + {{ if $v.DefaultRegion }}default_region: {{ $v.DefaultRegion }}{{ else }}# default_region: fr-par{{ end }} + {{ if $v.APIURL }}api_url: {{ $v.APIURL }}{{ else }}# api_url: https://api.scaleway.com{{ end }} + {{ if $v.Insecure }}insecure: {{ $v.Insecure }}{{ else }}# insecure: false{{ end }} +{{ end }} +{{- else }} +# profiles: +# myProfile: +# access_key: 11111111-1111-1111-1111-111111111111 +# secret_key: 11111111-1111-1111-1111-111111111111 +# default_organization_id: 11111111-1111-1111-1111-111111111111 +# default_project_id: 11111111-1111-1111-1111-111111111111 +# default_zone: fr-par-1 +# default_region: fr-par +# api_url: https://api.scaleway.com +# insecure: false +{{ end -}} +` + +type Config struct { + Profile `yaml:",inline"` + ActiveProfile *string `yaml:"active_profile,omitempty" json:"active_profile,omitempty"` + Profiles map[string]*Profile `yaml:"profiles,omitempty" json:"profiles,omitempty"` +} + +type Profile struct { + AccessKey *string `yaml:"access_key,omitempty" json:"access_key,omitempty"` + SecretKey *string `yaml:"secret_key,omitempty" json:"secret_key,omitempty"` + APIURL *string `yaml:"api_url,omitempty" json:"api_url,omitempty"` + Insecure *bool `yaml:"insecure,omitempty" json:"insecure,omitempty"` + DefaultOrganizationID *string `yaml:"default_organization_id,omitempty" json:"default_organization_id,omitempty"` + DefaultProjectID *string `yaml:"default_project_id,omitempty" json:"default_project_id,omitempty"` + DefaultRegion *string `yaml:"default_region,omitempty" json:"default_region,omitempty"` + DefaultZone *string `yaml:"default_zone,omitempty" json:"default_zone,omitempty"` + SendTelemetry *bool `yaml:"send_telemetry,omitempty" json:"send_telemetry,omitempty"` +} + +func (p *Profile) String() string { + p2 := *p + p2.SecretKey = hideSecretKey(p2.SecretKey) + configRaw, _ := yaml.Marshal(p2) + return string(configRaw) +} + +// clone deep copy config object +func (c *Config) clone() *Config { + c2 := &Config{} + configRaw, _ := yaml.Marshal(c) + _ = yaml.Unmarshal(configRaw, c2) + return c2 +} + +func (c *Config) String() string { + c2 := c.clone() + c2.SecretKey = hideSecretKey(c2.SecretKey) + for _, p := range c2.Profiles { + p.SecretKey = hideSecretKey(p.SecretKey) + } + + configRaw, _ := yaml.Marshal(c2) + return string(configRaw) +} + +func (c *Config) IsEmpty() bool { + return c.String() == "{}\n" +} + +func hideSecretKey(key *string) *string { + if key == nil { + return nil + } + + newKey := auth.HideSecretKey(*key) + return &newKey +} + +func unmarshalConfV2(content []byte) (*Config, error) { + var config Config + + err := yaml.Unmarshal(content, &config) + if err != nil { + return nil, err + } + return &config, nil +} + +// MustLoadConfig is like LoadConfig but panic instead of returning an error. +func MustLoadConfig() *Config { + c, err := LoadConfigFromPath(GetConfigPath()) + if err != nil { + panic(err) + } + return c +} + +// LoadConfig read the config from the default path. +func LoadConfig() (*Config, error) { + configPath := GetConfigPath() + cfg, err := LoadConfigFromPath(configPath) + + // Special case if using default config path + // if config.yaml does not exist, we should try to read config.yml + if os.Getenv(ScwConfigPathEnv) == "" { + var configNotFoundError *ConfigFileNotFoundError + if err != nil && goerrors.As(err, &configNotFoundError) && strings.HasSuffix(configPath, ".yaml") { + configPath = strings.TrimSuffix(configPath, ".yaml") + ".yml" + cfgYml, errYml := LoadConfigFromPath(configPath) + // If .yml config is not found, return first error when reading .yaml + if errYml == nil || (errYml != nil && !goerrors.As(errYml, &configNotFoundError)) { + return cfgYml, errYml + } + } + } + + return cfg, err +} + +// LoadConfigFromPath read the config from the given path. +func LoadConfigFromPath(path string) (*Config, error) { + _, err := os.Stat(path) + if os.IsNotExist(err) { + return nil, configFileNotFound(path) + } + if err != nil { + return nil, err + } + + file, err := ioutil.ReadFile(path) + if err != nil { + return nil, errors.Wrap(err, "cannot read config file") + } + + confV2, err := unmarshalConfV2(file) + if err != nil { + return nil, errors.Wrap(err, "content of config file %s is invalid", path) + } + + return confV2, nil +} + +// GetProfile returns the profile corresponding to the given profile name. +func (c *Config) GetProfile(profileName string) (*Profile, error) { + if profileName == "" { + return nil, errors.New("profileName cannot be empty") + } + + if profileName == DefaultProfileName { + return &c.Profile, nil + } + + p, exist := c.Profiles[profileName] + if !exist { + return nil, errors.New("given profile %s does not exist", profileName) + } + + // Merge selected profile on top of default profile + return MergeProfiles(&c.Profile, p), nil +} + +// GetActiveProfile returns the active profile of the config based on the following order: +// env SCW_PROFILE > config active_profile > config root profile +func (c *Config) GetActiveProfile() (*Profile, error) { + switch { + case os.Getenv(ScwActiveProfileEnv) != "": + logger.Debugf("using active profile from env: %s=%s", ScwActiveProfileEnv, os.Getenv(ScwActiveProfileEnv)) + return c.GetProfile(os.Getenv(ScwActiveProfileEnv)) + case c.ActiveProfile != nil: + logger.Debugf("using active profile from config: active_profile=%s", ScwActiveProfileEnv, *c.ActiveProfile) + return c.GetProfile(*c.ActiveProfile) + default: + return &c.Profile, nil + } +} + +// SaveTo will save the config to the default config path. This +// action will overwrite the previous file when it exists. +func (c *Config) Save() error { + return c.SaveTo(GetConfigPath()) +} + +// HumanConfig will generate a config file with documented arguments. +func (c *Config) HumanConfig() (string, error) { + tmpl, err := template.New("configuration").Parse(configFileTemplate) + if err != nil { + return "", err + } + + var buf bytes.Buffer + err = tmpl.Execute(&buf, c) + if err != nil { + return "", err + } + + return buf.String(), nil +} + +// SaveTo will save the config to the given path. This action will +// overwrite the previous file when it exists. +func (c *Config) SaveTo(path string) error { + path = filepath.Clean(path) + + // STEP 1: Render the configuration file as a file + file, err := c.HumanConfig() + if err != nil { + return err + } + + // STEP 2: create config path dir in cases it didn't exist before + err = os.MkdirAll(filepath.Dir(path), 0700) + if err != nil { + return err + } + + // STEP 3: write new config file + err = ioutil.WriteFile(path, []byte(file), defaultConfigPermission) + if err != nil { + return err + } + + return nil +} + +// MergeProfiles merges profiles in a new one. The last profile has priority. +func MergeProfiles(original *Profile, others ...*Profile) *Profile { + np := &Profile{ + AccessKey: original.AccessKey, + SecretKey: original.SecretKey, + APIURL: original.APIURL, + Insecure: original.Insecure, + DefaultOrganizationID: original.DefaultOrganizationID, + DefaultProjectID: original.DefaultProjectID, + DefaultRegion: original.DefaultRegion, + DefaultZone: original.DefaultZone, + SendTelemetry: original.SendTelemetry, + } + + for _, other := range others { + if other.AccessKey != nil { + np.AccessKey = other.AccessKey + } + if other.SecretKey != nil { + np.SecretKey = other.SecretKey + } + if other.APIURL != nil { + np.APIURL = other.APIURL + } + if other.Insecure != nil { + np.Insecure = other.Insecure + } + if other.DefaultOrganizationID != nil { + np.DefaultOrganizationID = other.DefaultOrganizationID + } + if other.DefaultProjectID != nil { + np.DefaultProjectID = other.DefaultProjectID + } + if other.DefaultRegion != nil { + np.DefaultRegion = other.DefaultRegion + } + if other.DefaultZone != nil { + np.DefaultZone = other.DefaultZone + } + if other.SendTelemetry != nil { + np.SendTelemetry = other.SendTelemetry + } + } + + return np +} diff --git a/vendor/github.com/scaleway/scaleway-sdk-go/scw/convert.go b/vendor/github.com/scaleway/scaleway-sdk-go/scw/convert.go new file mode 100644 index 000000000..e48c21fa7 --- /dev/null +++ b/vendor/github.com/scaleway/scaleway-sdk-go/scw/convert.go @@ -0,0 +1,176 @@ +package scw + +import ( + "net" + "time" +) + +// StringPtr returns a pointer to the string value passed in. +func StringPtr(v string) *string { + return &v +} + +// StringSlicePtr converts a slice of string values into a slice of +// string pointers +func StringSlicePtr(src []string) []*string { + dst := make([]*string, len(src)) + for i := 0; i < len(src); i++ { + dst[i] = &(src[i]) + } + return dst +} + +// StringsPtr returns a pointer to the []string value passed in. +func StringsPtr(v []string) *[]string { + return &v +} + +// StringsSlicePtr converts a slice of []string values into a slice of +// []string pointers +func StringsSlicePtr(src [][]string) []*[]string { + dst := make([]*[]string, len(src)) + for i := 0; i < len(src); i++ { + dst[i] = &(src[i]) + } + return dst +} + +// BytesPtr returns a pointer to the []byte value passed in. +func BytesPtr(v []byte) *[]byte { + return &v +} + +// BytesSlicePtr converts a slice of []byte values into a slice of +// []byte pointers +func BytesSlicePtr(src [][]byte) []*[]byte { + dst := make([]*[]byte, len(src)) + for i := 0; i < len(src); i++ { + dst[i] = &(src[i]) + } + return dst +} + +// BoolPtr returns a pointer to the bool value passed in. +func BoolPtr(v bool) *bool { + return &v +} + +// BoolSlicePtr converts a slice of bool values into a slice of +// bool pointers +func BoolSlicePtr(src []bool) []*bool { + dst := make([]*bool, len(src)) + for i := 0; i < len(src); i++ { + dst[i] = &(src[i]) + } + return dst +} + +// Int32Ptr returns a pointer to the int32 value passed in. +func Int32Ptr(v int32) *int32 { + return &v +} + +// Int32SlicePtr converts a slice of int32 values into a slice of +// int32 pointers +func Int32SlicePtr(src []int32) []*int32 { + dst := make([]*int32, len(src)) + for i := 0; i < len(src); i++ { + dst[i] = &(src[i]) + } + return dst +} + +// Int64Ptr returns a pointer to the int64 value passed in. +func Int64Ptr(v int64) *int64 { + return &v +} + +// Int64SlicePtr converts a slice of int64 values into a slice of +// int64 pointers +func Int64SlicePtr(src []int64) []*int64 { + dst := make([]*int64, len(src)) + for i := 0; i < len(src); i++ { + dst[i] = &(src[i]) + } + return dst +} + +// Uint32Ptr returns a pointer to the uint32 value passed in. +func Uint32Ptr(v uint32) *uint32 { + return &v +} + +// Uint32SlicePtr converts a slice of uint32 values into a slice of +// uint32 pointers +func Uint32SlicePtr(src []uint32) []*uint32 { + dst := make([]*uint32, len(src)) + for i := 0; i < len(src); i++ { + dst[i] = &(src[i]) + } + return dst +} + +// Uint64Ptr returns a pointer to the uint64 value passed in. +func Uint64Ptr(v uint64) *uint64 { + return &v +} + +// Uint64SlicePtr converts a slice of uint64 values into a slice of +// uint64 pointers +func Uint64SlicePtr(src []uint64) []*uint64 { + dst := make([]*uint64, len(src)) + for i := 0; i < len(src); i++ { + dst[i] = &(src[i]) + } + return dst +} + +// Float32Ptr returns a pointer to the float32 value passed in. +func Float32Ptr(v float32) *float32 { + return &v +} + +// Float32SlicePtr converts a slice of float32 values into a slice of +// float32 pointers +func Float32SlicePtr(src []float32) []*float32 { + dst := make([]*float32, len(src)) + for i := 0; i < len(src); i++ { + dst[i] = &(src[i]) + } + return dst +} + +// Float64Ptr returns a pointer to the float64 value passed in. +func Float64Ptr(v float64) *float64 { + return &v +} + +// Float64SlicePtr converts a slice of float64 values into a slice of +// float64 pointers +func Float64SlicePtr(src []float64) []*float64 { + dst := make([]*float64, len(src)) + for i := 0; i < len(src); i++ { + dst[i] = &(src[i]) + } + return dst +} + +// TimeDurationPtr returns a pointer to the Duration value passed in. +func TimeDurationPtr(v time.Duration) *time.Duration { + return &v +} + +// TimePtr returns a pointer to the Time value passed in. +func TimePtr(v time.Time) *time.Time { + return &v +} + +// SizePtr returns a pointer to the Size value passed in. +func SizePtr(v Size) *Size { + return &v +} + +// IPPtr returns a pointer to the net.IP value passed in. +func IPPtr(v net.IP) *net.IP { + return &v +} diff --git a/vendor/github.com/scaleway/scaleway-sdk-go/scw/custom_types.go b/vendor/github.com/scaleway/scaleway-sdk-go/scw/custom_types.go new file mode 100644 index 000000000..673a598eb --- /dev/null +++ b/vendor/github.com/scaleway/scaleway-sdk-go/scw/custom_types.go @@ -0,0 +1,450 @@ +package scw + +import ( + "bytes" + "encoding/base64" + "encoding/json" + "fmt" + "io" + "net" + "strconv" + "strings" + "time" + + "github.com/scaleway/scaleway-sdk-go/internal/errors" + "github.com/scaleway/scaleway-sdk-go/logger" +) + +// ServiceInfo contains API metadata +// These metadata are only here for debugging. Do not rely on these values +type ServiceInfo struct { + // Name is the name of the API + Name string `json:"name"` + + // Description is a human readable description for the API + Description string `json:"description"` + + // Version is the version of the API + Version string `json:"version"` + + // DocumentationURL is the a web url where the documentation of the API can be found + DocumentationURL *string `json:"documentation_url"` +} + +// File is the structure used to receive / send a file from / to the API +type File struct { + // Name of the file + Name string `json:"name"` + + // ContentType used in the HTTP header `Content-Type` + ContentType string `json:"content_type"` + + // Content of the file + Content io.Reader `json:"content"` +} + +func (f *File) MarshalJSON() ([]byte, error) { + buf := new(bytes.Buffer) + if f.Content != nil { + _, err := io.Copy(buf, f.Content) + if err != nil { + return nil, err + } + } + + tmpFile := struct { + Name string `json:"name"` + ContentType string `json:"content_type"` + Content string `json:"content"` + }{ + Name: f.Name, + ContentType: f.ContentType, + Content: buf.String(), + } + + return json.Marshal(tmpFile) +} + +func (f *File) UnmarshalJSON(b []byte) error { + type file File + var tmpFile struct { + file + Content []byte `json:"content"` + } + + err := json.Unmarshal(b, &tmpFile) + if err != nil { + return err + } + + tmpFile.file.Content = bytes.NewReader(tmpFile.Content) + + *f = File(tmpFile.file) + return nil +} + +// Money represents an amount of money with its currency type. +type Money struct { + // CurrencyCode is the 3-letter currency code defined in ISO 4217. + CurrencyCode string `json:"currency_code"` + + // Units is the whole units of the amount. + // For example if `currencyCode` is `"USD"`, then 1 unit is one US dollar. + Units int64 `json:"units"` + + // Nanos is the number of nano (10^-9) units of the amount. + // The value must be between -999,999,999 and +999,999,999 inclusive. + // If `units` is positive, `nanos` must be positive or zero. + // If `units` is zero, `nanos` can be positive, zero, or negative. + // If `units` is negative, `nanos` must be negative or zero. + // For example $-1.75 is represented as `units`=-1 and `nanos`=-750,000,000. + Nanos int32 `json:"nanos"` +} + +// NewMoneyFromFloat converts a float with currency to a Money. +// +// value: The float value. +// currencyCode: The 3-letter currency code defined in ISO 4217. +// precision: The number of digits after the decimal point used to parse the nanos part of the value. +// +// Examples: +// - (value = 1.3333, precision = 2) => Money{Units = 1, Nanos = 330000000} +// - (value = 1.123456789, precision = 9) => Money{Units = 1, Nanos = 123456789} +func NewMoneyFromFloat(value float64, currencyCode string, precision int) *Money { + if precision > 9 { + panic(fmt.Errorf("max precision is 9")) + } + + strValue := strconv.FormatFloat(value, 'f', precision, 64) + units, nanos, err := splitFloatString(strValue) + if err != nil { + panic(err) + } + + return &Money{ + CurrencyCode: currencyCode, + Units: units, + Nanos: nanos, + } +} + +// String returns the string representation of Money. +func (m Money) String() string { + currencySignsByCodes := map[string]string{ + "EUR": "€", + "USD": "$", + } + + currencySign, currencySignFound := currencySignsByCodes[m.CurrencyCode] + if !currencySignFound { + logger.Debugf("%s currency code is not supported", m.CurrencyCode) + currencySign = m.CurrencyCode + } + + cents := fmt.Sprintf("%09d", m.Nanos) + cents = cents[:2] + strings.TrimRight(cents[2:], "0") + + return fmt.Sprintf("%s %d.%s", currencySign, m.Units, cents) +} + +// ToFloat converts a Money object to a float. +func (m Money) ToFloat() float64 { + return float64(m.Units) + float64(m.Nanos)/1e9 +} + +// Size represents a size in bytes. +type Size uint64 + +const ( + B Size = 1 + KB = 1000 * B + MB = 1000 * KB + GB = 1000 * MB + TB = 1000 * GB + PB = 1000 * TB +) + +// String returns the string representation of a Size. +func (s Size) String() string { + return fmt.Sprintf("%d", s) +} + +// TimeSeries represents a time series that could be used for graph purposes. +type TimeSeries struct { + // Name of the metric. + Name string `json:"name"` + + // Points contains all the points that composed the series. + Points []*TimeSeriesPoint `json:"points"` + + // Metadata contains some string metadata related to a metric. + Metadata map[string]string `json:"metadata"` +} + +// TimeSeriesPoint represents a point of a time series. +type TimeSeriesPoint struct { + Timestamp time.Time + Value float32 +} + +func (tsp TimeSeriesPoint) MarshalJSON() ([]byte, error) { + timestamp := tsp.Timestamp.Format(time.RFC3339) + value, err := json.Marshal(tsp.Value) + if err != nil { + return nil, err + } + + return []byte(`["` + timestamp + `",` + string(value) + "]"), nil +} + +func (tsp *TimeSeriesPoint) UnmarshalJSON(b []byte) error { + point := [2]interface{}{} + + err := json.Unmarshal(b, &point) + if err != nil { + return err + } + + if len(point) != 2 { + return fmt.Errorf("invalid point array") + } + + strTimestamp, isStrTimestamp := point[0].(string) + if !isStrTimestamp { + return fmt.Errorf("%s timestamp is not a string in RFC 3339 format", point[0]) + } + timestamp, err := time.Parse(time.RFC3339, strTimestamp) + if err != nil { + return fmt.Errorf("%s timestamp is not in RFC 3339 format", point[0]) + } + tsp.Timestamp = timestamp + + // By default, JSON unmarshal a float in float64 but the TimeSeriesPoint is a float32 value. + value, isValue := point[1].(float64) + if !isValue { + return fmt.Errorf("%s is not a valid float32 value", point[1]) + } + tsp.Value = float32(value) + + return nil +} + +// IPNet inherits net.IPNet and represents an IP network. +type IPNet struct { + net.IPNet +} + +func (n IPNet) MarshalJSON() ([]byte, error) { + value := n.String() + if value == "" { + value = "" + } + return []byte(`"` + value + `"`), nil +} + +func (n *IPNet) UnmarshalJSON(b []byte) error { + var str string + + err := json.Unmarshal(b, &str) + if err != nil { + return err + } + if str == "" { + *n = IPNet{} + return nil + } + + switch ip := net.ParseIP(str); { + case ip.To4() != nil: + str += "/32" + case ip.To16() != nil: + str += "/128" + } + + ip, value, err := net.ParseCIDR(str) + if err != nil { + return err + } + value.IP = ip + n.IPNet = *value + + return nil +} + +// Duration represents a signed, fixed-length span of time represented as a +// count of seconds and fractions of seconds at nanosecond resolution. It is +// independent of any calendar and concepts like "day" or "month". It is related +// to Timestamp in that the difference between two Timestamp values is a Duration +// and it can be added or subtracted from a Timestamp. +// Range is approximately +-10,000 years. +type Duration struct { + Seconds int64 + Nanos int32 +} + +func (d *Duration) ToTimeDuration() *time.Duration { + if d == nil { + return nil + } + timeDuration := time.Duration(d.Nanos) + time.Duration(d.Seconds)*time.Second + return &timeDuration +} + +func (d Duration) MarshalJSON() ([]byte, error) { + nanos := d.Nanos + if nanos < 0 { + nanos = -nanos + } + + return []byte(`"` + fmt.Sprintf("%d.%09d", d.Seconds, nanos) + `s"`), nil +} + +func (d *Duration) UnmarshalJSON(b []byte) error { + if string(b) == "null" { + return nil + } + var str string + + err := json.Unmarshal(b, &str) + if err != nil { + return err + } + if str == "" { + *d = Duration{} + return nil + } + + seconds, nanos, err := splitFloatString(strings.TrimRight(str, "s")) + if err != nil { + return err + } + + *d = Duration{ + Seconds: seconds, + Nanos: nanos, + } + + return nil +} + +func NewDurationFromTimeDuration(t time.Duration) *Duration { + duration := Duration{ + Seconds: int64(t.Seconds()), + } + duration.Nanos = int32(t.Nanoseconds() - (time.Duration(duration.Seconds) * time.Second).Nanoseconds()) + + return &duration +} + +// splitFloatString splits a float represented in a string, and returns its units (left-coma part) and nanos (right-coma part). +// E.g.: +// "3" ==> units = 3 | nanos = 0 +// "3.14" ==> units = 3 | nanos = 14*1e7 +// "-3.14" ==> units = -3 | nanos = -14*1e7 +func splitFloatString(input string) (units int64, nanos int32, err error) { + parts := strings.SplitN(input, ".", 2) + + // parse units as int64 + units, err = strconv.ParseInt(parts[0], 10, 64) + if err != nil { + return 0, 0, errors.Wrap(err, "invalid units") + } + + // handle nanos + if len(parts) == 2 { + // add leading zeros + strNanos := parts[1] + "000000000"[len(parts[1]):] + + // parse nanos as int32 + n, err := strconv.ParseUint(strNanos, 10, 32) + if err != nil { + return 0, 0, errors.Wrap(err, "invalid nanos") + } + + nanos = int32(n) + } + + if units < 0 { + nanos = -nanos + } + + return units, nanos, nil +} + +// JSONObject represent any JSON object. See struct.proto. +// It will be marshaled into a json string. +// This type can be used just like any other map. +// +// Example: +// +// values := scw.JSONValue{ +// "Foo": "Bar", +// } +// values["Baz"] = "Qux" +type JSONObject map[string]interface{} + +// EscapeMode is the mode that should be use for escaping a value +type EscapeMode uint + +// The modes for escaping a value before it is marshaled, and unmarshalled. +const ( + NoEscape EscapeMode = iota + Base64Escape + QuotedEscape +) + +// DecodeJSONObject will attempt to decode the string input as a JSONValue. +// Optionally decoding base64 the value first before JSON unmarshalling. +// +// Will panic if the escape mode is unknown. +func DecodeJSONObject(v string, escape EscapeMode) (JSONObject, error) { + var b []byte + var err error + + switch escape { + case NoEscape: + b = []byte(v) + case Base64Escape: + b, err = base64.StdEncoding.DecodeString(v) + case QuotedEscape: + var u string + u, err = strconv.Unquote(v) + b = []byte(u) + default: + panic(fmt.Sprintf("DecodeJSONObject called with unknown EscapeMode, %v", escape)) + } + + if err != nil { + return nil, err + } + + m := JSONObject{} + err = json.Unmarshal(b, &m) + if err != nil { + return nil, err + } + + return m, nil +} + +// EncodeJSONObject marshals the value into a JSON string, and optionally base64 +// encodes the string before returning it. +// +// Will panic if the escape mode is unknown. +func EncodeJSONObject(v JSONObject, escape EscapeMode) (string, error) { + b, err := json.Marshal(v) + if err != nil { + return "", err + } + + switch escape { + case NoEscape: + return string(b), nil + case Base64Escape: + return base64.StdEncoding.EncodeToString(b), nil + case QuotedEscape: + return strconv.Quote(string(b)), nil + } + + panic(fmt.Sprintf("EncodeJSONObject called with unknown EscapeMode, %v", escape)) +} diff --git a/vendor/github.com/scaleway/scaleway-sdk-go/scw/env.go b/vendor/github.com/scaleway/scaleway-sdk-go/scw/env.go new file mode 100644 index 000000000..27e5e685a --- /dev/null +++ b/vendor/github.com/scaleway/scaleway-sdk-go/scw/env.go @@ -0,0 +1,145 @@ +package scw + +import ( + "os" + "strconv" + + "github.com/scaleway/scaleway-sdk-go/logger" +) + +// Environment variables +const ( + // Up-to-date + ScwCacheDirEnv = "SCW_CACHE_DIR" + ScwConfigPathEnv = "SCW_CONFIG_PATH" + ScwAccessKeyEnv = "SCW_ACCESS_KEY" + ScwSecretKeyEnv = "SCW_SECRET_KEY" // #nosec G101 + ScwActiveProfileEnv = "SCW_PROFILE" + ScwAPIURLEnv = "SCW_API_URL" + ScwInsecureEnv = "SCW_INSECURE" + ScwDefaultOrganizationIDEnv = "SCW_DEFAULT_ORGANIZATION_ID" + ScwDefaultProjectIDEnv = "SCW_DEFAULT_PROJECT_ID" + ScwDefaultRegionEnv = "SCW_DEFAULT_REGION" + ScwDefaultZoneEnv = "SCW_DEFAULT_ZONE" + ScwEnableBeta = "SCW_ENABLE_BETA" + DebugEnv = logger.DebugEnv + + // All deprecated (cli&terraform) + terraformAccessKeyEnv = "SCALEWAY_ACCESS_KEY" // used both as access key and secret key + terraformSecretKeyEnv = "SCALEWAY_TOKEN" + terraformOrganizationEnv = "SCALEWAY_ORGANIZATION" + terraformRegionEnv = "SCALEWAY_REGION" + cliTLSVerifyEnv = "SCW_TLSVERIFY" + cliOrganizationEnv = "SCW_ORGANIZATION" + cliRegionEnv = "SCW_REGION" + cliSecretKeyEnv = "SCW_TOKEN" + + // TBD + //cliVerboseEnv = "SCW_VERBOSE_API" + //cliDebugEnv = "DEBUG" + //cliNoCheckVersionEnv = "SCW_NOCHECKVERSION" + //cliTestWithRealAPIEnv = "TEST_WITH_REAL_API" + //cliSecureExecEnv = "SCW_SECURE_EXEC" + //cliGatewayEnv = "SCW_GATEWAY" + //cliSensitiveEnv = "SCW_SENSITIVE" + //cliAccountAPIEnv = "SCW_ACCOUNT_API" + //cliMetadataAPIEnv = "SCW_METADATA_API" + //cliMarketPlaceAPIEnv = "SCW_MARKETPLACE_API" + //cliComputePar1APIEnv = "SCW_COMPUTE_PAR1_API" + //cliComputeAms1APIEnv = "SCW_COMPUTE_AMS1_API" + //cliCommercialTypeEnv = "SCW_COMMERCIAL_TYPE" + //cliTargetArchEnv = "SCW_TARGET_ARCH" +) + +const ( + v1RegionFrPar = "par1" + v1RegionNlAms = "ams1" +) + +func LoadEnvProfile() *Profile { + p := &Profile{} + + accessKey, _, envExist := getEnv(ScwAccessKeyEnv, terraformAccessKeyEnv) + if envExist { + p.AccessKey = &accessKey + } + + secretKey, _, envExist := getEnv(ScwSecretKeyEnv, cliSecretKeyEnv, terraformSecretKeyEnv, terraformAccessKeyEnv) + if envExist { + p.SecretKey = &secretKey + } + + apiURL, _, envExist := getEnv(ScwAPIURLEnv) + if envExist { + p.APIURL = &apiURL + } + + insecureValue, envKey, envExist := getEnv(ScwInsecureEnv, cliTLSVerifyEnv) + if envExist { + insecure, err := strconv.ParseBool(insecureValue) + if err != nil { + logger.Warningf("env variable %s cannot be parsed: %s is invalid boolean", envKey, insecureValue) + } + + if envKey == cliTLSVerifyEnv { + insecure = !insecure // TLSVerify is the inverse of Insecure + } + + p.Insecure = &insecure + } + + organizationID, _, envExist := getEnv(ScwDefaultOrganizationIDEnv, cliOrganizationEnv, terraformOrganizationEnv) + if envExist { + p.DefaultOrganizationID = &organizationID + } + + projectID, _, envExist := getEnv(ScwDefaultProjectIDEnv) + if envExist { + p.DefaultProjectID = &projectID + } + + region, _, envExist := getEnv(ScwDefaultRegionEnv, cliRegionEnv, terraformRegionEnv) + if envExist { + region = v1RegionToV2(region) + p.DefaultRegion = ®ion + } + + zone, _, envExist := getEnv(ScwDefaultZoneEnv) + if envExist { + p.DefaultZone = &zone + } + + return p +} + +func getEnv(upToDateKey string, deprecatedKeys ...string) (string, string, bool) { + value, exist := os.LookupEnv(upToDateKey) + if exist { + logger.Debugf("reading value from %s", upToDateKey) + return value, upToDateKey, true + } + + for _, key := range deprecatedKeys { + value, exist := os.LookupEnv(key) + if exist { + logger.Debugf("reading value from %s", key) + logger.Warningf("%s is deprecated, please use %s instead", key, upToDateKey) + return value, key, true + } + } + + return "", "", false +} + +func v1RegionToV2(region string) string { + switch region { + case v1RegionFrPar: + logger.Warningf("par1 is a deprecated name for region, use fr-par instead") + return "fr-par" + case v1RegionNlAms: + logger.Warningf("ams1 is a deprecated name for region, use nl-ams instead") + return "nl-ams" + default: + return region + } +} diff --git a/vendor/github.com/scaleway/scaleway-sdk-go/scw/errors.go b/vendor/github.com/scaleway/scaleway-sdk-go/scw/errors.go new file mode 100644 index 000000000..3563b0de9 --- /dev/null +++ b/vendor/github.com/scaleway/scaleway-sdk-go/scw/errors.go @@ -0,0 +1,552 @@ +package scw + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + "sort" + "strings" + "time" + + "github.com/scaleway/scaleway-sdk-go/internal/errors" + "github.com/scaleway/scaleway-sdk-go/validation" +) + +// SdkError is a base interface for all Scaleway SDK errors. +type SdkError interface { + Error() string + IsScwSdkError() +} + +// ResponseError is an error type for the Scaleway API +type ResponseError struct { + // Message is a human-friendly error message + Message string `json:"message"` + + // Type is a string code that defines the kind of error. This field is only used by instance API + Type string `json:"type,omitempty"` + + // Resource is a string code that defines the resource concerned by the error. This field is only used by instance API + Resource string `json:"resource,omitempty"` + + // Fields contains detail about validation error. This field is only used by instance API + Fields map[string][]string `json:"fields,omitempty"` + + // StatusCode is the HTTP status code received + StatusCode int `json:"-"` + + // Status is the HTTP status received + Status string `json:"-"` + + RawBody json.RawMessage `json:"-"` +} + +func (e *ResponseError) UnmarshalJSON(b []byte) error { + type tmpResponseError ResponseError + tmp := tmpResponseError(*e) + + err := json.Unmarshal(b, &tmp) + if err != nil { + return err + } + + tmp.Message = strings.ToLower(tmp.Message) + + *e = ResponseError(tmp) + return nil +} + +// IsScwSdkError implement SdkError interface +func (e *ResponseError) IsScwSdkError() {} +func (e *ResponseError) Error() string { + s := fmt.Sprintf("scaleway-sdk-go: http error %s", e.Status) + + if e.Resource != "" { + s = fmt.Sprintf("%s: resource %s", s, e.Resource) + } + + if e.Message != "" { + s = fmt.Sprintf("%s: %s", s, e.Message) + } + + if len(e.Fields) > 0 { + s = fmt.Sprintf("%s: %v", s, e.Fields) + } + + return s +} +func (e *ResponseError) GetRawBody() json.RawMessage { + return e.RawBody +} + +// hasResponseError returns an SdkError when the HTTP status is not OK. +func hasResponseError(res *http.Response) error { + if res.StatusCode >= 200 && res.StatusCode <= 299 { + return nil + } + + newErr := &ResponseError{ + StatusCode: res.StatusCode, + Status: res.Status, + } + + if res.Body == nil { + return newErr + } + + body, err := ioutil.ReadAll(res.Body) + if err != nil { + return errors.Wrap(err, "cannot read error response body") + } + newErr.RawBody = body + + // The error content is not encoded in JSON, only returns HTTP data. + contentType := res.Header.Get("Content-Type") + if !strings.HasPrefix(contentType, "application/json") { + newErr.Message = res.Status + return newErr + } + + err = json.Unmarshal(body, newErr) + if err != nil { + return errors.Wrap(err, "could not parse error response body") + } + + err = unmarshalStandardError(newErr.Type, body) + if err != nil { + return err + } + + err = unmarshalNonStandardError(newErr.Type, body) + if err != nil { + return err + } + + return newErr +} + +func unmarshalStandardError(errorType string, body []byte) error { + var stdErr SdkError + + switch errorType { + case "invalid_arguments": + stdErr = &InvalidArgumentsError{RawBody: body} + case "quotas_exceeded": + stdErr = &QuotasExceededError{RawBody: body} + case "transient_state": + stdErr = &TransientStateError{RawBody: body} + case "not_found": + stdErr = &ResourceNotFoundError{RawBody: body} + case "locked": + stdErr = &ResourceLockedError{RawBody: body} + case "permissions_denied": + stdErr = &PermissionsDeniedError{RawBody: body} + case "out_of_stock": + stdErr = &OutOfStockError{RawBody: body} + case "resource_expired": + stdErr = &ResourceExpiredError{RawBody: body} + case "denied_authentication": + stdErr = &DeniedAuthenticationError{RawBody: body} + case "precondition_failed": + stdErr = &PreconditionFailedError{RawBody: body} + default: + return nil + } + + err := json.Unmarshal(body, stdErr) + if err != nil { + return errors.Wrap(err, "could not parse error %s response body", errorType) + } + + return stdErr +} + +func unmarshalNonStandardError(errorType string, body []byte) error { + switch errorType { + // Only in instance API. + + case "unknown_resource": + unknownResourceError := &UnknownResource{RawBody: body} + err := json.Unmarshal(body, unknownResourceError) + if err != nil { + return errors.Wrap(err, "could not parse error %s response body", errorType) + } + return unknownResourceError.ToResourceNotFoundError() + + case "invalid_request_error": + invalidRequestError := &InvalidRequestError{RawBody: body} + err := json.Unmarshal(body, invalidRequestError) + if err != nil { + return errors.Wrap(err, "could not parse error %s response body", errorType) + } + + invalidArgumentsError := invalidRequestError.ToInvalidArgumentsError() + if invalidArgumentsError != nil { + return invalidArgumentsError + } + + quotasExceededError := invalidRequestError.ToQuotasExceededError() + if quotasExceededError != nil { + return quotasExceededError + } + + // At this point, the invalid_request_error is not an InvalidArgumentsError and + // the default marshalling will be used. + return nil + + default: + return nil + } +} + +type InvalidArgumentsErrorDetail struct { + ArgumentName string `json:"argument_name"` + Reason string `json:"reason"` + HelpMessage string `json:"help_message"` +} + +type InvalidArgumentsError struct { + Details []InvalidArgumentsErrorDetail `json:"details"` + + RawBody json.RawMessage `json:"-"` +} + +// IsScwSdkError implements the SdkError interface +func (e *InvalidArgumentsError) IsScwSdkError() {} +func (e *InvalidArgumentsError) Error() string { + invalidArgs := make([]string, len(e.Details)) + for i, d := range e.Details { + invalidArgs[i] = d.ArgumentName + switch d.Reason { + case "unknown": + invalidArgs[i] += " is invalid for unexpected reason" + case "required": + invalidArgs[i] += " is required" + case "format": + invalidArgs[i] += " is wrongly formatted" + case "constraint": + invalidArgs[i] += " does not respect constraint" + } + if d.HelpMessage != "" { + invalidArgs[i] += ", " + d.HelpMessage + } + } + + return "scaleway-sdk-go: invalid argument(s): " + strings.Join(invalidArgs, "; ") +} +func (e *InvalidArgumentsError) GetRawBody() json.RawMessage { + return e.RawBody +} + +// UnknownResource is only returned by the instance API. +// Warning: this is not a standard error. +type UnknownResource struct { + Message string `json:"message"` + RawBody json.RawMessage `json:"-"` +} + +// ToSdkError returns a standard error InvalidArgumentsError or nil Fields is nil. +func (e *UnknownResource) ToResourceNotFoundError() SdkError { + resourceNotFound := &ResourceNotFoundError{ + RawBody: e.RawBody, + } + + messageParts := strings.Split(e.Message, `"`) + + // Some errors uses ' and not " + if len(messageParts) == 1 { + messageParts = strings.Split(e.Message, "'") + } + + switch len(messageParts) { + case 2: // message like: `"111..." not found` + resourceNotFound.ResourceID = messageParts[0] + case 3: // message like: `Security Group "111..." not found` + resourceNotFound.ResourceID = messageParts[1] + // transform `Security group ` to `security_group` + resourceNotFound.Resource = strings.ReplaceAll(strings.ToLower(strings.TrimSpace(messageParts[0])), " ", "_") + default: + return nil + } + if !validation.IsUUID(resourceNotFound.ResourceID) { + return nil + } + return resourceNotFound +} + +// InvalidRequestError is only returned by the instance API. +// Warning: this is not a standard error. +type InvalidRequestError struct { + Message string `json:"message"` + + Fields map[string][]string `json:"fields"` + + Resource string `json:"resource"` + + RawBody json.RawMessage `json:"-"` +} + +// ToSdkError returns a standard error InvalidArgumentsError or nil Fields is nil. +func (e *InvalidRequestError) ToInvalidArgumentsError() SdkError { + // If error has no fields, it is not an InvalidArgumentsError. + if e.Fields == nil || len(e.Fields) == 0 { + return nil + } + + invalidArguments := &InvalidArgumentsError{ + RawBody: e.RawBody, + } + fieldNames := []string(nil) + for fieldName := range e.Fields { + fieldNames = append(fieldNames, fieldName) + } + sort.Strings(fieldNames) + for _, fieldName := range fieldNames { + for _, message := range e.Fields[fieldName] { + invalidArguments.Details = append(invalidArguments.Details, InvalidArgumentsErrorDetail{ + ArgumentName: fieldName, + Reason: "constraint", + HelpMessage: message, + }) + } + } + return invalidArguments +} + +func (e *InvalidRequestError) ToQuotasExceededError() SdkError { + if !strings.Contains(strings.ToLower(e.Message), "quota exceeded for this resource") { + return nil + } + + return &QuotasExceededError{ + Details: []QuotasExceededErrorDetail{ + { + Resource: e.Resource, + Quota: 0, + Current: 0, + }, + }, + RawBody: e.RawBody, + } +} + +type QuotasExceededErrorDetail struct { + Resource string `json:"resource"` + Quota uint32 `json:"quota"` + Current uint32 `json:"current"` +} + +type QuotasExceededError struct { + Details []QuotasExceededErrorDetail `json:"details"` + RawBody json.RawMessage `json:"-"` +} + +// IsScwSdkError implements the SdkError interface +func (e *QuotasExceededError) IsScwSdkError() {} +func (e *QuotasExceededError) Error() string { + invalidArgs := make([]string, len(e.Details)) + for i, d := range e.Details { + invalidArgs[i] = fmt.Sprintf("%s has reached its quota (%d/%d)", d.Resource, d.Current, d.Quota) + } + + return "scaleway-sdk-go: quota exceeded(s): " + strings.Join(invalidArgs, "; ") +} +func (e *QuotasExceededError) GetRawBody() json.RawMessage { + return e.RawBody +} + +type PermissionsDeniedError struct { + Details []struct { + Resource string `json:"resource"` + Action string `json:"action"` + } `json:"details"` + + RawBody json.RawMessage `json:"-"` +} + +// IsScwSdkError implements the SdkError interface +func (e *PermissionsDeniedError) IsScwSdkError() {} +func (e *PermissionsDeniedError) Error() string { + invalidArgs := make([]string, len(e.Details)) + for i, d := range e.Details { + invalidArgs[i] = fmt.Sprintf("%s %s", d.Action, d.Resource) + } + + return "scaleway-sdk-go: insufficient permissions: " + strings.Join(invalidArgs, "; ") +} +func (e *PermissionsDeniedError) GetRawBody() json.RawMessage { + return e.RawBody +} + +type TransientStateError struct { + Resource string `json:"resource"` + ResourceID string `json:"resource_id"` + CurrentState string `json:"current_state"` + + RawBody json.RawMessage `json:"-"` +} + +// IsScwSdkError implements the SdkError interface +func (e *TransientStateError) IsScwSdkError() {} +func (e *TransientStateError) Error() string { + return fmt.Sprintf("scaleway-sdk-go: resource %s with ID %s is in a transient state: %s", e.Resource, e.ResourceID, e.CurrentState) +} +func (e *TransientStateError) GetRawBody() json.RawMessage { + return e.RawBody +} + +type ResourceNotFoundError struct { + Resource string `json:"resource"` + ResourceID string `json:"resource_id"` + + RawBody json.RawMessage `json:"-"` +} + +// IsScwSdkError implements the SdkError interface +func (e *ResourceNotFoundError) IsScwSdkError() {} +func (e *ResourceNotFoundError) Error() string { + return fmt.Sprintf("scaleway-sdk-go: resource %s with ID %s is not found", e.Resource, e.ResourceID) +} +func (e *ResourceNotFoundError) GetRawBody() json.RawMessage { + return e.RawBody +} + +type ResourceLockedError struct { + Resource string `json:"resource"` + ResourceID string `json:"resource_id"` + + RawBody json.RawMessage `json:"-"` +} + +// IsScwSdkError implements the SdkError interface +func (e *ResourceLockedError) IsScwSdkError() {} +func (e *ResourceLockedError) Error() string { + return fmt.Sprintf("scaleway-sdk-go: resource %s with ID %s is locked", e.Resource, e.ResourceID) +} +func (e *ResourceLockedError) GetRawBody() json.RawMessage { + return e.RawBody +} + +type OutOfStockError struct { + Resource string `json:"resource"` + + RawBody json.RawMessage `json:"-"` +} + +// IsScwSdkError implements the SdkError interface +func (e *OutOfStockError) IsScwSdkError() {} +func (e *OutOfStockError) Error() string { + return fmt.Sprintf("scaleway-sdk-go: resource %s is out of stock", e.Resource) +} +func (e *OutOfStockError) GetRawBody() json.RawMessage { + return e.RawBody +} + +// InvalidClientOptionError indicates that at least one of client data has been badly provided for the client creation. +type InvalidClientOptionError struct { + errorType string +} + +func NewInvalidClientOptionError(format string, a ...interface{}) *InvalidClientOptionError { + return &InvalidClientOptionError{errorType: fmt.Sprintf(format, a...)} +} + +// IsScwSdkError implements the SdkError interface +func (e InvalidClientOptionError) IsScwSdkError() {} +func (e InvalidClientOptionError) Error() string { + return fmt.Sprintf("scaleway-sdk-go: %s", e.errorType) +} + +// ConfigFileNotFound indicates that the config file could not be found +type ConfigFileNotFoundError struct { + path string +} + +func configFileNotFound(path string) *ConfigFileNotFoundError { + return &ConfigFileNotFoundError{path: path} +} + +// ConfigFileNotFoundError implements the SdkError interface +func (e ConfigFileNotFoundError) IsScwSdkError() {} +func (e ConfigFileNotFoundError) Error() string { + return fmt.Sprintf("scaleway-sdk-go: cannot read config file %s: no such file or directory", e.path) +} + +// ResourceExpiredError implements the SdkError interface +type ResourceExpiredError struct { + Resource string `json:"resource"` + ResourceID string `json:"resource_id"` + ExpiredSince time.Time `json:"expired_since"` + + RawBody json.RawMessage `json:"-"` +} + +func (r ResourceExpiredError) Error() string { + return fmt.Sprintf("scaleway-sdk-go: resource %s with ID %s expired since %s", r.Resource, r.ResourceID, r.ExpiredSince.String()) +} + +func (r ResourceExpiredError) IsScwSdkError() {} + +// DeniedAuthenticationError implements the SdkError interface +type DeniedAuthenticationError struct { + Method string `json:"method"` + Reason string `json:"reason"` + + RawBody json.RawMessage `json:"-"` +} + +func (r DeniedAuthenticationError) Error() string { + var reason string + var method string + + switch r.Method { + case "unknown_method": + method = "unknown method" + case "jwt": + method = "JWT" + case "api_key": + method = "API key" + } + + switch r.Reason { + case "unknown_reason": + reason = "unknown reason" + case "invalid_argument": + reason = "invalid " + method + " format or empty value" + case "not_found": + reason = method + " does not exist" + case "expired": + reason = method + " is expired" + } + return fmt.Sprintf("scaleway-sdk-go: denied authentication: %s", reason) +} + +func (r DeniedAuthenticationError) IsScwSdkError() {} + +// PreconditionFailedError implements the SdkError interface +type PreconditionFailedError struct { + Precondition string `json:"precondition"` + HelpMessage string `json:"help_message"` + + RawBody json.RawMessage `json:"-"` +} + +func (r PreconditionFailedError) Error() string { + var msg string + switch r.Precondition { + case "unknown_precondition": + msg = "unknown precondition" + case "resource_still_in_use": + msg = "resource is still in use" + case "attribute_must_be_set": + msg = "attribute must be set" + } + if r.HelpMessage != "" { + msg += ", " + r.HelpMessage + } + + return fmt.Sprintf("scaleway-sdk-go: precondition failed: %s", msg) +} + +func (r PreconditionFailedError) IsScwSdkError() {} diff --git a/vendor/github.com/scaleway/scaleway-sdk-go/scw/locality.go b/vendor/github.com/scaleway/scaleway-sdk-go/scw/locality.go new file mode 100644 index 000000000..55e0a1543 --- /dev/null +++ b/vendor/github.com/scaleway/scaleway-sdk-go/scw/locality.go @@ -0,0 +1,221 @@ +package scw + +import ( + "encoding/json" + "fmt" + "strings" + + "github.com/scaleway/scaleway-sdk-go/internal/errors" + "github.com/scaleway/scaleway-sdk-go/logger" + "github.com/scaleway/scaleway-sdk-go/validation" +) + +// localityPartsSeparator is the separator used in Zone and Region +const localityPartsSeparator = "-" + +// Zone is an availability zone +type Zone string + +const ( + // ZoneFrPar1 represents the fr-par-1 zone + ZoneFrPar1 = Zone("fr-par-1") + // ZoneFrPar2 represents the fr-par-2 zone + ZoneFrPar2 = Zone("fr-par-2") + // ZoneFrPar3 represents the fr-par-3 zone + ZoneFrPar3 = Zone("fr-par-3") + // ZoneNlAms1 represents the nl-ams-1 zone + ZoneNlAms1 = Zone("nl-ams-1") + // ZoneNlAms2 represents the nl-ams-2 zone + ZoneNlAms2 = Zone("nl-ams-2") + // ZoneNlAms3 represents the nl-ams-3 zone + ZoneNlAms3 = Zone("nl-ams-3") + // ZonePlWaw1 represents the pl-waw-1 zone + ZonePlWaw1 = Zone("pl-waw-1") + // ZonePlWaw2 represents the pl-waw-2 zone + ZonePlWaw2 = Zone("pl-waw-2") + // ZonePlWaw3 represents the pl-waw-3 zone + ZonePlWaw3 = Zone("pl-waw-3") +) + +var ( + // AllZones is an array that list all zones + AllZones = []Zone{ + ZoneFrPar1, + ZoneFrPar2, + ZoneFrPar3, + ZoneNlAms1, + ZoneNlAms2, + ZoneNlAms3, + ZonePlWaw1, + ZonePlWaw2, + ZonePlWaw3, + } +) + +// Exists checks whether a zone exists +func (zone Zone) Exists() bool { + for _, z := range AllZones { + if z == zone { + return true + } + } + return false +} + +// String returns a Zone as a string +func (zone Zone) String() string { + return string(zone) +} + +// Region returns the parent Region for the Zone. +// Manipulates the string directly to allow unlisted zones formatted as xx-yyy-z. +func (zone Zone) Region() (Region, error) { + zoneStr := zone.String() + if !validation.IsZone(zoneStr) { + return "", fmt.Errorf("invalid zone '%v'", zoneStr) + } + zoneParts := strings.Split(zoneStr, localityPartsSeparator) + return Region(strings.Join(zoneParts[:2], localityPartsSeparator)), nil +} + +// Region is a geographical location +type Region string + +const ( + // RegionFrPar represents the fr-par region + RegionFrPar = Region("fr-par") + // RegionNlAms represents the nl-ams region + RegionNlAms = Region("nl-ams") + // RegionPlWaw represents the pl-waw region + RegionPlWaw = Region("pl-waw") +) + +var ( + // AllRegions is an array that list all regions + AllRegions = []Region{ + RegionFrPar, + RegionNlAms, + RegionPlWaw, + } +) + +// Exists checks whether a region exists +func (region Region) Exists() bool { + for _, r := range AllRegions { + if r == region { + return true + } + } + return false +} + +// GetZones is a function that returns the zones for the specified region +func (region Region) GetZones() []Zone { + switch region { + case RegionFrPar: + return []Zone{ZoneFrPar1, ZoneFrPar2, ZoneFrPar3} + case RegionNlAms: + return []Zone{ZoneNlAms1, ZoneNlAms2, ZoneNlAms3} + case RegionPlWaw: + return []Zone{ZonePlWaw1, ZonePlWaw2, ZonePlWaw3} + default: + return []Zone{} + } +} + +// ParseZone parses a string value into a Zone and returns an error if it has a bad format. +func ParseZone(zone string) (Zone, error) { + switch zone { + case "par1": + // would be triggered by API market place + // logger.Warningf("par1 is a deprecated name for zone, use fr-par-1 instead") + return ZoneFrPar1, nil + case "ams1": + // would be triggered by API market place + // logger.Warningf("ams1 is a deprecated name for zone, use nl-ams-1 instead") + return ZoneNlAms1, nil + default: + if !validation.IsZone(zone) { + zones := []string(nil) + for _, z := range AllZones { + zones = append(zones, string(z)) + } + return "", errors.New("bad zone format, available zones are: %s", strings.Join(zones, ", ")) + } + + newZone := Zone(zone) + if !newZone.Exists() { + logger.Infof("%s is an unknown zone\n", newZone) + } + return newZone, nil + } +} + +// UnmarshalJSON implements the Unmarshaler interface for a Zone. +// this to call ParseZone on the string input and return the correct Zone object. +func (zone *Zone) UnmarshalJSON(input []byte) error { + // parse input value as string + var stringValue string + err := json.Unmarshal(input, &stringValue) + if err != nil { + return err + } + + // parse string as Zone + *zone, err = ParseZone(stringValue) + if err != nil { + return err + } + return nil +} + +// ParseRegion parses a string value into a Region and returns an error if it has a bad format. +func ParseRegion(region string) (Region, error) { + switch region { + case "par1": + // would be triggered by API market place + // logger.Warningf("par1 is a deprecated name for region, use fr-par instead") + return RegionFrPar, nil + case "ams1": + // would be triggered by API market place + // logger.Warningf("ams1 is a deprecated name for region, use nl-ams instead") + return RegionNlAms, nil + default: + if !validation.IsRegion(region) { + regions := []string(nil) + for _, r := range AllRegions { + regions = append(regions, string(r)) + } + return "", errors.New("bad region format, available regions are: %s", strings.Join(regions, ", ")) + } + + newRegion := Region(region) + if !newRegion.Exists() { + logger.Infof("%s is an unknown region\n", newRegion) + } + return newRegion, nil + } +} + +// UnmarshalJSON implements the Unmarshaler interface for a Region. +// this to call ParseRegion on the string input and return the correct Region object. +func (region *Region) UnmarshalJSON(input []byte) error { + // parse input value as string + var stringValue string + err := json.Unmarshal(input, &stringValue) + if err != nil { + return err + } + + // parse string as Region + *region, err = ParseRegion(stringValue) + if err != nil { + return err + } + return nil +} + +// String returns a Region as a string +func (region Region) String() string { + return string(region) +} diff --git a/vendor/github.com/scaleway/scaleway-sdk-go/scw/path.go b/vendor/github.com/scaleway/scaleway-sdk-go/scw/path.go new file mode 100644 index 000000000..7bc0a3c3c --- /dev/null +++ b/vendor/github.com/scaleway/scaleway-sdk-go/scw/path.go @@ -0,0 +1,89 @@ +package scw + +import ( + "errors" + "os" + "path/filepath" +) + +const ( + // XDG wiki: https://wiki.archlinux.org/index.php/XDG_Base_Directory + xdgConfigDirEnv = "XDG_CONFIG_HOME" + xdgCacheDirEnv = "XDG_CACHE_HOME" + + unixHomeDirEnv = "HOME" + windowsHomeDirEnv = "USERPROFILE" + + defaultConfigFileName = "config.yaml" +) + +var ( + // ErrNoHomeDir errors when no user directory is found + ErrNoHomeDir = errors.New("user home directory not found") +) + +// GetCacheDirectory returns the default cache directory. +// Cache directory is based on the following priority order: +// - $SCW_CACHE_DIR +// - $XDG_CACHE_HOME/scw +// - $HOME/.cache/scw +// - $USERPROFILE/.cache/scw +func GetCacheDirectory() string { + cacheDir := "" + switch { + case os.Getenv(ScwCacheDirEnv) != "": + cacheDir = os.Getenv(ScwCacheDirEnv) + case os.Getenv(xdgCacheDirEnv) != "": + cacheDir = filepath.Join(os.Getenv(xdgCacheDirEnv), "scw") + case os.Getenv(unixHomeDirEnv) != "": + cacheDir = filepath.Join(os.Getenv(unixHomeDirEnv), ".cache", "scw") + case os.Getenv(windowsHomeDirEnv) != "": + cacheDir = filepath.Join(os.Getenv(windowsHomeDirEnv), ".cache", "scw") + default: + // TODO: fallback on local folder? + } + + // Clean the cache directory path when exiting the function + return filepath.Clean(cacheDir) +} + +// GetConfigPath returns the default path. +// Default path is based on the following priority order: +// - $SCW_CONFIG_PATH +// - $XDG_CONFIG_HOME/scw/config.yaml +// - $HOME/.config/scw/config.yaml +// - $USERPROFILE/.config/scw/config.yaml +func GetConfigPath() string { + configPath := os.Getenv(ScwConfigPathEnv) + if configPath == "" { + configPath, _ = getConfigV2FilePath() + } + return filepath.Clean(configPath) +} + +// getConfigV2FilePath returns the path to the v2 config file +func getConfigV2FilePath() (string, bool) { + configDir, err := GetScwConfigDir() + if err != nil { + return "", false + } + return filepath.Clean(filepath.Join(configDir, defaultConfigFileName)), true +} + +// GetScwConfigDir returns the path to scw config folder +func GetScwConfigDir() (string, error) { + if xdgPath := os.Getenv(xdgConfigDirEnv); xdgPath != "" { + return filepath.Join(xdgPath, "scw"), nil + } + + homeDir, err := os.UserHomeDir() + if err != nil { + return "", err + } + return filepath.Join(homeDir, ".config", "scw"), nil +} + +func fileExist(name string) bool { + _, err := os.Stat(name) + return err == nil +} diff --git a/vendor/github.com/scaleway/scaleway-sdk-go/scw/request.go b/vendor/github.com/scaleway/scaleway-sdk-go/scw/request.go new file mode 100644 index 000000000..a8832fd70 --- /dev/null +++ b/vendor/github.com/scaleway/scaleway-sdk-go/scw/request.go @@ -0,0 +1,99 @@ +package scw + +import ( + "bytes" + "context" + "encoding/json" + "io" + "net/http" + "net/url" + + "github.com/scaleway/scaleway-sdk-go/internal/auth" + "github.com/scaleway/scaleway-sdk-go/internal/errors" +) + +// ScalewayRequest contains all the contents related to performing a request on the Scaleway API. +type ScalewayRequest struct { + Method string + Path string + Headers http.Header + Query url.Values + Body io.Reader + + // request options + ctx context.Context + auth auth.Auth + allPages bool + zones []Zone + regions []Region +} + +// getURL constructs a URL based on the base url and the client. +func (req *ScalewayRequest) getURL(baseURL string) (*url.URL, error) { + url, err := url.Parse(baseURL + req.Path) + if err != nil { + return nil, errors.New("invalid url %s: %s", baseURL+req.Path, err) + } + url.RawQuery = req.Query.Encode() + + return url, nil +} + +// SetBody json marshal the given body and write the json content type +// to the request. It also catches when body is a file. +func (req *ScalewayRequest) SetBody(body interface{}) error { + var contentType string + var content io.Reader + + switch b := body.(type) { + case *File: + contentType = b.ContentType + content = b.Content + case io.Reader: + contentType = "text/plain" + content = b + default: + buf, err := json.Marshal(body) + if err != nil { + return err + } + contentType = "application/json" + content = bytes.NewReader(buf) + } + + if req.Headers == nil { + req.Headers = http.Header{} + } + + req.Headers.Set("Content-Type", contentType) + req.Body = content + + return nil +} + +func (req *ScalewayRequest) apply(opts []RequestOption) { + for _, opt := range opts { + opt(req) + } +} + +func (req *ScalewayRequest) validate() error { + // nothing so far + return nil +} + +func (req *ScalewayRequest) clone() *ScalewayRequest { + clonedReq := &ScalewayRequest{ + Method: req.Method, + Path: req.Path, + Headers: req.Headers.Clone(), + ctx: req.ctx, + auth: req.auth, + allPages: req.allPages, + zones: req.zones, + } + if req.Query != nil { + clonedReq.Query = url.Values(http.Header(req.Query).Clone()) + } + return clonedReq +} diff --git a/vendor/github.com/scaleway/scaleway-sdk-go/scw/request_header.go b/vendor/github.com/scaleway/scaleway-sdk-go/scw/request_header.go new file mode 100644 index 000000000..9a083be2d --- /dev/null +++ b/vendor/github.com/scaleway/scaleway-sdk-go/scw/request_header.go @@ -0,0 +1,32 @@ +//go:build !wasm || !js + +package scw + +import ( + "net/http" + + "github.com/scaleway/scaleway-sdk-go/internal/auth" +) + +// getAllHeaders constructs a http.Header object and aggregates all headers into the object. +func (req *ScalewayRequest) getAllHeaders(token auth.Auth, userAgent string, anonymized bool) http.Header { + var allHeaders http.Header + if anonymized { + allHeaders = token.AnonymizedHeaders() + } else { + allHeaders = token.Headers() + } + + allHeaders.Set("User-Agent", userAgent) + if req.Body != nil { + allHeaders.Set("Content-Type", "application/json") + } + for key, value := range req.Headers { + allHeaders.Del(key) + for _, v := range value { + allHeaders.Add(key, v) + } + } + + return allHeaders +} diff --git a/vendor/github.com/scaleway/scaleway-sdk-go/scw/request_header_wasm.go b/vendor/github.com/scaleway/scaleway-sdk-go/scw/request_header_wasm.go new file mode 100644 index 000000000..662cc7bde --- /dev/null +++ b/vendor/github.com/scaleway/scaleway-sdk-go/scw/request_header_wasm.go @@ -0,0 +1,32 @@ +//go:build wasm && js + +package scw + +import ( + "net/http" + + "github.com/scaleway/scaleway-sdk-go/internal/auth" +) + +// getAllHeaders constructs a http.Header object and aggregates all headers into the object. +func (req *ScalewayRequest) getAllHeaders(token auth.Auth, userAgent string, anonymized bool) http.Header { + var allHeaders http.Header + if anonymized { + allHeaders = token.AnonymizedHeaders() + } else { + allHeaders = token.Headers() + } + + allHeaders.Set("X-User-Agent", userAgent) + if req.Body != nil { + allHeaders.Set("Content-Type", "application/json") + } + for key, value := range req.Headers { + allHeaders.Del(key) + for _, v := range value { + allHeaders.Add(key, v) + } + } + + return allHeaders +} diff --git a/vendor/github.com/scaleway/scaleway-sdk-go/scw/request_option.go b/vendor/github.com/scaleway/scaleway-sdk-go/scw/request_option.go new file mode 100644 index 000000000..9ad8b0f59 --- /dev/null +++ b/vendor/github.com/scaleway/scaleway-sdk-go/scw/request_option.go @@ -0,0 +1,50 @@ +package scw + +import ( + "context" + + "github.com/scaleway/scaleway-sdk-go/internal/auth" +) + +// RequestOption is a function that applies options to a ScalewayRequest. +type RequestOption func(*ScalewayRequest) + +// WithContext request option sets the context of a ScalewayRequest +func WithContext(ctx context.Context) RequestOption { + return func(s *ScalewayRequest) { + s.ctx = ctx + } +} + +// WithAllPages aggregate all pages in the response of a List request. +// Will error when pagination is not supported on the request. +func WithAllPages() RequestOption { + return func(s *ScalewayRequest) { + s.allPages = true + } +} + +// WithAuthRequest overwrites the client access key and secret key used in the request. +func WithAuthRequest(accessKey, secretKey string) RequestOption { + return func(s *ScalewayRequest) { + s.auth = auth.NewToken(accessKey, secretKey) + } +} + +// WithZones aggregate results from requested zones in the response of a List request. +// response rows are sorted by zone using order of given zones +// Will error when pagination is not supported on the request. +func WithZones(zones ...Zone) RequestOption { + return func(s *ScalewayRequest) { + s.zones = append(s.zones, zones...) + } +} + +// WithRegions aggregate results from requested regions in the response of a List request. +// response rows are sorted by region using order of given regions +// Will error when pagination is not supported on the request. +func WithRegions(regions ...Region) RequestOption { + return func(s *ScalewayRequest) { + s.regions = append(s.regions, regions...) + } +} diff --git a/vendor/github.com/scaleway/scaleway-sdk-go/scw/transport.go b/vendor/github.com/scaleway/scaleway-sdk-go/scw/transport.go new file mode 100644 index 000000000..791a37393 --- /dev/null +++ b/vendor/github.com/scaleway/scaleway-sdk-go/scw/transport.go @@ -0,0 +1,62 @@ +package scw + +import ( + "net/http" + "net/http/httputil" + "sync/atomic" + + "github.com/scaleway/scaleway-sdk-go/internal/auth" + "github.com/scaleway/scaleway-sdk-go/logger" +) + +type requestLoggerTransport struct { + rt http.RoundTripper + // requestNumber auto increments on each do(). + // This allows easy distinguishing of concurrently performed requests in log. + requestNumber uint32 +} + +func (l *requestLoggerTransport) RoundTrip(request *http.Request) (*http.Response, error) { + currentRequestNumber := atomic.AddUint32(&l.requestNumber, 1) + // Keep original headers (before anonymization) + originalHeaders := request.Header + + // Get anonymized headers + request.Header = auth.AnonymizeHeaders(request.Header.Clone()) + + dump, err := httputil.DumpRequestOut(request, true) + if err != nil { + logger.Warningf("cannot dump outgoing request: %s", err) + } else { + var logString string + logString += "\n--------------- Scaleway SDK REQUEST %d : ---------------\n" + logString += "%s\n" + logString += "---------------------------------------------------------" + + logger.Debugf(logString, currentRequestNumber, dump) + } + + // Restore original headers before sending the request + request.Header = originalHeaders + response, requestError := l.rt.RoundTrip(request) + if requestError != nil { + _, isSdkError := requestError.(SdkError) + if !isSdkError { + return response, requestError + } + } + + dump, err = httputil.DumpResponse(response, true) + if err != nil { + logger.Warningf("cannot dump ingoing response: %s", err) + } else { + var logString string + logString += "\n--------------- Scaleway SDK RESPONSE %d : ---------------\n" + logString += "%s\n" + logString += "----------------------------------------------------------" + + logger.Debugf(logString, currentRequestNumber, dump) + } + + return response, requestError +} diff --git a/vendor/github.com/scaleway/scaleway-sdk-go/scw/version.go b/vendor/github.com/scaleway/scaleway-sdk-go/scw/version.go new file mode 100644 index 000000000..6d5911942 --- /dev/null +++ b/vendor/github.com/scaleway/scaleway-sdk-go/scw/version.go @@ -0,0 +1,11 @@ +package scw + +import ( + "fmt" + "runtime" +) + +// TODO: versioning process +const version = "v1.0.0-beta.7+dev" + +var userAgent = fmt.Sprintf("scaleway-sdk-go/%s (%s; %s; %s)", version, runtime.Version(), runtime.GOOS, runtime.GOARCH) diff --git a/vendor/github.com/scaleway/scaleway-sdk-go/validation/is.go b/vendor/github.com/scaleway/scaleway-sdk-go/validation/is.go new file mode 100644 index 000000000..a7e52d0bd --- /dev/null +++ b/vendor/github.com/scaleway/scaleway-sdk-go/validation/is.go @@ -0,0 +1,61 @@ +// Package validation provides format validation functions. +package validation + +import ( + "net/url" + "regexp" +) + +var ( + isUUIDRegexp = regexp.MustCompile("^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$") + isRegionRegex = regexp.MustCompile("^[a-z]{2}-[a-z]{3}$") + isZoneRegex = regexp.MustCompile("^[a-z]{2}-[a-z]{3}-[1-9]$") + isAccessKey = regexp.MustCompile("^SCW[A-Z0-9]{17}$") + isEmailRegexp = regexp.MustCompile("^.+@.+$") +) + +// IsUUID returns true if the given string has a valid UUID format. +func IsUUID(s string) bool { + return isUUIDRegexp.MatchString(s) +} + +// IsAccessKey returns true if the given string has a valid Scaleway access key format. +func IsAccessKey(s string) bool { + return isAccessKey.MatchString(s) +} + +// IsSecretKey returns true if the given string has a valid Scaleway secret key format. +func IsSecretKey(s string) bool { + return IsUUID(s) +} + +// IsOrganizationID returns true if the given string has a valid Scaleway organization ID format. +func IsOrganizationID(s string) bool { + return IsUUID(s) +} + +// IsProjectID returns true if the given string has a valid Scaleway project ID format. +func IsProjectID(s string) bool { + return IsUUID(s) +} + +// IsRegion returns true if the given string has a valid region format. +func IsRegion(s string) bool { + return isRegionRegex.MatchString(s) +} + +// IsZone returns true if the given string has a valid zone format. +func IsZone(s string) bool { + return isZoneRegex.MatchString(s) +} + +// IsURL returns true if the given string has a valid URL format. +func IsURL(s string) bool { + _, err := url.Parse(s) + return err == nil +} + +// IsEmail returns true if the given string has an email format. +func IsEmail(v string) bool { + return isEmailRegexp.MatchString(v) +} diff --git a/vendor/gopkg.in/yaml.v2/.travis.yml b/vendor/gopkg.in/yaml.v2/.travis.yml new file mode 100644 index 000000000..7348c50c0 --- /dev/null +++ b/vendor/gopkg.in/yaml.v2/.travis.yml @@ -0,0 +1,17 @@ +language: go + +go: + - "1.4.x" + - "1.5.x" + - "1.6.x" + - "1.7.x" + - "1.8.x" + - "1.9.x" + - "1.10.x" + - "1.11.x" + - "1.12.x" + - "1.13.x" + - "1.14.x" + - "tip" + +go_import_path: gopkg.in/yaml.v2 diff --git a/vendor/gopkg.in/yaml.v2/LICENSE b/vendor/gopkg.in/yaml.v2/LICENSE new file mode 100644 index 000000000..8dada3eda --- /dev/null +++ b/vendor/gopkg.in/yaml.v2/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + 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. diff --git a/vendor/gopkg.in/yaml.v2/LICENSE.libyaml b/vendor/gopkg.in/yaml.v2/LICENSE.libyaml new file mode 100644 index 000000000..8da58fbf6 --- /dev/null +++ b/vendor/gopkg.in/yaml.v2/LICENSE.libyaml @@ -0,0 +1,31 @@ +The following files were ported to Go from C files of libyaml, and thus +are still covered by their original copyright and license: + + apic.go + emitterc.go + parserc.go + readerc.go + scannerc.go + writerc.go + yamlh.go + yamlprivateh.go + +Copyright (c) 2006 Kirill Simonov + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/gopkg.in/yaml.v2/NOTICE b/vendor/gopkg.in/yaml.v2/NOTICE new file mode 100644 index 000000000..866d74a7a --- /dev/null +++ b/vendor/gopkg.in/yaml.v2/NOTICE @@ -0,0 +1,13 @@ +Copyright 2011-2016 Canonical Ltd. + +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. diff --git a/vendor/gopkg.in/yaml.v2/README.md b/vendor/gopkg.in/yaml.v2/README.md new file mode 100644 index 000000000..b50c6e877 --- /dev/null +++ b/vendor/gopkg.in/yaml.v2/README.md @@ -0,0 +1,133 @@ +# YAML support for the Go language + +Introduction +------------ + +The yaml package enables Go programs to comfortably encode and decode YAML +values. It was developed within [Canonical](https://www.canonical.com) as +part of the [juju](https://juju.ubuntu.com) project, and is based on a +pure Go port of the well-known [libyaml](http://pyyaml.org/wiki/LibYAML) +C library to parse and generate YAML data quickly and reliably. + +Compatibility +------------- + +The yaml package supports most of YAML 1.1 and 1.2, including support for +anchors, tags, map merging, etc. Multi-document unmarshalling is not yet +implemented, and base-60 floats from YAML 1.1 are purposefully not +supported since they're a poor design and are gone in YAML 1.2. + +Installation and usage +---------------------- + +The import path for the package is *gopkg.in/yaml.v2*. + +To install it, run: + + go get gopkg.in/yaml.v2 + +API documentation +----------------- + +If opened in a browser, the import path itself leads to the API documentation: + + * [https://gopkg.in/yaml.v2](https://gopkg.in/yaml.v2) + +API stability +------------- + +The package API for yaml v2 will remain stable as described in [gopkg.in](https://gopkg.in). + + +License +------- + +The yaml package is licensed under the Apache License 2.0. Please see the LICENSE file for details. + + +Example +------- + +```Go +package main + +import ( + "fmt" + "log" + + "gopkg.in/yaml.v2" +) + +var data = ` +a: Easy! +b: + c: 2 + d: [3, 4] +` + +// Note: struct fields must be public in order for unmarshal to +// correctly populate the data. +type T struct { + A string + B struct { + RenamedC int `yaml:"c"` + D []int `yaml:",flow"` + } +} + +func main() { + t := T{} + + err := yaml.Unmarshal([]byte(data), &t) + if err != nil { + log.Fatalf("error: %v", err) + } + fmt.Printf("--- t:\n%v\n\n", t) + + d, err := yaml.Marshal(&t) + if err != nil { + log.Fatalf("error: %v", err) + } + fmt.Printf("--- t dump:\n%s\n\n", string(d)) + + m := make(map[interface{}]interface{}) + + err = yaml.Unmarshal([]byte(data), &m) + if err != nil { + log.Fatalf("error: %v", err) + } + fmt.Printf("--- m:\n%v\n\n", m) + + d, err = yaml.Marshal(&m) + if err != nil { + log.Fatalf("error: %v", err) + } + fmt.Printf("--- m dump:\n%s\n\n", string(d)) +} +``` + +This example will generate the following output: + +``` +--- t: +{Easy! {2 [3 4]}} + +--- t dump: +a: Easy! +b: + c: 2 + d: [3, 4] + + +--- m: +map[a:Easy! b:map[c:2 d:[3 4]]] + +--- m dump: +a: Easy! +b: + c: 2 + d: + - 3 + - 4 +``` + diff --git a/vendor/gopkg.in/yaml.v2/apic.go b/vendor/gopkg.in/yaml.v2/apic.go new file mode 100644 index 000000000..acf71402c --- /dev/null +++ b/vendor/gopkg.in/yaml.v2/apic.go @@ -0,0 +1,744 @@ +package yaml + +import ( + "io" +) + +func yaml_insert_token(parser *yaml_parser_t, pos int, token *yaml_token_t) { + //fmt.Println("yaml_insert_token", "pos:", pos, "typ:", token.typ, "head:", parser.tokens_head, "len:", len(parser.tokens)) + + // Check if we can move the queue at the beginning of the buffer. + if parser.tokens_head > 0 && len(parser.tokens) == cap(parser.tokens) { + if parser.tokens_head != len(parser.tokens) { + copy(parser.tokens, parser.tokens[parser.tokens_head:]) + } + parser.tokens = parser.tokens[:len(parser.tokens)-parser.tokens_head] + parser.tokens_head = 0 + } + parser.tokens = append(parser.tokens, *token) + if pos < 0 { + return + } + copy(parser.tokens[parser.tokens_head+pos+1:], parser.tokens[parser.tokens_head+pos:]) + parser.tokens[parser.tokens_head+pos] = *token +} + +// Create a new parser object. +func yaml_parser_initialize(parser *yaml_parser_t) bool { + *parser = yaml_parser_t{ + raw_buffer: make([]byte, 0, input_raw_buffer_size), + buffer: make([]byte, 0, input_buffer_size), + } + return true +} + +// Destroy a parser object. +func yaml_parser_delete(parser *yaml_parser_t) { + *parser = yaml_parser_t{} +} + +// String read handler. +func yaml_string_read_handler(parser *yaml_parser_t, buffer []byte) (n int, err error) { + if parser.input_pos == len(parser.input) { + return 0, io.EOF + } + n = copy(buffer, parser.input[parser.input_pos:]) + parser.input_pos += n + return n, nil +} + +// Reader read handler. +func yaml_reader_read_handler(parser *yaml_parser_t, buffer []byte) (n int, err error) { + return parser.input_reader.Read(buffer) +} + +// Set a string input. +func yaml_parser_set_input_string(parser *yaml_parser_t, input []byte) { + if parser.read_handler != nil { + panic("must set the input source only once") + } + parser.read_handler = yaml_string_read_handler + parser.input = input + parser.input_pos = 0 +} + +// Set a file input. +func yaml_parser_set_input_reader(parser *yaml_parser_t, r io.Reader) { + if parser.read_handler != nil { + panic("must set the input source only once") + } + parser.read_handler = yaml_reader_read_handler + parser.input_reader = r +} + +// Set the source encoding. +func yaml_parser_set_encoding(parser *yaml_parser_t, encoding yaml_encoding_t) { + if parser.encoding != yaml_ANY_ENCODING { + panic("must set the encoding only once") + } + parser.encoding = encoding +} + +var disableLineWrapping = false + +// Create a new emitter object. +func yaml_emitter_initialize(emitter *yaml_emitter_t) { + *emitter = yaml_emitter_t{ + buffer: make([]byte, output_buffer_size), + raw_buffer: make([]byte, 0, output_raw_buffer_size), + states: make([]yaml_emitter_state_t, 0, initial_stack_size), + events: make([]yaml_event_t, 0, initial_queue_size), + } + if disableLineWrapping { + emitter.best_width = -1 + } +} + +// Destroy an emitter object. +func yaml_emitter_delete(emitter *yaml_emitter_t) { + *emitter = yaml_emitter_t{} +} + +// String write handler. +func yaml_string_write_handler(emitter *yaml_emitter_t, buffer []byte) error { + *emitter.output_buffer = append(*emitter.output_buffer, buffer...) + return nil +} + +// yaml_writer_write_handler uses emitter.output_writer to write the +// emitted text. +func yaml_writer_write_handler(emitter *yaml_emitter_t, buffer []byte) error { + _, err := emitter.output_writer.Write(buffer) + return err +} + +// Set a string output. +func yaml_emitter_set_output_string(emitter *yaml_emitter_t, output_buffer *[]byte) { + if emitter.write_handler != nil { + panic("must set the output target only once") + } + emitter.write_handler = yaml_string_write_handler + emitter.output_buffer = output_buffer +} + +// Set a file output. +func yaml_emitter_set_output_writer(emitter *yaml_emitter_t, w io.Writer) { + if emitter.write_handler != nil { + panic("must set the output target only once") + } + emitter.write_handler = yaml_writer_write_handler + emitter.output_writer = w +} + +// Set the output encoding. +func yaml_emitter_set_encoding(emitter *yaml_emitter_t, encoding yaml_encoding_t) { + if emitter.encoding != yaml_ANY_ENCODING { + panic("must set the output encoding only once") + } + emitter.encoding = encoding +} + +// Set the canonical output style. +func yaml_emitter_set_canonical(emitter *yaml_emitter_t, canonical bool) { + emitter.canonical = canonical +} + +//// Set the indentation increment. +func yaml_emitter_set_indent(emitter *yaml_emitter_t, indent int) { + if indent < 2 || indent > 9 { + indent = 2 + } + emitter.best_indent = indent +} + +// Set the preferred line width. +func yaml_emitter_set_width(emitter *yaml_emitter_t, width int) { + if width < 0 { + width = -1 + } + emitter.best_width = width +} + +// Set if unescaped non-ASCII characters are allowed. +func yaml_emitter_set_unicode(emitter *yaml_emitter_t, unicode bool) { + emitter.unicode = unicode +} + +// Set the preferred line break character. +func yaml_emitter_set_break(emitter *yaml_emitter_t, line_break yaml_break_t) { + emitter.line_break = line_break +} + +///* +// * Destroy a token object. +// */ +// +//YAML_DECLARE(void) +//yaml_token_delete(yaml_token_t *token) +//{ +// assert(token); // Non-NULL token object expected. +// +// switch (token.type) +// { +// case YAML_TAG_DIRECTIVE_TOKEN: +// yaml_free(token.data.tag_directive.handle); +// yaml_free(token.data.tag_directive.prefix); +// break; +// +// case YAML_ALIAS_TOKEN: +// yaml_free(token.data.alias.value); +// break; +// +// case YAML_ANCHOR_TOKEN: +// yaml_free(token.data.anchor.value); +// break; +// +// case YAML_TAG_TOKEN: +// yaml_free(token.data.tag.handle); +// yaml_free(token.data.tag.suffix); +// break; +// +// case YAML_SCALAR_TOKEN: +// yaml_free(token.data.scalar.value); +// break; +// +// default: +// break; +// } +// +// memset(token, 0, sizeof(yaml_token_t)); +//} +// +///* +// * Check if a string is a valid UTF-8 sequence. +// * +// * Check 'reader.c' for more details on UTF-8 encoding. +// */ +// +//static int +//yaml_check_utf8(yaml_char_t *start, size_t length) +//{ +// yaml_char_t *end = start+length; +// yaml_char_t *pointer = start; +// +// while (pointer < end) { +// unsigned char octet; +// unsigned int width; +// unsigned int value; +// size_t k; +// +// octet = pointer[0]; +// width = (octet & 0x80) == 0x00 ? 1 : +// (octet & 0xE0) == 0xC0 ? 2 : +// (octet & 0xF0) == 0xE0 ? 3 : +// (octet & 0xF8) == 0xF0 ? 4 : 0; +// value = (octet & 0x80) == 0x00 ? octet & 0x7F : +// (octet & 0xE0) == 0xC0 ? octet & 0x1F : +// (octet & 0xF0) == 0xE0 ? octet & 0x0F : +// (octet & 0xF8) == 0xF0 ? octet & 0x07 : 0; +// if (!width) return 0; +// if (pointer+width > end) return 0; +// for (k = 1; k < width; k ++) { +// octet = pointer[k]; +// if ((octet & 0xC0) != 0x80) return 0; +// value = (value << 6) + (octet & 0x3F); +// } +// if (!((width == 1) || +// (width == 2 && value >= 0x80) || +// (width == 3 && value >= 0x800) || +// (width == 4 && value >= 0x10000))) return 0; +// +// pointer += width; +// } +// +// return 1; +//} +// + +// Create STREAM-START. +func yaml_stream_start_event_initialize(event *yaml_event_t, encoding yaml_encoding_t) { + *event = yaml_event_t{ + typ: yaml_STREAM_START_EVENT, + encoding: encoding, + } +} + +// Create STREAM-END. +func yaml_stream_end_event_initialize(event *yaml_event_t) { + *event = yaml_event_t{ + typ: yaml_STREAM_END_EVENT, + } +} + +// Create DOCUMENT-START. +func yaml_document_start_event_initialize( + event *yaml_event_t, + version_directive *yaml_version_directive_t, + tag_directives []yaml_tag_directive_t, + implicit bool, +) { + *event = yaml_event_t{ + typ: yaml_DOCUMENT_START_EVENT, + version_directive: version_directive, + tag_directives: tag_directives, + implicit: implicit, + } +} + +// Create DOCUMENT-END. +func yaml_document_end_event_initialize(event *yaml_event_t, implicit bool) { + *event = yaml_event_t{ + typ: yaml_DOCUMENT_END_EVENT, + implicit: implicit, + } +} + +///* +// * Create ALIAS. +// */ +// +//YAML_DECLARE(int) +//yaml_alias_event_initialize(event *yaml_event_t, anchor *yaml_char_t) +//{ +// mark yaml_mark_t = { 0, 0, 0 } +// anchor_copy *yaml_char_t = NULL +// +// assert(event) // Non-NULL event object is expected. +// assert(anchor) // Non-NULL anchor is expected. +// +// if (!yaml_check_utf8(anchor, strlen((char *)anchor))) return 0 +// +// anchor_copy = yaml_strdup(anchor) +// if (!anchor_copy) +// return 0 +// +// ALIAS_EVENT_INIT(*event, anchor_copy, mark, mark) +// +// return 1 +//} + +// Create SCALAR. +func yaml_scalar_event_initialize(event *yaml_event_t, anchor, tag, value []byte, plain_implicit, quoted_implicit bool, style yaml_scalar_style_t) bool { + *event = yaml_event_t{ + typ: yaml_SCALAR_EVENT, + anchor: anchor, + tag: tag, + value: value, + implicit: plain_implicit, + quoted_implicit: quoted_implicit, + style: yaml_style_t(style), + } + return true +} + +// Create SEQUENCE-START. +func yaml_sequence_start_event_initialize(event *yaml_event_t, anchor, tag []byte, implicit bool, style yaml_sequence_style_t) bool { + *event = yaml_event_t{ + typ: yaml_SEQUENCE_START_EVENT, + anchor: anchor, + tag: tag, + implicit: implicit, + style: yaml_style_t(style), + } + return true +} + +// Create SEQUENCE-END. +func yaml_sequence_end_event_initialize(event *yaml_event_t) bool { + *event = yaml_event_t{ + typ: yaml_SEQUENCE_END_EVENT, + } + return true +} + +// Create MAPPING-START. +func yaml_mapping_start_event_initialize(event *yaml_event_t, anchor, tag []byte, implicit bool, style yaml_mapping_style_t) { + *event = yaml_event_t{ + typ: yaml_MAPPING_START_EVENT, + anchor: anchor, + tag: tag, + implicit: implicit, + style: yaml_style_t(style), + } +} + +// Create MAPPING-END. +func yaml_mapping_end_event_initialize(event *yaml_event_t) { + *event = yaml_event_t{ + typ: yaml_MAPPING_END_EVENT, + } +} + +// Destroy an event object. +func yaml_event_delete(event *yaml_event_t) { + *event = yaml_event_t{} +} + +///* +// * Create a document object. +// */ +// +//YAML_DECLARE(int) +//yaml_document_initialize(document *yaml_document_t, +// version_directive *yaml_version_directive_t, +// tag_directives_start *yaml_tag_directive_t, +// tag_directives_end *yaml_tag_directive_t, +// start_implicit int, end_implicit int) +//{ +// struct { +// error yaml_error_type_t +// } context +// struct { +// start *yaml_node_t +// end *yaml_node_t +// top *yaml_node_t +// } nodes = { NULL, NULL, NULL } +// version_directive_copy *yaml_version_directive_t = NULL +// struct { +// start *yaml_tag_directive_t +// end *yaml_tag_directive_t +// top *yaml_tag_directive_t +// } tag_directives_copy = { NULL, NULL, NULL } +// value yaml_tag_directive_t = { NULL, NULL } +// mark yaml_mark_t = { 0, 0, 0 } +// +// assert(document) // Non-NULL document object is expected. +// assert((tag_directives_start && tag_directives_end) || +// (tag_directives_start == tag_directives_end)) +// // Valid tag directives are expected. +// +// if (!STACK_INIT(&context, nodes, INITIAL_STACK_SIZE)) goto error +// +// if (version_directive) { +// version_directive_copy = yaml_malloc(sizeof(yaml_version_directive_t)) +// if (!version_directive_copy) goto error +// version_directive_copy.major = version_directive.major +// version_directive_copy.minor = version_directive.minor +// } +// +// if (tag_directives_start != tag_directives_end) { +// tag_directive *yaml_tag_directive_t +// if (!STACK_INIT(&context, tag_directives_copy, INITIAL_STACK_SIZE)) +// goto error +// for (tag_directive = tag_directives_start +// tag_directive != tag_directives_end; tag_directive ++) { +// assert(tag_directive.handle) +// assert(tag_directive.prefix) +// if (!yaml_check_utf8(tag_directive.handle, +// strlen((char *)tag_directive.handle))) +// goto error +// if (!yaml_check_utf8(tag_directive.prefix, +// strlen((char *)tag_directive.prefix))) +// goto error +// value.handle = yaml_strdup(tag_directive.handle) +// value.prefix = yaml_strdup(tag_directive.prefix) +// if (!value.handle || !value.prefix) goto error +// if (!PUSH(&context, tag_directives_copy, value)) +// goto error +// value.handle = NULL +// value.prefix = NULL +// } +// } +// +// DOCUMENT_INIT(*document, nodes.start, nodes.end, version_directive_copy, +// tag_directives_copy.start, tag_directives_copy.top, +// start_implicit, end_implicit, mark, mark) +// +// return 1 +// +//error: +// STACK_DEL(&context, nodes) +// yaml_free(version_directive_copy) +// while (!STACK_EMPTY(&context, tag_directives_copy)) { +// value yaml_tag_directive_t = POP(&context, tag_directives_copy) +// yaml_free(value.handle) +// yaml_free(value.prefix) +// } +// STACK_DEL(&context, tag_directives_copy) +// yaml_free(value.handle) +// yaml_free(value.prefix) +// +// return 0 +//} +// +///* +// * Destroy a document object. +// */ +// +//YAML_DECLARE(void) +//yaml_document_delete(document *yaml_document_t) +//{ +// struct { +// error yaml_error_type_t +// } context +// tag_directive *yaml_tag_directive_t +// +// context.error = YAML_NO_ERROR // Eliminate a compiler warning. +// +// assert(document) // Non-NULL document object is expected. +// +// while (!STACK_EMPTY(&context, document.nodes)) { +// node yaml_node_t = POP(&context, document.nodes) +// yaml_free(node.tag) +// switch (node.type) { +// case YAML_SCALAR_NODE: +// yaml_free(node.data.scalar.value) +// break +// case YAML_SEQUENCE_NODE: +// STACK_DEL(&context, node.data.sequence.items) +// break +// case YAML_MAPPING_NODE: +// STACK_DEL(&context, node.data.mapping.pairs) +// break +// default: +// assert(0) // Should not happen. +// } +// } +// STACK_DEL(&context, document.nodes) +// +// yaml_free(document.version_directive) +// for (tag_directive = document.tag_directives.start +// tag_directive != document.tag_directives.end +// tag_directive++) { +// yaml_free(tag_directive.handle) +// yaml_free(tag_directive.prefix) +// } +// yaml_free(document.tag_directives.start) +// +// memset(document, 0, sizeof(yaml_document_t)) +//} +// +///** +// * Get a document node. +// */ +// +//YAML_DECLARE(yaml_node_t *) +//yaml_document_get_node(document *yaml_document_t, index int) +//{ +// assert(document) // Non-NULL document object is expected. +// +// if (index > 0 && document.nodes.start + index <= document.nodes.top) { +// return document.nodes.start + index - 1 +// } +// return NULL +//} +// +///** +// * Get the root object. +// */ +// +//YAML_DECLARE(yaml_node_t *) +//yaml_document_get_root_node(document *yaml_document_t) +//{ +// assert(document) // Non-NULL document object is expected. +// +// if (document.nodes.top != document.nodes.start) { +// return document.nodes.start +// } +// return NULL +//} +// +///* +// * Add a scalar node to a document. +// */ +// +//YAML_DECLARE(int) +//yaml_document_add_scalar(document *yaml_document_t, +// tag *yaml_char_t, value *yaml_char_t, length int, +// style yaml_scalar_style_t) +//{ +// struct { +// error yaml_error_type_t +// } context +// mark yaml_mark_t = { 0, 0, 0 } +// tag_copy *yaml_char_t = NULL +// value_copy *yaml_char_t = NULL +// node yaml_node_t +// +// assert(document) // Non-NULL document object is expected. +// assert(value) // Non-NULL value is expected. +// +// if (!tag) { +// tag = (yaml_char_t *)YAML_DEFAULT_SCALAR_TAG +// } +// +// if (!yaml_check_utf8(tag, strlen((char *)tag))) goto error +// tag_copy = yaml_strdup(tag) +// if (!tag_copy) goto error +// +// if (length < 0) { +// length = strlen((char *)value) +// } +// +// if (!yaml_check_utf8(value, length)) goto error +// value_copy = yaml_malloc(length+1) +// if (!value_copy) goto error +// memcpy(value_copy, value, length) +// value_copy[length] = '\0' +// +// SCALAR_NODE_INIT(node, tag_copy, value_copy, length, style, mark, mark) +// if (!PUSH(&context, document.nodes, node)) goto error +// +// return document.nodes.top - document.nodes.start +// +//error: +// yaml_free(tag_copy) +// yaml_free(value_copy) +// +// return 0 +//} +// +///* +// * Add a sequence node to a document. +// */ +// +//YAML_DECLARE(int) +//yaml_document_add_sequence(document *yaml_document_t, +// tag *yaml_char_t, style yaml_sequence_style_t) +//{ +// struct { +// error yaml_error_type_t +// } context +// mark yaml_mark_t = { 0, 0, 0 } +// tag_copy *yaml_char_t = NULL +// struct { +// start *yaml_node_item_t +// end *yaml_node_item_t +// top *yaml_node_item_t +// } items = { NULL, NULL, NULL } +// node yaml_node_t +// +// assert(document) // Non-NULL document object is expected. +// +// if (!tag) { +// tag = (yaml_char_t *)YAML_DEFAULT_SEQUENCE_TAG +// } +// +// if (!yaml_check_utf8(tag, strlen((char *)tag))) goto error +// tag_copy = yaml_strdup(tag) +// if (!tag_copy) goto error +// +// if (!STACK_INIT(&context, items, INITIAL_STACK_SIZE)) goto error +// +// SEQUENCE_NODE_INIT(node, tag_copy, items.start, items.end, +// style, mark, mark) +// if (!PUSH(&context, document.nodes, node)) goto error +// +// return document.nodes.top - document.nodes.start +// +//error: +// STACK_DEL(&context, items) +// yaml_free(tag_copy) +// +// return 0 +//} +// +///* +// * Add a mapping node to a document. +// */ +// +//YAML_DECLARE(int) +//yaml_document_add_mapping(document *yaml_document_t, +// tag *yaml_char_t, style yaml_mapping_style_t) +//{ +// struct { +// error yaml_error_type_t +// } context +// mark yaml_mark_t = { 0, 0, 0 } +// tag_copy *yaml_char_t = NULL +// struct { +// start *yaml_node_pair_t +// end *yaml_node_pair_t +// top *yaml_node_pair_t +// } pairs = { NULL, NULL, NULL } +// node yaml_node_t +// +// assert(document) // Non-NULL document object is expected. +// +// if (!tag) { +// tag = (yaml_char_t *)YAML_DEFAULT_MAPPING_TAG +// } +// +// if (!yaml_check_utf8(tag, strlen((char *)tag))) goto error +// tag_copy = yaml_strdup(tag) +// if (!tag_copy) goto error +// +// if (!STACK_INIT(&context, pairs, INITIAL_STACK_SIZE)) goto error +// +// MAPPING_NODE_INIT(node, tag_copy, pairs.start, pairs.end, +// style, mark, mark) +// if (!PUSH(&context, document.nodes, node)) goto error +// +// return document.nodes.top - document.nodes.start +// +//error: +// STACK_DEL(&context, pairs) +// yaml_free(tag_copy) +// +// return 0 +//} +// +///* +// * Append an item to a sequence node. +// */ +// +//YAML_DECLARE(int) +//yaml_document_append_sequence_item(document *yaml_document_t, +// sequence int, item int) +//{ +// struct { +// error yaml_error_type_t +// } context +// +// assert(document) // Non-NULL document is required. +// assert(sequence > 0 +// && document.nodes.start + sequence <= document.nodes.top) +// // Valid sequence id is required. +// assert(document.nodes.start[sequence-1].type == YAML_SEQUENCE_NODE) +// // A sequence node is required. +// assert(item > 0 && document.nodes.start + item <= document.nodes.top) +// // Valid item id is required. +// +// if (!PUSH(&context, +// document.nodes.start[sequence-1].data.sequence.items, item)) +// return 0 +// +// return 1 +//} +// +///* +// * Append a pair of a key and a value to a mapping node. +// */ +// +//YAML_DECLARE(int) +//yaml_document_append_mapping_pair(document *yaml_document_t, +// mapping int, key int, value int) +//{ +// struct { +// error yaml_error_type_t +// } context +// +// pair yaml_node_pair_t +// +// assert(document) // Non-NULL document is required. +// assert(mapping > 0 +// && document.nodes.start + mapping <= document.nodes.top) +// // Valid mapping id is required. +// assert(document.nodes.start[mapping-1].type == YAML_MAPPING_NODE) +// // A mapping node is required. +// assert(key > 0 && document.nodes.start + key <= document.nodes.top) +// // Valid key id is required. +// assert(value > 0 && document.nodes.start + value <= document.nodes.top) +// // Valid value id is required. +// +// pair.key = key +// pair.value = value +// +// if (!PUSH(&context, +// document.nodes.start[mapping-1].data.mapping.pairs, pair)) +// return 0 +// +// return 1 +//} +// +// diff --git a/vendor/gopkg.in/yaml.v2/decode.go b/vendor/gopkg.in/yaml.v2/decode.go new file mode 100644 index 000000000..129bc2a97 --- /dev/null +++ b/vendor/gopkg.in/yaml.v2/decode.go @@ -0,0 +1,815 @@ +package yaml + +import ( + "encoding" + "encoding/base64" + "fmt" + "io" + "math" + "reflect" + "strconv" + "time" +) + +const ( + documentNode = 1 << iota + mappingNode + sequenceNode + scalarNode + aliasNode +) + +type node struct { + kind int + line, column int + tag string + // For an alias node, alias holds the resolved alias. + alias *node + value string + implicit bool + children []*node + anchors map[string]*node +} + +// ---------------------------------------------------------------------------- +// Parser, produces a node tree out of a libyaml event stream. + +type parser struct { + parser yaml_parser_t + event yaml_event_t + doc *node + doneInit bool +} + +func newParser(b []byte) *parser { + p := parser{} + if !yaml_parser_initialize(&p.parser) { + panic("failed to initialize YAML emitter") + } + if len(b) == 0 { + b = []byte{'\n'} + } + yaml_parser_set_input_string(&p.parser, b) + return &p +} + +func newParserFromReader(r io.Reader) *parser { + p := parser{} + if !yaml_parser_initialize(&p.parser) { + panic("failed to initialize YAML emitter") + } + yaml_parser_set_input_reader(&p.parser, r) + return &p +} + +func (p *parser) init() { + if p.doneInit { + return + } + p.expect(yaml_STREAM_START_EVENT) + p.doneInit = true +} + +func (p *parser) destroy() { + if p.event.typ != yaml_NO_EVENT { + yaml_event_delete(&p.event) + } + yaml_parser_delete(&p.parser) +} + +// expect consumes an event from the event stream and +// checks that it's of the expected type. +func (p *parser) expect(e yaml_event_type_t) { + if p.event.typ == yaml_NO_EVENT { + if !yaml_parser_parse(&p.parser, &p.event) { + p.fail() + } + } + if p.event.typ == yaml_STREAM_END_EVENT { + failf("attempted to go past the end of stream; corrupted value?") + } + if p.event.typ != e { + p.parser.problem = fmt.Sprintf("expected %s event but got %s", e, p.event.typ) + p.fail() + } + yaml_event_delete(&p.event) + p.event.typ = yaml_NO_EVENT +} + +// peek peeks at the next event in the event stream, +// puts the results into p.event and returns the event type. +func (p *parser) peek() yaml_event_type_t { + if p.event.typ != yaml_NO_EVENT { + return p.event.typ + } + if !yaml_parser_parse(&p.parser, &p.event) { + p.fail() + } + return p.event.typ +} + +func (p *parser) fail() { + var where string + var line int + if p.parser.problem_mark.line != 0 { + line = p.parser.problem_mark.line + // Scanner errors don't iterate line before returning error + if p.parser.error == yaml_SCANNER_ERROR { + line++ + } + } else if p.parser.context_mark.line != 0 { + line = p.parser.context_mark.line + } + if line != 0 { + where = "line " + strconv.Itoa(line) + ": " + } + var msg string + if len(p.parser.problem) > 0 { + msg = p.parser.problem + } else { + msg = "unknown problem parsing YAML content" + } + failf("%s%s", where, msg) +} + +func (p *parser) anchor(n *node, anchor []byte) { + if anchor != nil { + p.doc.anchors[string(anchor)] = n + } +} + +func (p *parser) parse() *node { + p.init() + switch p.peek() { + case yaml_SCALAR_EVENT: + return p.scalar() + case yaml_ALIAS_EVENT: + return p.alias() + case yaml_MAPPING_START_EVENT: + return p.mapping() + case yaml_SEQUENCE_START_EVENT: + return p.sequence() + case yaml_DOCUMENT_START_EVENT: + return p.document() + case yaml_STREAM_END_EVENT: + // Happens when attempting to decode an empty buffer. + return nil + default: + panic("attempted to parse unknown event: " + p.event.typ.String()) + } +} + +func (p *parser) node(kind int) *node { + return &node{ + kind: kind, + line: p.event.start_mark.line, + column: p.event.start_mark.column, + } +} + +func (p *parser) document() *node { + n := p.node(documentNode) + n.anchors = make(map[string]*node) + p.doc = n + p.expect(yaml_DOCUMENT_START_EVENT) + n.children = append(n.children, p.parse()) + p.expect(yaml_DOCUMENT_END_EVENT) + return n +} + +func (p *parser) alias() *node { + n := p.node(aliasNode) + n.value = string(p.event.anchor) + n.alias = p.doc.anchors[n.value] + if n.alias == nil { + failf("unknown anchor '%s' referenced", n.value) + } + p.expect(yaml_ALIAS_EVENT) + return n +} + +func (p *parser) scalar() *node { + n := p.node(scalarNode) + n.value = string(p.event.value) + n.tag = string(p.event.tag) + n.implicit = p.event.implicit + p.anchor(n, p.event.anchor) + p.expect(yaml_SCALAR_EVENT) + return n +} + +func (p *parser) sequence() *node { + n := p.node(sequenceNode) + p.anchor(n, p.event.anchor) + p.expect(yaml_SEQUENCE_START_EVENT) + for p.peek() != yaml_SEQUENCE_END_EVENT { + n.children = append(n.children, p.parse()) + } + p.expect(yaml_SEQUENCE_END_EVENT) + return n +} + +func (p *parser) mapping() *node { + n := p.node(mappingNode) + p.anchor(n, p.event.anchor) + p.expect(yaml_MAPPING_START_EVENT) + for p.peek() != yaml_MAPPING_END_EVENT { + n.children = append(n.children, p.parse(), p.parse()) + } + p.expect(yaml_MAPPING_END_EVENT) + return n +} + +// ---------------------------------------------------------------------------- +// Decoder, unmarshals a node into a provided value. + +type decoder struct { + doc *node + aliases map[*node]bool + mapType reflect.Type + terrors []string + strict bool + + decodeCount int + aliasCount int + aliasDepth int +} + +var ( + mapItemType = reflect.TypeOf(MapItem{}) + durationType = reflect.TypeOf(time.Duration(0)) + defaultMapType = reflect.TypeOf(map[interface{}]interface{}{}) + ifaceType = defaultMapType.Elem() + timeType = reflect.TypeOf(time.Time{}) + ptrTimeType = reflect.TypeOf(&time.Time{}) +) + +func newDecoder(strict bool) *decoder { + d := &decoder{mapType: defaultMapType, strict: strict} + d.aliases = make(map[*node]bool) + return d +} + +func (d *decoder) terror(n *node, tag string, out reflect.Value) { + if n.tag != "" { + tag = n.tag + } + value := n.value + if tag != yaml_SEQ_TAG && tag != yaml_MAP_TAG { + if len(value) > 10 { + value = " `" + value[:7] + "...`" + } else { + value = " `" + value + "`" + } + } + d.terrors = append(d.terrors, fmt.Sprintf("line %d: cannot unmarshal %s%s into %s", n.line+1, shortTag(tag), value, out.Type())) +} + +func (d *decoder) callUnmarshaler(n *node, u Unmarshaler) (good bool) { + terrlen := len(d.terrors) + err := u.UnmarshalYAML(func(v interface{}) (err error) { + defer handleErr(&err) + d.unmarshal(n, reflect.ValueOf(v)) + if len(d.terrors) > terrlen { + issues := d.terrors[terrlen:] + d.terrors = d.terrors[:terrlen] + return &TypeError{issues} + } + return nil + }) + if e, ok := err.(*TypeError); ok { + d.terrors = append(d.terrors, e.Errors...) + return false + } + if err != nil { + fail(err) + } + return true +} + +// d.prepare initializes and dereferences pointers and calls UnmarshalYAML +// if a value is found to implement it. +// It returns the initialized and dereferenced out value, whether +// unmarshalling was already done by UnmarshalYAML, and if so whether +// its types unmarshalled appropriately. +// +// If n holds a null value, prepare returns before doing anything. +func (d *decoder) prepare(n *node, out reflect.Value) (newout reflect.Value, unmarshaled, good bool) { + if n.tag == yaml_NULL_TAG || n.kind == scalarNode && n.tag == "" && (n.value == "null" || n.value == "~" || n.value == "" && n.implicit) { + return out, false, false + } + again := true + for again { + again = false + if out.Kind() == reflect.Ptr { + if out.IsNil() { + out.Set(reflect.New(out.Type().Elem())) + } + out = out.Elem() + again = true + } + if out.CanAddr() { + if u, ok := out.Addr().Interface().(Unmarshaler); ok { + good = d.callUnmarshaler(n, u) + return out, true, good + } + } + } + return out, false, false +} + +const ( + // 400,000 decode operations is ~500kb of dense object declarations, or + // ~5kb of dense object declarations with 10000% alias expansion + alias_ratio_range_low = 400000 + + // 4,000,000 decode operations is ~5MB of dense object declarations, or + // ~4.5MB of dense object declarations with 10% alias expansion + alias_ratio_range_high = 4000000 + + // alias_ratio_range is the range over which we scale allowed alias ratios + alias_ratio_range = float64(alias_ratio_range_high - alias_ratio_range_low) +) + +func allowedAliasRatio(decodeCount int) float64 { + switch { + case decodeCount <= alias_ratio_range_low: + // allow 99% to come from alias expansion for small-to-medium documents + return 0.99 + case decodeCount >= alias_ratio_range_high: + // allow 10% to come from alias expansion for very large documents + return 0.10 + default: + // scale smoothly from 99% down to 10% over the range. + // this maps to 396,000 - 400,000 allowed alias-driven decodes over the range. + // 400,000 decode operations is ~100MB of allocations in worst-case scenarios (single-item maps). + return 0.99 - 0.89*(float64(decodeCount-alias_ratio_range_low)/alias_ratio_range) + } +} + +func (d *decoder) unmarshal(n *node, out reflect.Value) (good bool) { + d.decodeCount++ + if d.aliasDepth > 0 { + d.aliasCount++ + } + if d.aliasCount > 100 && d.decodeCount > 1000 && float64(d.aliasCount)/float64(d.decodeCount) > allowedAliasRatio(d.decodeCount) { + failf("document contains excessive aliasing") + } + switch n.kind { + case documentNode: + return d.document(n, out) + case aliasNode: + return d.alias(n, out) + } + out, unmarshaled, good := d.prepare(n, out) + if unmarshaled { + return good + } + switch n.kind { + case scalarNode: + good = d.scalar(n, out) + case mappingNode: + good = d.mapping(n, out) + case sequenceNode: + good = d.sequence(n, out) + default: + panic("internal error: unknown node kind: " + strconv.Itoa(n.kind)) + } + return good +} + +func (d *decoder) document(n *node, out reflect.Value) (good bool) { + if len(n.children) == 1 { + d.doc = n + d.unmarshal(n.children[0], out) + return true + } + return false +} + +func (d *decoder) alias(n *node, out reflect.Value) (good bool) { + if d.aliases[n] { + // TODO this could actually be allowed in some circumstances. + failf("anchor '%s' value contains itself", n.value) + } + d.aliases[n] = true + d.aliasDepth++ + good = d.unmarshal(n.alias, out) + d.aliasDepth-- + delete(d.aliases, n) + return good +} + +var zeroValue reflect.Value + +func resetMap(out reflect.Value) { + for _, k := range out.MapKeys() { + out.SetMapIndex(k, zeroValue) + } +} + +func (d *decoder) scalar(n *node, out reflect.Value) bool { + var tag string + var resolved interface{} + if n.tag == "" && !n.implicit { + tag = yaml_STR_TAG + resolved = n.value + } else { + tag, resolved = resolve(n.tag, n.value) + if tag == yaml_BINARY_TAG { + data, err := base64.StdEncoding.DecodeString(resolved.(string)) + if err != nil { + failf("!!binary value contains invalid base64 data") + } + resolved = string(data) + } + } + if resolved == nil { + if out.Kind() == reflect.Map && !out.CanAddr() { + resetMap(out) + } else { + out.Set(reflect.Zero(out.Type())) + } + return true + } + if resolvedv := reflect.ValueOf(resolved); out.Type() == resolvedv.Type() { + // We've resolved to exactly the type we want, so use that. + out.Set(resolvedv) + return true + } + // Perhaps we can use the value as a TextUnmarshaler to + // set its value. + if out.CanAddr() { + u, ok := out.Addr().Interface().(encoding.TextUnmarshaler) + if ok { + var text []byte + if tag == yaml_BINARY_TAG { + text = []byte(resolved.(string)) + } else { + // We let any value be unmarshaled into TextUnmarshaler. + // That might be more lax than we'd like, but the + // TextUnmarshaler itself should bowl out any dubious values. + text = []byte(n.value) + } + err := u.UnmarshalText(text) + if err != nil { + fail(err) + } + return true + } + } + switch out.Kind() { + case reflect.String: + if tag == yaml_BINARY_TAG { + out.SetString(resolved.(string)) + return true + } + if resolved != nil { + out.SetString(n.value) + return true + } + case reflect.Interface: + if resolved == nil { + out.Set(reflect.Zero(out.Type())) + } else if tag == yaml_TIMESTAMP_TAG { + // It looks like a timestamp but for backward compatibility + // reasons we set it as a string, so that code that unmarshals + // timestamp-like values into interface{} will continue to + // see a string and not a time.Time. + // TODO(v3) Drop this. + out.Set(reflect.ValueOf(n.value)) + } else { + out.Set(reflect.ValueOf(resolved)) + } + return true + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + switch resolved := resolved.(type) { + case int: + if !out.OverflowInt(int64(resolved)) { + out.SetInt(int64(resolved)) + return true + } + case int64: + if !out.OverflowInt(resolved) { + out.SetInt(resolved) + return true + } + case uint64: + if resolved <= math.MaxInt64 && !out.OverflowInt(int64(resolved)) { + out.SetInt(int64(resolved)) + return true + } + case float64: + if resolved <= math.MaxInt64 && !out.OverflowInt(int64(resolved)) { + out.SetInt(int64(resolved)) + return true + } + case string: + if out.Type() == durationType { + d, err := time.ParseDuration(resolved) + if err == nil { + out.SetInt(int64(d)) + return true + } + } + } + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + switch resolved := resolved.(type) { + case int: + if resolved >= 0 && !out.OverflowUint(uint64(resolved)) { + out.SetUint(uint64(resolved)) + return true + } + case int64: + if resolved >= 0 && !out.OverflowUint(uint64(resolved)) { + out.SetUint(uint64(resolved)) + return true + } + case uint64: + if !out.OverflowUint(uint64(resolved)) { + out.SetUint(uint64(resolved)) + return true + } + case float64: + if resolved <= math.MaxUint64 && !out.OverflowUint(uint64(resolved)) { + out.SetUint(uint64(resolved)) + return true + } + } + case reflect.Bool: + switch resolved := resolved.(type) { + case bool: + out.SetBool(resolved) + return true + } + case reflect.Float32, reflect.Float64: + switch resolved := resolved.(type) { + case int: + out.SetFloat(float64(resolved)) + return true + case int64: + out.SetFloat(float64(resolved)) + return true + case uint64: + out.SetFloat(float64(resolved)) + return true + case float64: + out.SetFloat(resolved) + return true + } + case reflect.Struct: + if resolvedv := reflect.ValueOf(resolved); out.Type() == resolvedv.Type() { + out.Set(resolvedv) + return true + } + case reflect.Ptr: + if out.Type().Elem() == reflect.TypeOf(resolved) { + // TODO DOes this make sense? When is out a Ptr except when decoding a nil value? + elem := reflect.New(out.Type().Elem()) + elem.Elem().Set(reflect.ValueOf(resolved)) + out.Set(elem) + return true + } + } + d.terror(n, tag, out) + return false +} + +func settableValueOf(i interface{}) reflect.Value { + v := reflect.ValueOf(i) + sv := reflect.New(v.Type()).Elem() + sv.Set(v) + return sv +} + +func (d *decoder) sequence(n *node, out reflect.Value) (good bool) { + l := len(n.children) + + var iface reflect.Value + switch out.Kind() { + case reflect.Slice: + out.Set(reflect.MakeSlice(out.Type(), l, l)) + case reflect.Array: + if l != out.Len() { + failf("invalid array: want %d elements but got %d", out.Len(), l) + } + case reflect.Interface: + // No type hints. Will have to use a generic sequence. + iface = out + out = settableValueOf(make([]interface{}, l)) + default: + d.terror(n, yaml_SEQ_TAG, out) + return false + } + et := out.Type().Elem() + + j := 0 + for i := 0; i < l; i++ { + e := reflect.New(et).Elem() + if ok := d.unmarshal(n.children[i], e); ok { + out.Index(j).Set(e) + j++ + } + } + if out.Kind() != reflect.Array { + out.Set(out.Slice(0, j)) + } + if iface.IsValid() { + iface.Set(out) + } + return true +} + +func (d *decoder) mapping(n *node, out reflect.Value) (good bool) { + switch out.Kind() { + case reflect.Struct: + return d.mappingStruct(n, out) + case reflect.Slice: + return d.mappingSlice(n, out) + case reflect.Map: + // okay + case reflect.Interface: + if d.mapType.Kind() == reflect.Map { + iface := out + out = reflect.MakeMap(d.mapType) + iface.Set(out) + } else { + slicev := reflect.New(d.mapType).Elem() + if !d.mappingSlice(n, slicev) { + return false + } + out.Set(slicev) + return true + } + default: + d.terror(n, yaml_MAP_TAG, out) + return false + } + outt := out.Type() + kt := outt.Key() + et := outt.Elem() + + mapType := d.mapType + if outt.Key() == ifaceType && outt.Elem() == ifaceType { + d.mapType = outt + } + + if out.IsNil() { + out.Set(reflect.MakeMap(outt)) + } + l := len(n.children) + for i := 0; i < l; i += 2 { + if isMerge(n.children[i]) { + d.merge(n.children[i+1], out) + continue + } + k := reflect.New(kt).Elem() + if d.unmarshal(n.children[i], k) { + kkind := k.Kind() + if kkind == reflect.Interface { + kkind = k.Elem().Kind() + } + if kkind == reflect.Map || kkind == reflect.Slice { + failf("invalid map key: %#v", k.Interface()) + } + e := reflect.New(et).Elem() + if d.unmarshal(n.children[i+1], e) { + d.setMapIndex(n.children[i+1], out, k, e) + } + } + } + d.mapType = mapType + return true +} + +func (d *decoder) setMapIndex(n *node, out, k, v reflect.Value) { + if d.strict && out.MapIndex(k) != zeroValue { + d.terrors = append(d.terrors, fmt.Sprintf("line %d: key %#v already set in map", n.line+1, k.Interface())) + return + } + out.SetMapIndex(k, v) +} + +func (d *decoder) mappingSlice(n *node, out reflect.Value) (good bool) { + outt := out.Type() + if outt.Elem() != mapItemType { + d.terror(n, yaml_MAP_TAG, out) + return false + } + + mapType := d.mapType + d.mapType = outt + + var slice []MapItem + var l = len(n.children) + for i := 0; i < l; i += 2 { + if isMerge(n.children[i]) { + d.merge(n.children[i+1], out) + continue + } + item := MapItem{} + k := reflect.ValueOf(&item.Key).Elem() + if d.unmarshal(n.children[i], k) { + v := reflect.ValueOf(&item.Value).Elem() + if d.unmarshal(n.children[i+1], v) { + slice = append(slice, item) + } + } + } + out.Set(reflect.ValueOf(slice)) + d.mapType = mapType + return true +} + +func (d *decoder) mappingStruct(n *node, out reflect.Value) (good bool) { + sinfo, err := getStructInfo(out.Type()) + if err != nil { + panic(err) + } + name := settableValueOf("") + l := len(n.children) + + var inlineMap reflect.Value + var elemType reflect.Type + if sinfo.InlineMap != -1 { + inlineMap = out.Field(sinfo.InlineMap) + inlineMap.Set(reflect.New(inlineMap.Type()).Elem()) + elemType = inlineMap.Type().Elem() + } + + var doneFields []bool + if d.strict { + doneFields = make([]bool, len(sinfo.FieldsList)) + } + for i := 0; i < l; i += 2 { + ni := n.children[i] + if isMerge(ni) { + d.merge(n.children[i+1], out) + continue + } + if !d.unmarshal(ni, name) { + continue + } + if info, ok := sinfo.FieldsMap[name.String()]; ok { + if d.strict { + if doneFields[info.Id] { + d.terrors = append(d.terrors, fmt.Sprintf("line %d: field %s already set in type %s", ni.line+1, name.String(), out.Type())) + continue + } + doneFields[info.Id] = true + } + var field reflect.Value + if info.Inline == nil { + field = out.Field(info.Num) + } else { + field = out.FieldByIndex(info.Inline) + } + d.unmarshal(n.children[i+1], field) + } else if sinfo.InlineMap != -1 { + if inlineMap.IsNil() { + inlineMap.Set(reflect.MakeMap(inlineMap.Type())) + } + value := reflect.New(elemType).Elem() + d.unmarshal(n.children[i+1], value) + d.setMapIndex(n.children[i+1], inlineMap, name, value) + } else if d.strict { + d.terrors = append(d.terrors, fmt.Sprintf("line %d: field %s not found in type %s", ni.line+1, name.String(), out.Type())) + } + } + return true +} + +func failWantMap() { + failf("map merge requires map or sequence of maps as the value") +} + +func (d *decoder) merge(n *node, out reflect.Value) { + switch n.kind { + case mappingNode: + d.unmarshal(n, out) + case aliasNode: + if n.alias != nil && n.alias.kind != mappingNode { + failWantMap() + } + d.unmarshal(n, out) + case sequenceNode: + // Step backwards as earlier nodes take precedence. + for i := len(n.children) - 1; i >= 0; i-- { + ni := n.children[i] + if ni.kind == aliasNode { + if ni.alias != nil && ni.alias.kind != mappingNode { + failWantMap() + } + } else if ni.kind != mappingNode { + failWantMap() + } + d.unmarshal(ni, out) + } + default: + failWantMap() + } +} + +func isMerge(n *node) bool { + return n.kind == scalarNode && n.value == "<<" && (n.implicit == true || n.tag == yaml_MERGE_TAG) +} diff --git a/vendor/gopkg.in/yaml.v2/emitterc.go b/vendor/gopkg.in/yaml.v2/emitterc.go new file mode 100644 index 000000000..a1c2cc526 --- /dev/null +++ b/vendor/gopkg.in/yaml.v2/emitterc.go @@ -0,0 +1,1685 @@ +package yaml + +import ( + "bytes" + "fmt" +) + +// Flush the buffer if needed. +func flush(emitter *yaml_emitter_t) bool { + if emitter.buffer_pos+5 >= len(emitter.buffer) { + return yaml_emitter_flush(emitter) + } + return true +} + +// Put a character to the output buffer. +func put(emitter *yaml_emitter_t, value byte) bool { + if emitter.buffer_pos+5 >= len(emitter.buffer) && !yaml_emitter_flush(emitter) { + return false + } + emitter.buffer[emitter.buffer_pos] = value + emitter.buffer_pos++ + emitter.column++ + return true +} + +// Put a line break to the output buffer. +func put_break(emitter *yaml_emitter_t) bool { + if emitter.buffer_pos+5 >= len(emitter.buffer) && !yaml_emitter_flush(emitter) { + return false + } + switch emitter.line_break { + case yaml_CR_BREAK: + emitter.buffer[emitter.buffer_pos] = '\r' + emitter.buffer_pos += 1 + case yaml_LN_BREAK: + emitter.buffer[emitter.buffer_pos] = '\n' + emitter.buffer_pos += 1 + case yaml_CRLN_BREAK: + emitter.buffer[emitter.buffer_pos+0] = '\r' + emitter.buffer[emitter.buffer_pos+1] = '\n' + emitter.buffer_pos += 2 + default: + panic("unknown line break setting") + } + emitter.column = 0 + emitter.line++ + return true +} + +// Copy a character from a string into buffer. +func write(emitter *yaml_emitter_t, s []byte, i *int) bool { + if emitter.buffer_pos+5 >= len(emitter.buffer) && !yaml_emitter_flush(emitter) { + return false + } + p := emitter.buffer_pos + w := width(s[*i]) + switch w { + case 4: + emitter.buffer[p+3] = s[*i+3] + fallthrough + case 3: + emitter.buffer[p+2] = s[*i+2] + fallthrough + case 2: + emitter.buffer[p+1] = s[*i+1] + fallthrough + case 1: + emitter.buffer[p+0] = s[*i+0] + default: + panic("unknown character width") + } + emitter.column++ + emitter.buffer_pos += w + *i += w + return true +} + +// Write a whole string into buffer. +func write_all(emitter *yaml_emitter_t, s []byte) bool { + for i := 0; i < len(s); { + if !write(emitter, s, &i) { + return false + } + } + return true +} + +// Copy a line break character from a string into buffer. +func write_break(emitter *yaml_emitter_t, s []byte, i *int) bool { + if s[*i] == '\n' { + if !put_break(emitter) { + return false + } + *i++ + } else { + if !write(emitter, s, i) { + return false + } + emitter.column = 0 + emitter.line++ + } + return true +} + +// Set an emitter error and return false. +func yaml_emitter_set_emitter_error(emitter *yaml_emitter_t, problem string) bool { + emitter.error = yaml_EMITTER_ERROR + emitter.problem = problem + return false +} + +// Emit an event. +func yaml_emitter_emit(emitter *yaml_emitter_t, event *yaml_event_t) bool { + emitter.events = append(emitter.events, *event) + for !yaml_emitter_need_more_events(emitter) { + event := &emitter.events[emitter.events_head] + if !yaml_emitter_analyze_event(emitter, event) { + return false + } + if !yaml_emitter_state_machine(emitter, event) { + return false + } + yaml_event_delete(event) + emitter.events_head++ + } + return true +} + +// Check if we need to accumulate more events before emitting. +// +// We accumulate extra +// - 1 event for DOCUMENT-START +// - 2 events for SEQUENCE-START +// - 3 events for MAPPING-START +// +func yaml_emitter_need_more_events(emitter *yaml_emitter_t) bool { + if emitter.events_head == len(emitter.events) { + return true + } + var accumulate int + switch emitter.events[emitter.events_head].typ { + case yaml_DOCUMENT_START_EVENT: + accumulate = 1 + break + case yaml_SEQUENCE_START_EVENT: + accumulate = 2 + break + case yaml_MAPPING_START_EVENT: + accumulate = 3 + break + default: + return false + } + if len(emitter.events)-emitter.events_head > accumulate { + return false + } + var level int + for i := emitter.events_head; i < len(emitter.events); i++ { + switch emitter.events[i].typ { + case yaml_STREAM_START_EVENT, yaml_DOCUMENT_START_EVENT, yaml_SEQUENCE_START_EVENT, yaml_MAPPING_START_EVENT: + level++ + case yaml_STREAM_END_EVENT, yaml_DOCUMENT_END_EVENT, yaml_SEQUENCE_END_EVENT, yaml_MAPPING_END_EVENT: + level-- + } + if level == 0 { + return false + } + } + return true +} + +// Append a directive to the directives stack. +func yaml_emitter_append_tag_directive(emitter *yaml_emitter_t, value *yaml_tag_directive_t, allow_duplicates bool) bool { + for i := 0; i < len(emitter.tag_directives); i++ { + if bytes.Equal(value.handle, emitter.tag_directives[i].handle) { + if allow_duplicates { + return true + } + return yaml_emitter_set_emitter_error(emitter, "duplicate %TAG directive") + } + } + + // [Go] Do we actually need to copy this given garbage collection + // and the lack of deallocating destructors? + tag_copy := yaml_tag_directive_t{ + handle: make([]byte, len(value.handle)), + prefix: make([]byte, len(value.prefix)), + } + copy(tag_copy.handle, value.handle) + copy(tag_copy.prefix, value.prefix) + emitter.tag_directives = append(emitter.tag_directives, tag_copy) + return true +} + +// Increase the indentation level. +func yaml_emitter_increase_indent(emitter *yaml_emitter_t, flow, indentless bool) bool { + emitter.indents = append(emitter.indents, emitter.indent) + if emitter.indent < 0 { + if flow { + emitter.indent = emitter.best_indent + } else { + emitter.indent = 0 + } + } else if !indentless { + emitter.indent += emitter.best_indent + } + return true +} + +// State dispatcher. +func yaml_emitter_state_machine(emitter *yaml_emitter_t, event *yaml_event_t) bool { + switch emitter.state { + default: + case yaml_EMIT_STREAM_START_STATE: + return yaml_emitter_emit_stream_start(emitter, event) + + case yaml_EMIT_FIRST_DOCUMENT_START_STATE: + return yaml_emitter_emit_document_start(emitter, event, true) + + case yaml_EMIT_DOCUMENT_START_STATE: + return yaml_emitter_emit_document_start(emitter, event, false) + + case yaml_EMIT_DOCUMENT_CONTENT_STATE: + return yaml_emitter_emit_document_content(emitter, event) + + case yaml_EMIT_DOCUMENT_END_STATE: + return yaml_emitter_emit_document_end(emitter, event) + + case yaml_EMIT_FLOW_SEQUENCE_FIRST_ITEM_STATE: + return yaml_emitter_emit_flow_sequence_item(emitter, event, true) + + case yaml_EMIT_FLOW_SEQUENCE_ITEM_STATE: + return yaml_emitter_emit_flow_sequence_item(emitter, event, false) + + case yaml_EMIT_FLOW_MAPPING_FIRST_KEY_STATE: + return yaml_emitter_emit_flow_mapping_key(emitter, event, true) + + case yaml_EMIT_FLOW_MAPPING_KEY_STATE: + return yaml_emitter_emit_flow_mapping_key(emitter, event, false) + + case yaml_EMIT_FLOW_MAPPING_SIMPLE_VALUE_STATE: + return yaml_emitter_emit_flow_mapping_value(emitter, event, true) + + case yaml_EMIT_FLOW_MAPPING_VALUE_STATE: + return yaml_emitter_emit_flow_mapping_value(emitter, event, false) + + case yaml_EMIT_BLOCK_SEQUENCE_FIRST_ITEM_STATE: + return yaml_emitter_emit_block_sequence_item(emitter, event, true) + + case yaml_EMIT_BLOCK_SEQUENCE_ITEM_STATE: + return yaml_emitter_emit_block_sequence_item(emitter, event, false) + + case yaml_EMIT_BLOCK_MAPPING_FIRST_KEY_STATE: + return yaml_emitter_emit_block_mapping_key(emitter, event, true) + + case yaml_EMIT_BLOCK_MAPPING_KEY_STATE: + return yaml_emitter_emit_block_mapping_key(emitter, event, false) + + case yaml_EMIT_BLOCK_MAPPING_SIMPLE_VALUE_STATE: + return yaml_emitter_emit_block_mapping_value(emitter, event, true) + + case yaml_EMIT_BLOCK_MAPPING_VALUE_STATE: + return yaml_emitter_emit_block_mapping_value(emitter, event, false) + + case yaml_EMIT_END_STATE: + return yaml_emitter_set_emitter_error(emitter, "expected nothing after STREAM-END") + } + panic("invalid emitter state") +} + +// Expect STREAM-START. +func yaml_emitter_emit_stream_start(emitter *yaml_emitter_t, event *yaml_event_t) bool { + if event.typ != yaml_STREAM_START_EVENT { + return yaml_emitter_set_emitter_error(emitter, "expected STREAM-START") + } + if emitter.encoding == yaml_ANY_ENCODING { + emitter.encoding = event.encoding + if emitter.encoding == yaml_ANY_ENCODING { + emitter.encoding = yaml_UTF8_ENCODING + } + } + if emitter.best_indent < 2 || emitter.best_indent > 9 { + emitter.best_indent = 2 + } + if emitter.best_width >= 0 && emitter.best_width <= emitter.best_indent*2 { + emitter.best_width = 80 + } + if emitter.best_width < 0 { + emitter.best_width = 1<<31 - 1 + } + if emitter.line_break == yaml_ANY_BREAK { + emitter.line_break = yaml_LN_BREAK + } + + emitter.indent = -1 + emitter.line = 0 + emitter.column = 0 + emitter.whitespace = true + emitter.indention = true + + if emitter.encoding != yaml_UTF8_ENCODING { + if !yaml_emitter_write_bom(emitter) { + return false + } + } + emitter.state = yaml_EMIT_FIRST_DOCUMENT_START_STATE + return true +} + +// Expect DOCUMENT-START or STREAM-END. +func yaml_emitter_emit_document_start(emitter *yaml_emitter_t, event *yaml_event_t, first bool) bool { + + if event.typ == yaml_DOCUMENT_START_EVENT { + + if event.version_directive != nil { + if !yaml_emitter_analyze_version_directive(emitter, event.version_directive) { + return false + } + } + + for i := 0; i < len(event.tag_directives); i++ { + tag_directive := &event.tag_directives[i] + if !yaml_emitter_analyze_tag_directive(emitter, tag_directive) { + return false + } + if !yaml_emitter_append_tag_directive(emitter, tag_directive, false) { + return false + } + } + + for i := 0; i < len(default_tag_directives); i++ { + tag_directive := &default_tag_directives[i] + if !yaml_emitter_append_tag_directive(emitter, tag_directive, true) { + return false + } + } + + implicit := event.implicit + if !first || emitter.canonical { + implicit = false + } + + if emitter.open_ended && (event.version_directive != nil || len(event.tag_directives) > 0) { + if !yaml_emitter_write_indicator(emitter, []byte("..."), true, false, false) { + return false + } + if !yaml_emitter_write_indent(emitter) { + return false + } + } + + if event.version_directive != nil { + implicit = false + if !yaml_emitter_write_indicator(emitter, []byte("%YAML"), true, false, false) { + return false + } + if !yaml_emitter_write_indicator(emitter, []byte("1.1"), true, false, false) { + return false + } + if !yaml_emitter_write_indent(emitter) { + return false + } + } + + if len(event.tag_directives) > 0 { + implicit = false + for i := 0; i < len(event.tag_directives); i++ { + tag_directive := &event.tag_directives[i] + if !yaml_emitter_write_indicator(emitter, []byte("%TAG"), true, false, false) { + return false + } + if !yaml_emitter_write_tag_handle(emitter, tag_directive.handle) { + return false + } + if !yaml_emitter_write_tag_content(emitter, tag_directive.prefix, true) { + return false + } + if !yaml_emitter_write_indent(emitter) { + return false + } + } + } + + if yaml_emitter_check_empty_document(emitter) { + implicit = false + } + if !implicit { + if !yaml_emitter_write_indent(emitter) { + return false + } + if !yaml_emitter_write_indicator(emitter, []byte("---"), true, false, false) { + return false + } + if emitter.canonical { + if !yaml_emitter_write_indent(emitter) { + return false + } + } + } + + emitter.state = yaml_EMIT_DOCUMENT_CONTENT_STATE + return true + } + + if event.typ == yaml_STREAM_END_EVENT { + if emitter.open_ended { + if !yaml_emitter_write_indicator(emitter, []byte("..."), true, false, false) { + return false + } + if !yaml_emitter_write_indent(emitter) { + return false + } + } + if !yaml_emitter_flush(emitter) { + return false + } + emitter.state = yaml_EMIT_END_STATE + return true + } + + return yaml_emitter_set_emitter_error(emitter, "expected DOCUMENT-START or STREAM-END") +} + +// Expect the root node. +func yaml_emitter_emit_document_content(emitter *yaml_emitter_t, event *yaml_event_t) bool { + emitter.states = append(emitter.states, yaml_EMIT_DOCUMENT_END_STATE) + return yaml_emitter_emit_node(emitter, event, true, false, false, false) +} + +// Expect DOCUMENT-END. +func yaml_emitter_emit_document_end(emitter *yaml_emitter_t, event *yaml_event_t) bool { + if event.typ != yaml_DOCUMENT_END_EVENT { + return yaml_emitter_set_emitter_error(emitter, "expected DOCUMENT-END") + } + if !yaml_emitter_write_indent(emitter) { + return false + } + if !event.implicit { + // [Go] Allocate the slice elsewhere. + if !yaml_emitter_write_indicator(emitter, []byte("..."), true, false, false) { + return false + } + if !yaml_emitter_write_indent(emitter) { + return false + } + } + if !yaml_emitter_flush(emitter) { + return false + } + emitter.state = yaml_EMIT_DOCUMENT_START_STATE + emitter.tag_directives = emitter.tag_directives[:0] + return true +} + +// Expect a flow item node. +func yaml_emitter_emit_flow_sequence_item(emitter *yaml_emitter_t, event *yaml_event_t, first bool) bool { + if first { + if !yaml_emitter_write_indicator(emitter, []byte{'['}, true, true, false) { + return false + } + if !yaml_emitter_increase_indent(emitter, true, false) { + return false + } + emitter.flow_level++ + } + + if event.typ == yaml_SEQUENCE_END_EVENT { + emitter.flow_level-- + emitter.indent = emitter.indents[len(emitter.indents)-1] + emitter.indents = emitter.indents[:len(emitter.indents)-1] + if emitter.canonical && !first { + if !yaml_emitter_write_indicator(emitter, []byte{','}, false, false, false) { + return false + } + if !yaml_emitter_write_indent(emitter) { + return false + } + } + if !yaml_emitter_write_indicator(emitter, []byte{']'}, false, false, false) { + return false + } + emitter.state = emitter.states[len(emitter.states)-1] + emitter.states = emitter.states[:len(emitter.states)-1] + + return true + } + + if !first { + if !yaml_emitter_write_indicator(emitter, []byte{','}, false, false, false) { + return false + } + } + + if emitter.canonical || emitter.column > emitter.best_width { + if !yaml_emitter_write_indent(emitter) { + return false + } + } + emitter.states = append(emitter.states, yaml_EMIT_FLOW_SEQUENCE_ITEM_STATE) + return yaml_emitter_emit_node(emitter, event, false, true, false, false) +} + +// Expect a flow key node. +func yaml_emitter_emit_flow_mapping_key(emitter *yaml_emitter_t, event *yaml_event_t, first bool) bool { + if first { + if !yaml_emitter_write_indicator(emitter, []byte{'{'}, true, true, false) { + return false + } + if !yaml_emitter_increase_indent(emitter, true, false) { + return false + } + emitter.flow_level++ + } + + if event.typ == yaml_MAPPING_END_EVENT { + emitter.flow_level-- + emitter.indent = emitter.indents[len(emitter.indents)-1] + emitter.indents = emitter.indents[:len(emitter.indents)-1] + if emitter.canonical && !first { + if !yaml_emitter_write_indicator(emitter, []byte{','}, false, false, false) { + return false + } + if !yaml_emitter_write_indent(emitter) { + return false + } + } + if !yaml_emitter_write_indicator(emitter, []byte{'}'}, false, false, false) { + return false + } + emitter.state = emitter.states[len(emitter.states)-1] + emitter.states = emitter.states[:len(emitter.states)-1] + return true + } + + if !first { + if !yaml_emitter_write_indicator(emitter, []byte{','}, false, false, false) { + return false + } + } + if emitter.canonical || emitter.column > emitter.best_width { + if !yaml_emitter_write_indent(emitter) { + return false + } + } + + if !emitter.canonical && yaml_emitter_check_simple_key(emitter) { + emitter.states = append(emitter.states, yaml_EMIT_FLOW_MAPPING_SIMPLE_VALUE_STATE) + return yaml_emitter_emit_node(emitter, event, false, false, true, true) + } + if !yaml_emitter_write_indicator(emitter, []byte{'?'}, true, false, false) { + return false + } + emitter.states = append(emitter.states, yaml_EMIT_FLOW_MAPPING_VALUE_STATE) + return yaml_emitter_emit_node(emitter, event, false, false, true, false) +} + +// Expect a flow value node. +func yaml_emitter_emit_flow_mapping_value(emitter *yaml_emitter_t, event *yaml_event_t, simple bool) bool { + if simple { + if !yaml_emitter_write_indicator(emitter, []byte{':'}, false, false, false) { + return false + } + } else { + if emitter.canonical || emitter.column > emitter.best_width { + if !yaml_emitter_write_indent(emitter) { + return false + } + } + if !yaml_emitter_write_indicator(emitter, []byte{':'}, true, false, false) { + return false + } + } + emitter.states = append(emitter.states, yaml_EMIT_FLOW_MAPPING_KEY_STATE) + return yaml_emitter_emit_node(emitter, event, false, false, true, false) +} + +// Expect a block item node. +func yaml_emitter_emit_block_sequence_item(emitter *yaml_emitter_t, event *yaml_event_t, first bool) bool { + if first { + if !yaml_emitter_increase_indent(emitter, false, emitter.mapping_context && !emitter.indention) { + return false + } + } + if event.typ == yaml_SEQUENCE_END_EVENT { + emitter.indent = emitter.indents[len(emitter.indents)-1] + emitter.indents = emitter.indents[:len(emitter.indents)-1] + emitter.state = emitter.states[len(emitter.states)-1] + emitter.states = emitter.states[:len(emitter.states)-1] + return true + } + if !yaml_emitter_write_indent(emitter) { + return false + } + if !yaml_emitter_write_indicator(emitter, []byte{'-'}, true, false, true) { + return false + } + emitter.states = append(emitter.states, yaml_EMIT_BLOCK_SEQUENCE_ITEM_STATE) + return yaml_emitter_emit_node(emitter, event, false, true, false, false) +} + +// Expect a block key node. +func yaml_emitter_emit_block_mapping_key(emitter *yaml_emitter_t, event *yaml_event_t, first bool) bool { + if first { + if !yaml_emitter_increase_indent(emitter, false, false) { + return false + } + } + if event.typ == yaml_MAPPING_END_EVENT { + emitter.indent = emitter.indents[len(emitter.indents)-1] + emitter.indents = emitter.indents[:len(emitter.indents)-1] + emitter.state = emitter.states[len(emitter.states)-1] + emitter.states = emitter.states[:len(emitter.states)-1] + return true + } + if !yaml_emitter_write_indent(emitter) { + return false + } + if yaml_emitter_check_simple_key(emitter) { + emitter.states = append(emitter.states, yaml_EMIT_BLOCK_MAPPING_SIMPLE_VALUE_STATE) + return yaml_emitter_emit_node(emitter, event, false, false, true, true) + } + if !yaml_emitter_write_indicator(emitter, []byte{'?'}, true, false, true) { + return false + } + emitter.states = append(emitter.states, yaml_EMIT_BLOCK_MAPPING_VALUE_STATE) + return yaml_emitter_emit_node(emitter, event, false, false, true, false) +} + +// Expect a block value node. +func yaml_emitter_emit_block_mapping_value(emitter *yaml_emitter_t, event *yaml_event_t, simple bool) bool { + if simple { + if !yaml_emitter_write_indicator(emitter, []byte{':'}, false, false, false) { + return false + } + } else { + if !yaml_emitter_write_indent(emitter) { + return false + } + if !yaml_emitter_write_indicator(emitter, []byte{':'}, true, false, true) { + return false + } + } + emitter.states = append(emitter.states, yaml_EMIT_BLOCK_MAPPING_KEY_STATE) + return yaml_emitter_emit_node(emitter, event, false, false, true, false) +} + +// Expect a node. +func yaml_emitter_emit_node(emitter *yaml_emitter_t, event *yaml_event_t, + root bool, sequence bool, mapping bool, simple_key bool) bool { + + emitter.root_context = root + emitter.sequence_context = sequence + emitter.mapping_context = mapping + emitter.simple_key_context = simple_key + + switch event.typ { + case yaml_ALIAS_EVENT: + return yaml_emitter_emit_alias(emitter, event) + case yaml_SCALAR_EVENT: + return yaml_emitter_emit_scalar(emitter, event) + case yaml_SEQUENCE_START_EVENT: + return yaml_emitter_emit_sequence_start(emitter, event) + case yaml_MAPPING_START_EVENT: + return yaml_emitter_emit_mapping_start(emitter, event) + default: + return yaml_emitter_set_emitter_error(emitter, + fmt.Sprintf("expected SCALAR, SEQUENCE-START, MAPPING-START, or ALIAS, but got %v", event.typ)) + } +} + +// Expect ALIAS. +func yaml_emitter_emit_alias(emitter *yaml_emitter_t, event *yaml_event_t) bool { + if !yaml_emitter_process_anchor(emitter) { + return false + } + emitter.state = emitter.states[len(emitter.states)-1] + emitter.states = emitter.states[:len(emitter.states)-1] + return true +} + +// Expect SCALAR. +func yaml_emitter_emit_scalar(emitter *yaml_emitter_t, event *yaml_event_t) bool { + if !yaml_emitter_select_scalar_style(emitter, event) { + return false + } + if !yaml_emitter_process_anchor(emitter) { + return false + } + if !yaml_emitter_process_tag(emitter) { + return false + } + if !yaml_emitter_increase_indent(emitter, true, false) { + return false + } + if !yaml_emitter_process_scalar(emitter) { + return false + } + emitter.indent = emitter.indents[len(emitter.indents)-1] + emitter.indents = emitter.indents[:len(emitter.indents)-1] + emitter.state = emitter.states[len(emitter.states)-1] + emitter.states = emitter.states[:len(emitter.states)-1] + return true +} + +// Expect SEQUENCE-START. +func yaml_emitter_emit_sequence_start(emitter *yaml_emitter_t, event *yaml_event_t) bool { + if !yaml_emitter_process_anchor(emitter) { + return false + } + if !yaml_emitter_process_tag(emitter) { + return false + } + if emitter.flow_level > 0 || emitter.canonical || event.sequence_style() == yaml_FLOW_SEQUENCE_STYLE || + yaml_emitter_check_empty_sequence(emitter) { + emitter.state = yaml_EMIT_FLOW_SEQUENCE_FIRST_ITEM_STATE + } else { + emitter.state = yaml_EMIT_BLOCK_SEQUENCE_FIRST_ITEM_STATE + } + return true +} + +// Expect MAPPING-START. +func yaml_emitter_emit_mapping_start(emitter *yaml_emitter_t, event *yaml_event_t) bool { + if !yaml_emitter_process_anchor(emitter) { + return false + } + if !yaml_emitter_process_tag(emitter) { + return false + } + if emitter.flow_level > 0 || emitter.canonical || event.mapping_style() == yaml_FLOW_MAPPING_STYLE || + yaml_emitter_check_empty_mapping(emitter) { + emitter.state = yaml_EMIT_FLOW_MAPPING_FIRST_KEY_STATE + } else { + emitter.state = yaml_EMIT_BLOCK_MAPPING_FIRST_KEY_STATE + } + return true +} + +// Check if the document content is an empty scalar. +func yaml_emitter_check_empty_document(emitter *yaml_emitter_t) bool { + return false // [Go] Huh? +} + +// Check if the next events represent an empty sequence. +func yaml_emitter_check_empty_sequence(emitter *yaml_emitter_t) bool { + if len(emitter.events)-emitter.events_head < 2 { + return false + } + return emitter.events[emitter.events_head].typ == yaml_SEQUENCE_START_EVENT && + emitter.events[emitter.events_head+1].typ == yaml_SEQUENCE_END_EVENT +} + +// Check if the next events represent an empty mapping. +func yaml_emitter_check_empty_mapping(emitter *yaml_emitter_t) bool { + if len(emitter.events)-emitter.events_head < 2 { + return false + } + return emitter.events[emitter.events_head].typ == yaml_MAPPING_START_EVENT && + emitter.events[emitter.events_head+1].typ == yaml_MAPPING_END_EVENT +} + +// Check if the next node can be expressed as a simple key. +func yaml_emitter_check_simple_key(emitter *yaml_emitter_t) bool { + length := 0 + switch emitter.events[emitter.events_head].typ { + case yaml_ALIAS_EVENT: + length += len(emitter.anchor_data.anchor) + case yaml_SCALAR_EVENT: + if emitter.scalar_data.multiline { + return false + } + length += len(emitter.anchor_data.anchor) + + len(emitter.tag_data.handle) + + len(emitter.tag_data.suffix) + + len(emitter.scalar_data.value) + case yaml_SEQUENCE_START_EVENT: + if !yaml_emitter_check_empty_sequence(emitter) { + return false + } + length += len(emitter.anchor_data.anchor) + + len(emitter.tag_data.handle) + + len(emitter.tag_data.suffix) + case yaml_MAPPING_START_EVENT: + if !yaml_emitter_check_empty_mapping(emitter) { + return false + } + length += len(emitter.anchor_data.anchor) + + len(emitter.tag_data.handle) + + len(emitter.tag_data.suffix) + default: + return false + } + return length <= 128 +} + +// Determine an acceptable scalar style. +func yaml_emitter_select_scalar_style(emitter *yaml_emitter_t, event *yaml_event_t) bool { + + no_tag := len(emitter.tag_data.handle) == 0 && len(emitter.tag_data.suffix) == 0 + if no_tag && !event.implicit && !event.quoted_implicit { + return yaml_emitter_set_emitter_error(emitter, "neither tag nor implicit flags are specified") + } + + style := event.scalar_style() + if style == yaml_ANY_SCALAR_STYLE { + style = yaml_PLAIN_SCALAR_STYLE + } + if emitter.canonical { + style = yaml_DOUBLE_QUOTED_SCALAR_STYLE + } + if emitter.simple_key_context && emitter.scalar_data.multiline { + style = yaml_DOUBLE_QUOTED_SCALAR_STYLE + } + + if style == yaml_PLAIN_SCALAR_STYLE { + if emitter.flow_level > 0 && !emitter.scalar_data.flow_plain_allowed || + emitter.flow_level == 0 && !emitter.scalar_data.block_plain_allowed { + style = yaml_SINGLE_QUOTED_SCALAR_STYLE + } + if len(emitter.scalar_data.value) == 0 && (emitter.flow_level > 0 || emitter.simple_key_context) { + style = yaml_SINGLE_QUOTED_SCALAR_STYLE + } + if no_tag && !event.implicit { + style = yaml_SINGLE_QUOTED_SCALAR_STYLE + } + } + if style == yaml_SINGLE_QUOTED_SCALAR_STYLE { + if !emitter.scalar_data.single_quoted_allowed { + style = yaml_DOUBLE_QUOTED_SCALAR_STYLE + } + } + if style == yaml_LITERAL_SCALAR_STYLE || style == yaml_FOLDED_SCALAR_STYLE { + if !emitter.scalar_data.block_allowed || emitter.flow_level > 0 || emitter.simple_key_context { + style = yaml_DOUBLE_QUOTED_SCALAR_STYLE + } + } + + if no_tag && !event.quoted_implicit && style != yaml_PLAIN_SCALAR_STYLE { + emitter.tag_data.handle = []byte{'!'} + } + emitter.scalar_data.style = style + return true +} + +// Write an anchor. +func yaml_emitter_process_anchor(emitter *yaml_emitter_t) bool { + if emitter.anchor_data.anchor == nil { + return true + } + c := []byte{'&'} + if emitter.anchor_data.alias { + c[0] = '*' + } + if !yaml_emitter_write_indicator(emitter, c, true, false, false) { + return false + } + return yaml_emitter_write_anchor(emitter, emitter.anchor_data.anchor) +} + +// Write a tag. +func yaml_emitter_process_tag(emitter *yaml_emitter_t) bool { + if len(emitter.tag_data.handle) == 0 && len(emitter.tag_data.suffix) == 0 { + return true + } + if len(emitter.tag_data.handle) > 0 { + if !yaml_emitter_write_tag_handle(emitter, emitter.tag_data.handle) { + return false + } + if len(emitter.tag_data.suffix) > 0 { + if !yaml_emitter_write_tag_content(emitter, emitter.tag_data.suffix, false) { + return false + } + } + } else { + // [Go] Allocate these slices elsewhere. + if !yaml_emitter_write_indicator(emitter, []byte("!<"), true, false, false) { + return false + } + if !yaml_emitter_write_tag_content(emitter, emitter.tag_data.suffix, false) { + return false + } + if !yaml_emitter_write_indicator(emitter, []byte{'>'}, false, false, false) { + return false + } + } + return true +} + +// Write a scalar. +func yaml_emitter_process_scalar(emitter *yaml_emitter_t) bool { + switch emitter.scalar_data.style { + case yaml_PLAIN_SCALAR_STYLE: + return yaml_emitter_write_plain_scalar(emitter, emitter.scalar_data.value, !emitter.simple_key_context) + + case yaml_SINGLE_QUOTED_SCALAR_STYLE: + return yaml_emitter_write_single_quoted_scalar(emitter, emitter.scalar_data.value, !emitter.simple_key_context) + + case yaml_DOUBLE_QUOTED_SCALAR_STYLE: + return yaml_emitter_write_double_quoted_scalar(emitter, emitter.scalar_data.value, !emitter.simple_key_context) + + case yaml_LITERAL_SCALAR_STYLE: + return yaml_emitter_write_literal_scalar(emitter, emitter.scalar_data.value) + + case yaml_FOLDED_SCALAR_STYLE: + return yaml_emitter_write_folded_scalar(emitter, emitter.scalar_data.value) + } + panic("unknown scalar style") +} + +// Check if a %YAML directive is valid. +func yaml_emitter_analyze_version_directive(emitter *yaml_emitter_t, version_directive *yaml_version_directive_t) bool { + if version_directive.major != 1 || version_directive.minor != 1 { + return yaml_emitter_set_emitter_error(emitter, "incompatible %YAML directive") + } + return true +} + +// Check if a %TAG directive is valid. +func yaml_emitter_analyze_tag_directive(emitter *yaml_emitter_t, tag_directive *yaml_tag_directive_t) bool { + handle := tag_directive.handle + prefix := tag_directive.prefix + if len(handle) == 0 { + return yaml_emitter_set_emitter_error(emitter, "tag handle must not be empty") + } + if handle[0] != '!' { + return yaml_emitter_set_emitter_error(emitter, "tag handle must start with '!'") + } + if handle[len(handle)-1] != '!' { + return yaml_emitter_set_emitter_error(emitter, "tag handle must end with '!'") + } + for i := 1; i < len(handle)-1; i += width(handle[i]) { + if !is_alpha(handle, i) { + return yaml_emitter_set_emitter_error(emitter, "tag handle must contain alphanumerical characters only") + } + } + if len(prefix) == 0 { + return yaml_emitter_set_emitter_error(emitter, "tag prefix must not be empty") + } + return true +} + +// Check if an anchor is valid. +func yaml_emitter_analyze_anchor(emitter *yaml_emitter_t, anchor []byte, alias bool) bool { + if len(anchor) == 0 { + problem := "anchor value must not be empty" + if alias { + problem = "alias value must not be empty" + } + return yaml_emitter_set_emitter_error(emitter, problem) + } + for i := 0; i < len(anchor); i += width(anchor[i]) { + if !is_alpha(anchor, i) { + problem := "anchor value must contain alphanumerical characters only" + if alias { + problem = "alias value must contain alphanumerical characters only" + } + return yaml_emitter_set_emitter_error(emitter, problem) + } + } + emitter.anchor_data.anchor = anchor + emitter.anchor_data.alias = alias + return true +} + +// Check if a tag is valid. +func yaml_emitter_analyze_tag(emitter *yaml_emitter_t, tag []byte) bool { + if len(tag) == 0 { + return yaml_emitter_set_emitter_error(emitter, "tag value must not be empty") + } + for i := 0; i < len(emitter.tag_directives); i++ { + tag_directive := &emitter.tag_directives[i] + if bytes.HasPrefix(tag, tag_directive.prefix) { + emitter.tag_data.handle = tag_directive.handle + emitter.tag_data.suffix = tag[len(tag_directive.prefix):] + return true + } + } + emitter.tag_data.suffix = tag + return true +} + +// Check if a scalar is valid. +func yaml_emitter_analyze_scalar(emitter *yaml_emitter_t, value []byte) bool { + var ( + block_indicators = false + flow_indicators = false + line_breaks = false + special_characters = false + + leading_space = false + leading_break = false + trailing_space = false + trailing_break = false + break_space = false + space_break = false + + preceded_by_whitespace = false + followed_by_whitespace = false + previous_space = false + previous_break = false + ) + + emitter.scalar_data.value = value + + if len(value) == 0 { + emitter.scalar_data.multiline = false + emitter.scalar_data.flow_plain_allowed = false + emitter.scalar_data.block_plain_allowed = true + emitter.scalar_data.single_quoted_allowed = true + emitter.scalar_data.block_allowed = false + return true + } + + if len(value) >= 3 && ((value[0] == '-' && value[1] == '-' && value[2] == '-') || (value[0] == '.' && value[1] == '.' && value[2] == '.')) { + block_indicators = true + flow_indicators = true + } + + preceded_by_whitespace = true + for i, w := 0, 0; i < len(value); i += w { + w = width(value[i]) + followed_by_whitespace = i+w >= len(value) || is_blank(value, i+w) + + if i == 0 { + switch value[i] { + case '#', ',', '[', ']', '{', '}', '&', '*', '!', '|', '>', '\'', '"', '%', '@', '`': + flow_indicators = true + block_indicators = true + case '?', ':': + flow_indicators = true + if followed_by_whitespace { + block_indicators = true + } + case '-': + if followed_by_whitespace { + flow_indicators = true + block_indicators = true + } + } + } else { + switch value[i] { + case ',', '?', '[', ']', '{', '}': + flow_indicators = true + case ':': + flow_indicators = true + if followed_by_whitespace { + block_indicators = true + } + case '#': + if preceded_by_whitespace { + flow_indicators = true + block_indicators = true + } + } + } + + if !is_printable(value, i) || !is_ascii(value, i) && !emitter.unicode { + special_characters = true + } + if is_space(value, i) { + if i == 0 { + leading_space = true + } + if i+width(value[i]) == len(value) { + trailing_space = true + } + if previous_break { + break_space = true + } + previous_space = true + previous_break = false + } else if is_break(value, i) { + line_breaks = true + if i == 0 { + leading_break = true + } + if i+width(value[i]) == len(value) { + trailing_break = true + } + if previous_space { + space_break = true + } + previous_space = false + previous_break = true + } else { + previous_space = false + previous_break = false + } + + // [Go]: Why 'z'? Couldn't be the end of the string as that's the loop condition. + preceded_by_whitespace = is_blankz(value, i) + } + + emitter.scalar_data.multiline = line_breaks + emitter.scalar_data.flow_plain_allowed = true + emitter.scalar_data.block_plain_allowed = true + emitter.scalar_data.single_quoted_allowed = true + emitter.scalar_data.block_allowed = true + + if leading_space || leading_break || trailing_space || trailing_break { + emitter.scalar_data.flow_plain_allowed = false + emitter.scalar_data.block_plain_allowed = false + } + if trailing_space { + emitter.scalar_data.block_allowed = false + } + if break_space { + emitter.scalar_data.flow_plain_allowed = false + emitter.scalar_data.block_plain_allowed = false + emitter.scalar_data.single_quoted_allowed = false + } + if space_break || special_characters { + emitter.scalar_data.flow_plain_allowed = false + emitter.scalar_data.block_plain_allowed = false + emitter.scalar_data.single_quoted_allowed = false + emitter.scalar_data.block_allowed = false + } + if line_breaks { + emitter.scalar_data.flow_plain_allowed = false + emitter.scalar_data.block_plain_allowed = false + } + if flow_indicators { + emitter.scalar_data.flow_plain_allowed = false + } + if block_indicators { + emitter.scalar_data.block_plain_allowed = false + } + return true +} + +// Check if the event data is valid. +func yaml_emitter_analyze_event(emitter *yaml_emitter_t, event *yaml_event_t) bool { + + emitter.anchor_data.anchor = nil + emitter.tag_data.handle = nil + emitter.tag_data.suffix = nil + emitter.scalar_data.value = nil + + switch event.typ { + case yaml_ALIAS_EVENT: + if !yaml_emitter_analyze_anchor(emitter, event.anchor, true) { + return false + } + + case yaml_SCALAR_EVENT: + if len(event.anchor) > 0 { + if !yaml_emitter_analyze_anchor(emitter, event.anchor, false) { + return false + } + } + if len(event.tag) > 0 && (emitter.canonical || (!event.implicit && !event.quoted_implicit)) { + if !yaml_emitter_analyze_tag(emitter, event.tag) { + return false + } + } + if !yaml_emitter_analyze_scalar(emitter, event.value) { + return false + } + + case yaml_SEQUENCE_START_EVENT: + if len(event.anchor) > 0 { + if !yaml_emitter_analyze_anchor(emitter, event.anchor, false) { + return false + } + } + if len(event.tag) > 0 && (emitter.canonical || !event.implicit) { + if !yaml_emitter_analyze_tag(emitter, event.tag) { + return false + } + } + + case yaml_MAPPING_START_EVENT: + if len(event.anchor) > 0 { + if !yaml_emitter_analyze_anchor(emitter, event.anchor, false) { + return false + } + } + if len(event.tag) > 0 && (emitter.canonical || !event.implicit) { + if !yaml_emitter_analyze_tag(emitter, event.tag) { + return false + } + } + } + return true +} + +// Write the BOM character. +func yaml_emitter_write_bom(emitter *yaml_emitter_t) bool { + if !flush(emitter) { + return false + } + pos := emitter.buffer_pos + emitter.buffer[pos+0] = '\xEF' + emitter.buffer[pos+1] = '\xBB' + emitter.buffer[pos+2] = '\xBF' + emitter.buffer_pos += 3 + return true +} + +func yaml_emitter_write_indent(emitter *yaml_emitter_t) bool { + indent := emitter.indent + if indent < 0 { + indent = 0 + } + if !emitter.indention || emitter.column > indent || (emitter.column == indent && !emitter.whitespace) { + if !put_break(emitter) { + return false + } + } + for emitter.column < indent { + if !put(emitter, ' ') { + return false + } + } + emitter.whitespace = true + emitter.indention = true + return true +} + +func yaml_emitter_write_indicator(emitter *yaml_emitter_t, indicator []byte, need_whitespace, is_whitespace, is_indention bool) bool { + if need_whitespace && !emitter.whitespace { + if !put(emitter, ' ') { + return false + } + } + if !write_all(emitter, indicator) { + return false + } + emitter.whitespace = is_whitespace + emitter.indention = (emitter.indention && is_indention) + emitter.open_ended = false + return true +} + +func yaml_emitter_write_anchor(emitter *yaml_emitter_t, value []byte) bool { + if !write_all(emitter, value) { + return false + } + emitter.whitespace = false + emitter.indention = false + return true +} + +func yaml_emitter_write_tag_handle(emitter *yaml_emitter_t, value []byte) bool { + if !emitter.whitespace { + if !put(emitter, ' ') { + return false + } + } + if !write_all(emitter, value) { + return false + } + emitter.whitespace = false + emitter.indention = false + return true +} + +func yaml_emitter_write_tag_content(emitter *yaml_emitter_t, value []byte, need_whitespace bool) bool { + if need_whitespace && !emitter.whitespace { + if !put(emitter, ' ') { + return false + } + } + for i := 0; i < len(value); { + var must_write bool + switch value[i] { + case ';', '/', '?', ':', '@', '&', '=', '+', '$', ',', '_', '.', '~', '*', '\'', '(', ')', '[', ']': + must_write = true + default: + must_write = is_alpha(value, i) + } + if must_write { + if !write(emitter, value, &i) { + return false + } + } else { + w := width(value[i]) + for k := 0; k < w; k++ { + octet := value[i] + i++ + if !put(emitter, '%') { + return false + } + + c := octet >> 4 + if c < 10 { + c += '0' + } else { + c += 'A' - 10 + } + if !put(emitter, c) { + return false + } + + c = octet & 0x0f + if c < 10 { + c += '0' + } else { + c += 'A' - 10 + } + if !put(emitter, c) { + return false + } + } + } + } + emitter.whitespace = false + emitter.indention = false + return true +} + +func yaml_emitter_write_plain_scalar(emitter *yaml_emitter_t, value []byte, allow_breaks bool) bool { + if !emitter.whitespace { + if !put(emitter, ' ') { + return false + } + } + + spaces := false + breaks := false + for i := 0; i < len(value); { + if is_space(value, i) { + if allow_breaks && !spaces && emitter.column > emitter.best_width && !is_space(value, i+1) { + if !yaml_emitter_write_indent(emitter) { + return false + } + i += width(value[i]) + } else { + if !write(emitter, value, &i) { + return false + } + } + spaces = true + } else if is_break(value, i) { + if !breaks && value[i] == '\n' { + if !put_break(emitter) { + return false + } + } + if !write_break(emitter, value, &i) { + return false + } + emitter.indention = true + breaks = true + } else { + if breaks { + if !yaml_emitter_write_indent(emitter) { + return false + } + } + if !write(emitter, value, &i) { + return false + } + emitter.indention = false + spaces = false + breaks = false + } + } + + emitter.whitespace = false + emitter.indention = false + if emitter.root_context { + emitter.open_ended = true + } + + return true +} + +func yaml_emitter_write_single_quoted_scalar(emitter *yaml_emitter_t, value []byte, allow_breaks bool) bool { + + if !yaml_emitter_write_indicator(emitter, []byte{'\''}, true, false, false) { + return false + } + + spaces := false + breaks := false + for i := 0; i < len(value); { + if is_space(value, i) { + if allow_breaks && !spaces && emitter.column > emitter.best_width && i > 0 && i < len(value)-1 && !is_space(value, i+1) { + if !yaml_emitter_write_indent(emitter) { + return false + } + i += width(value[i]) + } else { + if !write(emitter, value, &i) { + return false + } + } + spaces = true + } else if is_break(value, i) { + if !breaks && value[i] == '\n' { + if !put_break(emitter) { + return false + } + } + if !write_break(emitter, value, &i) { + return false + } + emitter.indention = true + breaks = true + } else { + if breaks { + if !yaml_emitter_write_indent(emitter) { + return false + } + } + if value[i] == '\'' { + if !put(emitter, '\'') { + return false + } + } + if !write(emitter, value, &i) { + return false + } + emitter.indention = false + spaces = false + breaks = false + } + } + if !yaml_emitter_write_indicator(emitter, []byte{'\''}, false, false, false) { + return false + } + emitter.whitespace = false + emitter.indention = false + return true +} + +func yaml_emitter_write_double_quoted_scalar(emitter *yaml_emitter_t, value []byte, allow_breaks bool) bool { + spaces := false + if !yaml_emitter_write_indicator(emitter, []byte{'"'}, true, false, false) { + return false + } + + for i := 0; i < len(value); { + if !is_printable(value, i) || (!emitter.unicode && !is_ascii(value, i)) || + is_bom(value, i) || is_break(value, i) || + value[i] == '"' || value[i] == '\\' { + + octet := value[i] + + var w int + var v rune + switch { + case octet&0x80 == 0x00: + w, v = 1, rune(octet&0x7F) + case octet&0xE0 == 0xC0: + w, v = 2, rune(octet&0x1F) + case octet&0xF0 == 0xE0: + w, v = 3, rune(octet&0x0F) + case octet&0xF8 == 0xF0: + w, v = 4, rune(octet&0x07) + } + for k := 1; k < w; k++ { + octet = value[i+k] + v = (v << 6) + (rune(octet) & 0x3F) + } + i += w + + if !put(emitter, '\\') { + return false + } + + var ok bool + switch v { + case 0x00: + ok = put(emitter, '0') + case 0x07: + ok = put(emitter, 'a') + case 0x08: + ok = put(emitter, 'b') + case 0x09: + ok = put(emitter, 't') + case 0x0A: + ok = put(emitter, 'n') + case 0x0b: + ok = put(emitter, 'v') + case 0x0c: + ok = put(emitter, 'f') + case 0x0d: + ok = put(emitter, 'r') + case 0x1b: + ok = put(emitter, 'e') + case 0x22: + ok = put(emitter, '"') + case 0x5c: + ok = put(emitter, '\\') + case 0x85: + ok = put(emitter, 'N') + case 0xA0: + ok = put(emitter, '_') + case 0x2028: + ok = put(emitter, 'L') + case 0x2029: + ok = put(emitter, 'P') + default: + if v <= 0xFF { + ok = put(emitter, 'x') + w = 2 + } else if v <= 0xFFFF { + ok = put(emitter, 'u') + w = 4 + } else { + ok = put(emitter, 'U') + w = 8 + } + for k := (w - 1) * 4; ok && k >= 0; k -= 4 { + digit := byte((v >> uint(k)) & 0x0F) + if digit < 10 { + ok = put(emitter, digit+'0') + } else { + ok = put(emitter, digit+'A'-10) + } + } + } + if !ok { + return false + } + spaces = false + } else if is_space(value, i) { + if allow_breaks && !spaces && emitter.column > emitter.best_width && i > 0 && i < len(value)-1 { + if !yaml_emitter_write_indent(emitter) { + return false + } + if is_space(value, i+1) { + if !put(emitter, '\\') { + return false + } + } + i += width(value[i]) + } else if !write(emitter, value, &i) { + return false + } + spaces = true + } else { + if !write(emitter, value, &i) { + return false + } + spaces = false + } + } + if !yaml_emitter_write_indicator(emitter, []byte{'"'}, false, false, false) { + return false + } + emitter.whitespace = false + emitter.indention = false + return true +} + +func yaml_emitter_write_block_scalar_hints(emitter *yaml_emitter_t, value []byte) bool { + if is_space(value, 0) || is_break(value, 0) { + indent_hint := []byte{'0' + byte(emitter.best_indent)} + if !yaml_emitter_write_indicator(emitter, indent_hint, false, false, false) { + return false + } + } + + emitter.open_ended = false + + var chomp_hint [1]byte + if len(value) == 0 { + chomp_hint[0] = '-' + } else { + i := len(value) - 1 + for value[i]&0xC0 == 0x80 { + i-- + } + if !is_break(value, i) { + chomp_hint[0] = '-' + } else if i == 0 { + chomp_hint[0] = '+' + emitter.open_ended = true + } else { + i-- + for value[i]&0xC0 == 0x80 { + i-- + } + if is_break(value, i) { + chomp_hint[0] = '+' + emitter.open_ended = true + } + } + } + if chomp_hint[0] != 0 { + if !yaml_emitter_write_indicator(emitter, chomp_hint[:], false, false, false) { + return false + } + } + return true +} + +func yaml_emitter_write_literal_scalar(emitter *yaml_emitter_t, value []byte) bool { + if !yaml_emitter_write_indicator(emitter, []byte{'|'}, true, false, false) { + return false + } + if !yaml_emitter_write_block_scalar_hints(emitter, value) { + return false + } + if !put_break(emitter) { + return false + } + emitter.indention = true + emitter.whitespace = true + breaks := true + for i := 0; i < len(value); { + if is_break(value, i) { + if !write_break(emitter, value, &i) { + return false + } + emitter.indention = true + breaks = true + } else { + if breaks { + if !yaml_emitter_write_indent(emitter) { + return false + } + } + if !write(emitter, value, &i) { + return false + } + emitter.indention = false + breaks = false + } + } + + return true +} + +func yaml_emitter_write_folded_scalar(emitter *yaml_emitter_t, value []byte) bool { + if !yaml_emitter_write_indicator(emitter, []byte{'>'}, true, false, false) { + return false + } + if !yaml_emitter_write_block_scalar_hints(emitter, value) { + return false + } + + if !put_break(emitter) { + return false + } + emitter.indention = true + emitter.whitespace = true + + breaks := true + leading_spaces := true + for i := 0; i < len(value); { + if is_break(value, i) { + if !breaks && !leading_spaces && value[i] == '\n' { + k := 0 + for is_break(value, k) { + k += width(value[k]) + } + if !is_blankz(value, k) { + if !put_break(emitter) { + return false + } + } + } + if !write_break(emitter, value, &i) { + return false + } + emitter.indention = true + breaks = true + } else { + if breaks { + if !yaml_emitter_write_indent(emitter) { + return false + } + leading_spaces = is_blank(value, i) + } + if !breaks && is_space(value, i) && !is_space(value, i+1) && emitter.column > emitter.best_width { + if !yaml_emitter_write_indent(emitter) { + return false + } + i += width(value[i]) + } else { + if !write(emitter, value, &i) { + return false + } + } + emitter.indention = false + breaks = false + } + } + return true +} diff --git a/vendor/gopkg.in/yaml.v2/encode.go b/vendor/gopkg.in/yaml.v2/encode.go new file mode 100644 index 000000000..0ee738e11 --- /dev/null +++ b/vendor/gopkg.in/yaml.v2/encode.go @@ -0,0 +1,390 @@ +package yaml + +import ( + "encoding" + "fmt" + "io" + "reflect" + "regexp" + "sort" + "strconv" + "strings" + "time" + "unicode/utf8" +) + +// jsonNumber is the interface of the encoding/json.Number datatype. +// Repeating the interface here avoids a dependency on encoding/json, and also +// supports other libraries like jsoniter, which use a similar datatype with +// the same interface. Detecting this interface is useful when dealing with +// structures containing json.Number, which is a string under the hood. The +// encoder should prefer the use of Int64(), Float64() and string(), in that +// order, when encoding this type. +type jsonNumber interface { + Float64() (float64, error) + Int64() (int64, error) + String() string +} + +type encoder struct { + emitter yaml_emitter_t + event yaml_event_t + out []byte + flow bool + // doneInit holds whether the initial stream_start_event has been + // emitted. + doneInit bool +} + +func newEncoder() *encoder { + e := &encoder{} + yaml_emitter_initialize(&e.emitter) + yaml_emitter_set_output_string(&e.emitter, &e.out) + yaml_emitter_set_unicode(&e.emitter, true) + return e +} + +func newEncoderWithWriter(w io.Writer) *encoder { + e := &encoder{} + yaml_emitter_initialize(&e.emitter) + yaml_emitter_set_output_writer(&e.emitter, w) + yaml_emitter_set_unicode(&e.emitter, true) + return e +} + +func (e *encoder) init() { + if e.doneInit { + return + } + yaml_stream_start_event_initialize(&e.event, yaml_UTF8_ENCODING) + e.emit() + e.doneInit = true +} + +func (e *encoder) finish() { + e.emitter.open_ended = false + yaml_stream_end_event_initialize(&e.event) + e.emit() +} + +func (e *encoder) destroy() { + yaml_emitter_delete(&e.emitter) +} + +func (e *encoder) emit() { + // This will internally delete the e.event value. + e.must(yaml_emitter_emit(&e.emitter, &e.event)) +} + +func (e *encoder) must(ok bool) { + if !ok { + msg := e.emitter.problem + if msg == "" { + msg = "unknown problem generating YAML content" + } + failf("%s", msg) + } +} + +func (e *encoder) marshalDoc(tag string, in reflect.Value) { + e.init() + yaml_document_start_event_initialize(&e.event, nil, nil, true) + e.emit() + e.marshal(tag, in) + yaml_document_end_event_initialize(&e.event, true) + e.emit() +} + +func (e *encoder) marshal(tag string, in reflect.Value) { + if !in.IsValid() || in.Kind() == reflect.Ptr && in.IsNil() { + e.nilv() + return + } + iface := in.Interface() + switch m := iface.(type) { + case jsonNumber: + integer, err := m.Int64() + if err == nil { + // In this case the json.Number is a valid int64 + in = reflect.ValueOf(integer) + break + } + float, err := m.Float64() + if err == nil { + // In this case the json.Number is a valid float64 + in = reflect.ValueOf(float) + break + } + // fallback case - no number could be obtained + in = reflect.ValueOf(m.String()) + case time.Time, *time.Time: + // Although time.Time implements TextMarshaler, + // we don't want to treat it as a string for YAML + // purposes because YAML has special support for + // timestamps. + case Marshaler: + v, err := m.MarshalYAML() + if err != nil { + fail(err) + } + if v == nil { + e.nilv() + return + } + in = reflect.ValueOf(v) + case encoding.TextMarshaler: + text, err := m.MarshalText() + if err != nil { + fail(err) + } + in = reflect.ValueOf(string(text)) + case nil: + e.nilv() + return + } + switch in.Kind() { + case reflect.Interface: + e.marshal(tag, in.Elem()) + case reflect.Map: + e.mapv(tag, in) + case reflect.Ptr: + if in.Type() == ptrTimeType { + e.timev(tag, in.Elem()) + } else { + e.marshal(tag, in.Elem()) + } + case reflect.Struct: + if in.Type() == timeType { + e.timev(tag, in) + } else { + e.structv(tag, in) + } + case reflect.Slice, reflect.Array: + if in.Type().Elem() == mapItemType { + e.itemsv(tag, in) + } else { + e.slicev(tag, in) + } + case reflect.String: + e.stringv(tag, in) + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + if in.Type() == durationType { + e.stringv(tag, reflect.ValueOf(iface.(time.Duration).String())) + } else { + e.intv(tag, in) + } + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + e.uintv(tag, in) + case reflect.Float32, reflect.Float64: + e.floatv(tag, in) + case reflect.Bool: + e.boolv(tag, in) + default: + panic("cannot marshal type: " + in.Type().String()) + } +} + +func (e *encoder) mapv(tag string, in reflect.Value) { + e.mappingv(tag, func() { + keys := keyList(in.MapKeys()) + sort.Sort(keys) + for _, k := range keys { + e.marshal("", k) + e.marshal("", in.MapIndex(k)) + } + }) +} + +func (e *encoder) itemsv(tag string, in reflect.Value) { + e.mappingv(tag, func() { + slice := in.Convert(reflect.TypeOf([]MapItem{})).Interface().([]MapItem) + for _, item := range slice { + e.marshal("", reflect.ValueOf(item.Key)) + e.marshal("", reflect.ValueOf(item.Value)) + } + }) +} + +func (e *encoder) structv(tag string, in reflect.Value) { + sinfo, err := getStructInfo(in.Type()) + if err != nil { + panic(err) + } + e.mappingv(tag, func() { + for _, info := range sinfo.FieldsList { + var value reflect.Value + if info.Inline == nil { + value = in.Field(info.Num) + } else { + value = in.FieldByIndex(info.Inline) + } + if info.OmitEmpty && isZero(value) { + continue + } + e.marshal("", reflect.ValueOf(info.Key)) + e.flow = info.Flow + e.marshal("", value) + } + if sinfo.InlineMap >= 0 { + m := in.Field(sinfo.InlineMap) + if m.Len() > 0 { + e.flow = false + keys := keyList(m.MapKeys()) + sort.Sort(keys) + for _, k := range keys { + if _, found := sinfo.FieldsMap[k.String()]; found { + panic(fmt.Sprintf("Can't have key %q in inlined map; conflicts with struct field", k.String())) + } + e.marshal("", k) + e.flow = false + e.marshal("", m.MapIndex(k)) + } + } + } + }) +} + +func (e *encoder) mappingv(tag string, f func()) { + implicit := tag == "" + style := yaml_BLOCK_MAPPING_STYLE + if e.flow { + e.flow = false + style = yaml_FLOW_MAPPING_STYLE + } + yaml_mapping_start_event_initialize(&e.event, nil, []byte(tag), implicit, style) + e.emit() + f() + yaml_mapping_end_event_initialize(&e.event) + e.emit() +} + +func (e *encoder) slicev(tag string, in reflect.Value) { + implicit := tag == "" + style := yaml_BLOCK_SEQUENCE_STYLE + if e.flow { + e.flow = false + style = yaml_FLOW_SEQUENCE_STYLE + } + e.must(yaml_sequence_start_event_initialize(&e.event, nil, []byte(tag), implicit, style)) + e.emit() + n := in.Len() + for i := 0; i < n; i++ { + e.marshal("", in.Index(i)) + } + e.must(yaml_sequence_end_event_initialize(&e.event)) + e.emit() +} + +// isBase60 returns whether s is in base 60 notation as defined in YAML 1.1. +// +// The base 60 float notation in YAML 1.1 is a terrible idea and is unsupported +// in YAML 1.2 and by this package, but these should be marshalled quoted for +// the time being for compatibility with other parsers. +func isBase60Float(s string) (result bool) { + // Fast path. + if s == "" { + return false + } + c := s[0] + if !(c == '+' || c == '-' || c >= '0' && c <= '9') || strings.IndexByte(s, ':') < 0 { + return false + } + // Do the full match. + return base60float.MatchString(s) +} + +// From http://yaml.org/type/float.html, except the regular expression there +// is bogus. In practice parsers do not enforce the "\.[0-9_]*" suffix. +var base60float = regexp.MustCompile(`^[-+]?[0-9][0-9_]*(?::[0-5]?[0-9])+(?:\.[0-9_]*)?$`) + +func (e *encoder) stringv(tag string, in reflect.Value) { + var style yaml_scalar_style_t + s := in.String() + canUsePlain := true + switch { + case !utf8.ValidString(s): + if tag == yaml_BINARY_TAG { + failf("explicitly tagged !!binary data must be base64-encoded") + } + if tag != "" { + failf("cannot marshal invalid UTF-8 data as %s", shortTag(tag)) + } + // It can't be encoded directly as YAML so use a binary tag + // and encode it as base64. + tag = yaml_BINARY_TAG + s = encodeBase64(s) + case tag == "": + // Check to see if it would resolve to a specific + // tag when encoded unquoted. If it doesn't, + // there's no need to quote it. + rtag, _ := resolve("", s) + canUsePlain = rtag == yaml_STR_TAG && !isBase60Float(s) + } + // Note: it's possible for user code to emit invalid YAML + // if they explicitly specify a tag and a string containing + // text that's incompatible with that tag. + switch { + case strings.Contains(s, "\n"): + style = yaml_LITERAL_SCALAR_STYLE + case canUsePlain: + style = yaml_PLAIN_SCALAR_STYLE + default: + style = yaml_DOUBLE_QUOTED_SCALAR_STYLE + } + e.emitScalar(s, "", tag, style) +} + +func (e *encoder) boolv(tag string, in reflect.Value) { + var s string + if in.Bool() { + s = "true" + } else { + s = "false" + } + e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE) +} + +func (e *encoder) intv(tag string, in reflect.Value) { + s := strconv.FormatInt(in.Int(), 10) + e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE) +} + +func (e *encoder) uintv(tag string, in reflect.Value) { + s := strconv.FormatUint(in.Uint(), 10) + e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE) +} + +func (e *encoder) timev(tag string, in reflect.Value) { + t := in.Interface().(time.Time) + s := t.Format(time.RFC3339Nano) + e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE) +} + +func (e *encoder) floatv(tag string, in reflect.Value) { + // Issue #352: When formatting, use the precision of the underlying value + precision := 64 + if in.Kind() == reflect.Float32 { + precision = 32 + } + + s := strconv.FormatFloat(in.Float(), 'g', -1, precision) + switch s { + case "+Inf": + s = ".inf" + case "-Inf": + s = "-.inf" + case "NaN": + s = ".nan" + } + e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE) +} + +func (e *encoder) nilv() { + e.emitScalar("null", "", "", yaml_PLAIN_SCALAR_STYLE) +} + +func (e *encoder) emitScalar(value, anchor, tag string, style yaml_scalar_style_t) { + implicit := tag == "" + e.must(yaml_scalar_event_initialize(&e.event, []byte(anchor), []byte(tag), []byte(value), implicit, implicit, style)) + e.emit() +} diff --git a/vendor/gopkg.in/yaml.v2/parserc.go b/vendor/gopkg.in/yaml.v2/parserc.go new file mode 100644 index 000000000..81d05dfe5 --- /dev/null +++ b/vendor/gopkg.in/yaml.v2/parserc.go @@ -0,0 +1,1095 @@ +package yaml + +import ( + "bytes" +) + +// The parser implements the following grammar: +// +// stream ::= STREAM-START implicit_document? explicit_document* STREAM-END +// implicit_document ::= block_node DOCUMENT-END* +// explicit_document ::= DIRECTIVE* DOCUMENT-START block_node? DOCUMENT-END* +// block_node_or_indentless_sequence ::= +// ALIAS +// | properties (block_content | indentless_block_sequence)? +// | block_content +// | indentless_block_sequence +// block_node ::= ALIAS +// | properties block_content? +// | block_content +// flow_node ::= ALIAS +// | properties flow_content? +// | flow_content +// properties ::= TAG ANCHOR? | ANCHOR TAG? +// block_content ::= block_collection | flow_collection | SCALAR +// flow_content ::= flow_collection | SCALAR +// block_collection ::= block_sequence | block_mapping +// flow_collection ::= flow_sequence | flow_mapping +// block_sequence ::= BLOCK-SEQUENCE-START (BLOCK-ENTRY block_node?)* BLOCK-END +// indentless_sequence ::= (BLOCK-ENTRY block_node?)+ +// block_mapping ::= BLOCK-MAPPING_START +// ((KEY block_node_or_indentless_sequence?)? +// (VALUE block_node_or_indentless_sequence?)?)* +// BLOCK-END +// flow_sequence ::= FLOW-SEQUENCE-START +// (flow_sequence_entry FLOW-ENTRY)* +// flow_sequence_entry? +// FLOW-SEQUENCE-END +// flow_sequence_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? +// flow_mapping ::= FLOW-MAPPING-START +// (flow_mapping_entry FLOW-ENTRY)* +// flow_mapping_entry? +// FLOW-MAPPING-END +// flow_mapping_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? + +// Peek the next token in the token queue. +func peek_token(parser *yaml_parser_t) *yaml_token_t { + if parser.token_available || yaml_parser_fetch_more_tokens(parser) { + return &parser.tokens[parser.tokens_head] + } + return nil +} + +// Remove the next token from the queue (must be called after peek_token). +func skip_token(parser *yaml_parser_t) { + parser.token_available = false + parser.tokens_parsed++ + parser.stream_end_produced = parser.tokens[parser.tokens_head].typ == yaml_STREAM_END_TOKEN + parser.tokens_head++ +} + +// Get the next event. +func yaml_parser_parse(parser *yaml_parser_t, event *yaml_event_t) bool { + // Erase the event object. + *event = yaml_event_t{} + + // No events after the end of the stream or error. + if parser.stream_end_produced || parser.error != yaml_NO_ERROR || parser.state == yaml_PARSE_END_STATE { + return true + } + + // Generate the next event. + return yaml_parser_state_machine(parser, event) +} + +// Set parser error. +func yaml_parser_set_parser_error(parser *yaml_parser_t, problem string, problem_mark yaml_mark_t) bool { + parser.error = yaml_PARSER_ERROR + parser.problem = problem + parser.problem_mark = problem_mark + return false +} + +func yaml_parser_set_parser_error_context(parser *yaml_parser_t, context string, context_mark yaml_mark_t, problem string, problem_mark yaml_mark_t) bool { + parser.error = yaml_PARSER_ERROR + parser.context = context + parser.context_mark = context_mark + parser.problem = problem + parser.problem_mark = problem_mark + return false +} + +// State dispatcher. +func yaml_parser_state_machine(parser *yaml_parser_t, event *yaml_event_t) bool { + //trace("yaml_parser_state_machine", "state:", parser.state.String()) + + switch parser.state { + case yaml_PARSE_STREAM_START_STATE: + return yaml_parser_parse_stream_start(parser, event) + + case yaml_PARSE_IMPLICIT_DOCUMENT_START_STATE: + return yaml_parser_parse_document_start(parser, event, true) + + case yaml_PARSE_DOCUMENT_START_STATE: + return yaml_parser_parse_document_start(parser, event, false) + + case yaml_PARSE_DOCUMENT_CONTENT_STATE: + return yaml_parser_parse_document_content(parser, event) + + case yaml_PARSE_DOCUMENT_END_STATE: + return yaml_parser_parse_document_end(parser, event) + + case yaml_PARSE_BLOCK_NODE_STATE: + return yaml_parser_parse_node(parser, event, true, false) + + case yaml_PARSE_BLOCK_NODE_OR_INDENTLESS_SEQUENCE_STATE: + return yaml_parser_parse_node(parser, event, true, true) + + case yaml_PARSE_FLOW_NODE_STATE: + return yaml_parser_parse_node(parser, event, false, false) + + case yaml_PARSE_BLOCK_SEQUENCE_FIRST_ENTRY_STATE: + return yaml_parser_parse_block_sequence_entry(parser, event, true) + + case yaml_PARSE_BLOCK_SEQUENCE_ENTRY_STATE: + return yaml_parser_parse_block_sequence_entry(parser, event, false) + + case yaml_PARSE_INDENTLESS_SEQUENCE_ENTRY_STATE: + return yaml_parser_parse_indentless_sequence_entry(parser, event) + + case yaml_PARSE_BLOCK_MAPPING_FIRST_KEY_STATE: + return yaml_parser_parse_block_mapping_key(parser, event, true) + + case yaml_PARSE_BLOCK_MAPPING_KEY_STATE: + return yaml_parser_parse_block_mapping_key(parser, event, false) + + case yaml_PARSE_BLOCK_MAPPING_VALUE_STATE: + return yaml_parser_parse_block_mapping_value(parser, event) + + case yaml_PARSE_FLOW_SEQUENCE_FIRST_ENTRY_STATE: + return yaml_parser_parse_flow_sequence_entry(parser, event, true) + + case yaml_PARSE_FLOW_SEQUENCE_ENTRY_STATE: + return yaml_parser_parse_flow_sequence_entry(parser, event, false) + + case yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_KEY_STATE: + return yaml_parser_parse_flow_sequence_entry_mapping_key(parser, event) + + case yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_VALUE_STATE: + return yaml_parser_parse_flow_sequence_entry_mapping_value(parser, event) + + case yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_END_STATE: + return yaml_parser_parse_flow_sequence_entry_mapping_end(parser, event) + + case yaml_PARSE_FLOW_MAPPING_FIRST_KEY_STATE: + return yaml_parser_parse_flow_mapping_key(parser, event, true) + + case yaml_PARSE_FLOW_MAPPING_KEY_STATE: + return yaml_parser_parse_flow_mapping_key(parser, event, false) + + case yaml_PARSE_FLOW_MAPPING_VALUE_STATE: + return yaml_parser_parse_flow_mapping_value(parser, event, false) + + case yaml_PARSE_FLOW_MAPPING_EMPTY_VALUE_STATE: + return yaml_parser_parse_flow_mapping_value(parser, event, true) + + default: + panic("invalid parser state") + } +} + +// Parse the production: +// stream ::= STREAM-START implicit_document? explicit_document* STREAM-END +// ************ +func yaml_parser_parse_stream_start(parser *yaml_parser_t, event *yaml_event_t) bool { + token := peek_token(parser) + if token == nil { + return false + } + if token.typ != yaml_STREAM_START_TOKEN { + return yaml_parser_set_parser_error(parser, "did not find expected ", token.start_mark) + } + parser.state = yaml_PARSE_IMPLICIT_DOCUMENT_START_STATE + *event = yaml_event_t{ + typ: yaml_STREAM_START_EVENT, + start_mark: token.start_mark, + end_mark: token.end_mark, + encoding: token.encoding, + } + skip_token(parser) + return true +} + +// Parse the productions: +// implicit_document ::= block_node DOCUMENT-END* +// * +// explicit_document ::= DIRECTIVE* DOCUMENT-START block_node? DOCUMENT-END* +// ************************* +func yaml_parser_parse_document_start(parser *yaml_parser_t, event *yaml_event_t, implicit bool) bool { + + token := peek_token(parser) + if token == nil { + return false + } + + // Parse extra document end indicators. + if !implicit { + for token.typ == yaml_DOCUMENT_END_TOKEN { + skip_token(parser) + token = peek_token(parser) + if token == nil { + return false + } + } + } + + if implicit && token.typ != yaml_VERSION_DIRECTIVE_TOKEN && + token.typ != yaml_TAG_DIRECTIVE_TOKEN && + token.typ != yaml_DOCUMENT_START_TOKEN && + token.typ != yaml_STREAM_END_TOKEN { + // Parse an implicit document. + if !yaml_parser_process_directives(parser, nil, nil) { + return false + } + parser.states = append(parser.states, yaml_PARSE_DOCUMENT_END_STATE) + parser.state = yaml_PARSE_BLOCK_NODE_STATE + + *event = yaml_event_t{ + typ: yaml_DOCUMENT_START_EVENT, + start_mark: token.start_mark, + end_mark: token.end_mark, + } + + } else if token.typ != yaml_STREAM_END_TOKEN { + // Parse an explicit document. + var version_directive *yaml_version_directive_t + var tag_directives []yaml_tag_directive_t + start_mark := token.start_mark + if !yaml_parser_process_directives(parser, &version_directive, &tag_directives) { + return false + } + token = peek_token(parser) + if token == nil { + return false + } + if token.typ != yaml_DOCUMENT_START_TOKEN { + yaml_parser_set_parser_error(parser, + "did not find expected ", token.start_mark) + return false + } + parser.states = append(parser.states, yaml_PARSE_DOCUMENT_END_STATE) + parser.state = yaml_PARSE_DOCUMENT_CONTENT_STATE + end_mark := token.end_mark + + *event = yaml_event_t{ + typ: yaml_DOCUMENT_START_EVENT, + start_mark: start_mark, + end_mark: end_mark, + version_directive: version_directive, + tag_directives: tag_directives, + implicit: false, + } + skip_token(parser) + + } else { + // Parse the stream end. + parser.state = yaml_PARSE_END_STATE + *event = yaml_event_t{ + typ: yaml_STREAM_END_EVENT, + start_mark: token.start_mark, + end_mark: token.end_mark, + } + skip_token(parser) + } + + return true +} + +// Parse the productions: +// explicit_document ::= DIRECTIVE* DOCUMENT-START block_node? DOCUMENT-END* +// *********** +// +func yaml_parser_parse_document_content(parser *yaml_parser_t, event *yaml_event_t) bool { + token := peek_token(parser) + if token == nil { + return false + } + if token.typ == yaml_VERSION_DIRECTIVE_TOKEN || + token.typ == yaml_TAG_DIRECTIVE_TOKEN || + token.typ == yaml_DOCUMENT_START_TOKEN || + token.typ == yaml_DOCUMENT_END_TOKEN || + token.typ == yaml_STREAM_END_TOKEN { + parser.state = parser.states[len(parser.states)-1] + parser.states = parser.states[:len(parser.states)-1] + return yaml_parser_process_empty_scalar(parser, event, + token.start_mark) + } + return yaml_parser_parse_node(parser, event, true, false) +} + +// Parse the productions: +// implicit_document ::= block_node DOCUMENT-END* +// ************* +// explicit_document ::= DIRECTIVE* DOCUMENT-START block_node? DOCUMENT-END* +// +func yaml_parser_parse_document_end(parser *yaml_parser_t, event *yaml_event_t) bool { + token := peek_token(parser) + if token == nil { + return false + } + + start_mark := token.start_mark + end_mark := token.start_mark + + implicit := true + if token.typ == yaml_DOCUMENT_END_TOKEN { + end_mark = token.end_mark + skip_token(parser) + implicit = false + } + + parser.tag_directives = parser.tag_directives[:0] + + parser.state = yaml_PARSE_DOCUMENT_START_STATE + *event = yaml_event_t{ + typ: yaml_DOCUMENT_END_EVENT, + start_mark: start_mark, + end_mark: end_mark, + implicit: implicit, + } + return true +} + +// Parse the productions: +// block_node_or_indentless_sequence ::= +// ALIAS +// ***** +// | properties (block_content | indentless_block_sequence)? +// ********** * +// | block_content | indentless_block_sequence +// * +// block_node ::= ALIAS +// ***** +// | properties block_content? +// ********** * +// | block_content +// * +// flow_node ::= ALIAS +// ***** +// | properties flow_content? +// ********** * +// | flow_content +// * +// properties ::= TAG ANCHOR? | ANCHOR TAG? +// ************************* +// block_content ::= block_collection | flow_collection | SCALAR +// ****** +// flow_content ::= flow_collection | SCALAR +// ****** +func yaml_parser_parse_node(parser *yaml_parser_t, event *yaml_event_t, block, indentless_sequence bool) bool { + //defer trace("yaml_parser_parse_node", "block:", block, "indentless_sequence:", indentless_sequence)() + + token := peek_token(parser) + if token == nil { + return false + } + + if token.typ == yaml_ALIAS_TOKEN { + parser.state = parser.states[len(parser.states)-1] + parser.states = parser.states[:len(parser.states)-1] + *event = yaml_event_t{ + typ: yaml_ALIAS_EVENT, + start_mark: token.start_mark, + end_mark: token.end_mark, + anchor: token.value, + } + skip_token(parser) + return true + } + + start_mark := token.start_mark + end_mark := token.start_mark + + var tag_token bool + var tag_handle, tag_suffix, anchor []byte + var tag_mark yaml_mark_t + if token.typ == yaml_ANCHOR_TOKEN { + anchor = token.value + start_mark = token.start_mark + end_mark = token.end_mark + skip_token(parser) + token = peek_token(parser) + if token == nil { + return false + } + if token.typ == yaml_TAG_TOKEN { + tag_token = true + tag_handle = token.value + tag_suffix = token.suffix + tag_mark = token.start_mark + end_mark = token.end_mark + skip_token(parser) + token = peek_token(parser) + if token == nil { + return false + } + } + } else if token.typ == yaml_TAG_TOKEN { + tag_token = true + tag_handle = token.value + tag_suffix = token.suffix + start_mark = token.start_mark + tag_mark = token.start_mark + end_mark = token.end_mark + skip_token(parser) + token = peek_token(parser) + if token == nil { + return false + } + if token.typ == yaml_ANCHOR_TOKEN { + anchor = token.value + end_mark = token.end_mark + skip_token(parser) + token = peek_token(parser) + if token == nil { + return false + } + } + } + + var tag []byte + if tag_token { + if len(tag_handle) == 0 { + tag = tag_suffix + tag_suffix = nil + } else { + for i := range parser.tag_directives { + if bytes.Equal(parser.tag_directives[i].handle, tag_handle) { + tag = append([]byte(nil), parser.tag_directives[i].prefix...) + tag = append(tag, tag_suffix...) + break + } + } + if len(tag) == 0 { + yaml_parser_set_parser_error_context(parser, + "while parsing a node", start_mark, + "found undefined tag handle", tag_mark) + return false + } + } + } + + implicit := len(tag) == 0 + if indentless_sequence && token.typ == yaml_BLOCK_ENTRY_TOKEN { + end_mark = token.end_mark + parser.state = yaml_PARSE_INDENTLESS_SEQUENCE_ENTRY_STATE + *event = yaml_event_t{ + typ: yaml_SEQUENCE_START_EVENT, + start_mark: start_mark, + end_mark: end_mark, + anchor: anchor, + tag: tag, + implicit: implicit, + style: yaml_style_t(yaml_BLOCK_SEQUENCE_STYLE), + } + return true + } + if token.typ == yaml_SCALAR_TOKEN { + var plain_implicit, quoted_implicit bool + end_mark = token.end_mark + if (len(tag) == 0 && token.style == yaml_PLAIN_SCALAR_STYLE) || (len(tag) == 1 && tag[0] == '!') { + plain_implicit = true + } else if len(tag) == 0 { + quoted_implicit = true + } + parser.state = parser.states[len(parser.states)-1] + parser.states = parser.states[:len(parser.states)-1] + + *event = yaml_event_t{ + typ: yaml_SCALAR_EVENT, + start_mark: start_mark, + end_mark: end_mark, + anchor: anchor, + tag: tag, + value: token.value, + implicit: plain_implicit, + quoted_implicit: quoted_implicit, + style: yaml_style_t(token.style), + } + skip_token(parser) + return true + } + if token.typ == yaml_FLOW_SEQUENCE_START_TOKEN { + // [Go] Some of the events below can be merged as they differ only on style. + end_mark = token.end_mark + parser.state = yaml_PARSE_FLOW_SEQUENCE_FIRST_ENTRY_STATE + *event = yaml_event_t{ + typ: yaml_SEQUENCE_START_EVENT, + start_mark: start_mark, + end_mark: end_mark, + anchor: anchor, + tag: tag, + implicit: implicit, + style: yaml_style_t(yaml_FLOW_SEQUENCE_STYLE), + } + return true + } + if token.typ == yaml_FLOW_MAPPING_START_TOKEN { + end_mark = token.end_mark + parser.state = yaml_PARSE_FLOW_MAPPING_FIRST_KEY_STATE + *event = yaml_event_t{ + typ: yaml_MAPPING_START_EVENT, + start_mark: start_mark, + end_mark: end_mark, + anchor: anchor, + tag: tag, + implicit: implicit, + style: yaml_style_t(yaml_FLOW_MAPPING_STYLE), + } + return true + } + if block && token.typ == yaml_BLOCK_SEQUENCE_START_TOKEN { + end_mark = token.end_mark + parser.state = yaml_PARSE_BLOCK_SEQUENCE_FIRST_ENTRY_STATE + *event = yaml_event_t{ + typ: yaml_SEQUENCE_START_EVENT, + start_mark: start_mark, + end_mark: end_mark, + anchor: anchor, + tag: tag, + implicit: implicit, + style: yaml_style_t(yaml_BLOCK_SEQUENCE_STYLE), + } + return true + } + if block && token.typ == yaml_BLOCK_MAPPING_START_TOKEN { + end_mark = token.end_mark + parser.state = yaml_PARSE_BLOCK_MAPPING_FIRST_KEY_STATE + *event = yaml_event_t{ + typ: yaml_MAPPING_START_EVENT, + start_mark: start_mark, + end_mark: end_mark, + anchor: anchor, + tag: tag, + implicit: implicit, + style: yaml_style_t(yaml_BLOCK_MAPPING_STYLE), + } + return true + } + if len(anchor) > 0 || len(tag) > 0 { + parser.state = parser.states[len(parser.states)-1] + parser.states = parser.states[:len(parser.states)-1] + + *event = yaml_event_t{ + typ: yaml_SCALAR_EVENT, + start_mark: start_mark, + end_mark: end_mark, + anchor: anchor, + tag: tag, + implicit: implicit, + quoted_implicit: false, + style: yaml_style_t(yaml_PLAIN_SCALAR_STYLE), + } + return true + } + + context := "while parsing a flow node" + if block { + context = "while parsing a block node" + } + yaml_parser_set_parser_error_context(parser, context, start_mark, + "did not find expected node content", token.start_mark) + return false +} + +// Parse the productions: +// block_sequence ::= BLOCK-SEQUENCE-START (BLOCK-ENTRY block_node?)* BLOCK-END +// ******************** *********** * ********* +// +func yaml_parser_parse_block_sequence_entry(parser *yaml_parser_t, event *yaml_event_t, first bool) bool { + if first { + token := peek_token(parser) + parser.marks = append(parser.marks, token.start_mark) + skip_token(parser) + } + + token := peek_token(parser) + if token == nil { + return false + } + + if token.typ == yaml_BLOCK_ENTRY_TOKEN { + mark := token.end_mark + skip_token(parser) + token = peek_token(parser) + if token == nil { + return false + } + if token.typ != yaml_BLOCK_ENTRY_TOKEN && token.typ != yaml_BLOCK_END_TOKEN { + parser.states = append(parser.states, yaml_PARSE_BLOCK_SEQUENCE_ENTRY_STATE) + return yaml_parser_parse_node(parser, event, true, false) + } else { + parser.state = yaml_PARSE_BLOCK_SEQUENCE_ENTRY_STATE + return yaml_parser_process_empty_scalar(parser, event, mark) + } + } + if token.typ == yaml_BLOCK_END_TOKEN { + parser.state = parser.states[len(parser.states)-1] + parser.states = parser.states[:len(parser.states)-1] + parser.marks = parser.marks[:len(parser.marks)-1] + + *event = yaml_event_t{ + typ: yaml_SEQUENCE_END_EVENT, + start_mark: token.start_mark, + end_mark: token.end_mark, + } + + skip_token(parser) + return true + } + + context_mark := parser.marks[len(parser.marks)-1] + parser.marks = parser.marks[:len(parser.marks)-1] + return yaml_parser_set_parser_error_context(parser, + "while parsing a block collection", context_mark, + "did not find expected '-' indicator", token.start_mark) +} + +// Parse the productions: +// indentless_sequence ::= (BLOCK-ENTRY block_node?)+ +// *********** * +func yaml_parser_parse_indentless_sequence_entry(parser *yaml_parser_t, event *yaml_event_t) bool { + token := peek_token(parser) + if token == nil { + return false + } + + if token.typ == yaml_BLOCK_ENTRY_TOKEN { + mark := token.end_mark + skip_token(parser) + token = peek_token(parser) + if token == nil { + return false + } + if token.typ != yaml_BLOCK_ENTRY_TOKEN && + token.typ != yaml_KEY_TOKEN && + token.typ != yaml_VALUE_TOKEN && + token.typ != yaml_BLOCK_END_TOKEN { + parser.states = append(parser.states, yaml_PARSE_INDENTLESS_SEQUENCE_ENTRY_STATE) + return yaml_parser_parse_node(parser, event, true, false) + } + parser.state = yaml_PARSE_INDENTLESS_SEQUENCE_ENTRY_STATE + return yaml_parser_process_empty_scalar(parser, event, mark) + } + parser.state = parser.states[len(parser.states)-1] + parser.states = parser.states[:len(parser.states)-1] + + *event = yaml_event_t{ + typ: yaml_SEQUENCE_END_EVENT, + start_mark: token.start_mark, + end_mark: token.start_mark, // [Go] Shouldn't this be token.end_mark? + } + return true +} + +// Parse the productions: +// block_mapping ::= BLOCK-MAPPING_START +// ******************* +// ((KEY block_node_or_indentless_sequence?)? +// *** * +// (VALUE block_node_or_indentless_sequence?)?)* +// +// BLOCK-END +// ********* +// +func yaml_parser_parse_block_mapping_key(parser *yaml_parser_t, event *yaml_event_t, first bool) bool { + if first { + token := peek_token(parser) + parser.marks = append(parser.marks, token.start_mark) + skip_token(parser) + } + + token := peek_token(parser) + if token == nil { + return false + } + + if token.typ == yaml_KEY_TOKEN { + mark := token.end_mark + skip_token(parser) + token = peek_token(parser) + if token == nil { + return false + } + if token.typ != yaml_KEY_TOKEN && + token.typ != yaml_VALUE_TOKEN && + token.typ != yaml_BLOCK_END_TOKEN { + parser.states = append(parser.states, yaml_PARSE_BLOCK_MAPPING_VALUE_STATE) + return yaml_parser_parse_node(parser, event, true, true) + } else { + parser.state = yaml_PARSE_BLOCK_MAPPING_VALUE_STATE + return yaml_parser_process_empty_scalar(parser, event, mark) + } + } else if token.typ == yaml_BLOCK_END_TOKEN { + parser.state = parser.states[len(parser.states)-1] + parser.states = parser.states[:len(parser.states)-1] + parser.marks = parser.marks[:len(parser.marks)-1] + *event = yaml_event_t{ + typ: yaml_MAPPING_END_EVENT, + start_mark: token.start_mark, + end_mark: token.end_mark, + } + skip_token(parser) + return true + } + + context_mark := parser.marks[len(parser.marks)-1] + parser.marks = parser.marks[:len(parser.marks)-1] + return yaml_parser_set_parser_error_context(parser, + "while parsing a block mapping", context_mark, + "did not find expected key", token.start_mark) +} + +// Parse the productions: +// block_mapping ::= BLOCK-MAPPING_START +// +// ((KEY block_node_or_indentless_sequence?)? +// +// (VALUE block_node_or_indentless_sequence?)?)* +// ***** * +// BLOCK-END +// +// +func yaml_parser_parse_block_mapping_value(parser *yaml_parser_t, event *yaml_event_t) bool { + token := peek_token(parser) + if token == nil { + return false + } + if token.typ == yaml_VALUE_TOKEN { + mark := token.end_mark + skip_token(parser) + token = peek_token(parser) + if token == nil { + return false + } + if token.typ != yaml_KEY_TOKEN && + token.typ != yaml_VALUE_TOKEN && + token.typ != yaml_BLOCK_END_TOKEN { + parser.states = append(parser.states, yaml_PARSE_BLOCK_MAPPING_KEY_STATE) + return yaml_parser_parse_node(parser, event, true, true) + } + parser.state = yaml_PARSE_BLOCK_MAPPING_KEY_STATE + return yaml_parser_process_empty_scalar(parser, event, mark) + } + parser.state = yaml_PARSE_BLOCK_MAPPING_KEY_STATE + return yaml_parser_process_empty_scalar(parser, event, token.start_mark) +} + +// Parse the productions: +// flow_sequence ::= FLOW-SEQUENCE-START +// ******************* +// (flow_sequence_entry FLOW-ENTRY)* +// * ********** +// flow_sequence_entry? +// * +// FLOW-SEQUENCE-END +// ***************** +// flow_sequence_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? +// * +// +func yaml_parser_parse_flow_sequence_entry(parser *yaml_parser_t, event *yaml_event_t, first bool) bool { + if first { + token := peek_token(parser) + parser.marks = append(parser.marks, token.start_mark) + skip_token(parser) + } + token := peek_token(parser) + if token == nil { + return false + } + if token.typ != yaml_FLOW_SEQUENCE_END_TOKEN { + if !first { + if token.typ == yaml_FLOW_ENTRY_TOKEN { + skip_token(parser) + token = peek_token(parser) + if token == nil { + return false + } + } else { + context_mark := parser.marks[len(parser.marks)-1] + parser.marks = parser.marks[:len(parser.marks)-1] + return yaml_parser_set_parser_error_context(parser, + "while parsing a flow sequence", context_mark, + "did not find expected ',' or ']'", token.start_mark) + } + } + + if token.typ == yaml_KEY_TOKEN { + parser.state = yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_KEY_STATE + *event = yaml_event_t{ + typ: yaml_MAPPING_START_EVENT, + start_mark: token.start_mark, + end_mark: token.end_mark, + implicit: true, + style: yaml_style_t(yaml_FLOW_MAPPING_STYLE), + } + skip_token(parser) + return true + } else if token.typ != yaml_FLOW_SEQUENCE_END_TOKEN { + parser.states = append(parser.states, yaml_PARSE_FLOW_SEQUENCE_ENTRY_STATE) + return yaml_parser_parse_node(parser, event, false, false) + } + } + + parser.state = parser.states[len(parser.states)-1] + parser.states = parser.states[:len(parser.states)-1] + parser.marks = parser.marks[:len(parser.marks)-1] + + *event = yaml_event_t{ + typ: yaml_SEQUENCE_END_EVENT, + start_mark: token.start_mark, + end_mark: token.end_mark, + } + + skip_token(parser) + return true +} + +// +// Parse the productions: +// flow_sequence_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? +// *** * +// +func yaml_parser_parse_flow_sequence_entry_mapping_key(parser *yaml_parser_t, event *yaml_event_t) bool { + token := peek_token(parser) + if token == nil { + return false + } + if token.typ != yaml_VALUE_TOKEN && + token.typ != yaml_FLOW_ENTRY_TOKEN && + token.typ != yaml_FLOW_SEQUENCE_END_TOKEN { + parser.states = append(parser.states, yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_VALUE_STATE) + return yaml_parser_parse_node(parser, event, false, false) + } + mark := token.end_mark + skip_token(parser) + parser.state = yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_VALUE_STATE + return yaml_parser_process_empty_scalar(parser, event, mark) +} + +// Parse the productions: +// flow_sequence_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? +// ***** * +// +func yaml_parser_parse_flow_sequence_entry_mapping_value(parser *yaml_parser_t, event *yaml_event_t) bool { + token := peek_token(parser) + if token == nil { + return false + } + if token.typ == yaml_VALUE_TOKEN { + skip_token(parser) + token := peek_token(parser) + if token == nil { + return false + } + if token.typ != yaml_FLOW_ENTRY_TOKEN && token.typ != yaml_FLOW_SEQUENCE_END_TOKEN { + parser.states = append(parser.states, yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_END_STATE) + return yaml_parser_parse_node(parser, event, false, false) + } + } + parser.state = yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_END_STATE + return yaml_parser_process_empty_scalar(parser, event, token.start_mark) +} + +// Parse the productions: +// flow_sequence_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? +// * +// +func yaml_parser_parse_flow_sequence_entry_mapping_end(parser *yaml_parser_t, event *yaml_event_t) bool { + token := peek_token(parser) + if token == nil { + return false + } + parser.state = yaml_PARSE_FLOW_SEQUENCE_ENTRY_STATE + *event = yaml_event_t{ + typ: yaml_MAPPING_END_EVENT, + start_mark: token.start_mark, + end_mark: token.start_mark, // [Go] Shouldn't this be end_mark? + } + return true +} + +// Parse the productions: +// flow_mapping ::= FLOW-MAPPING-START +// ****************** +// (flow_mapping_entry FLOW-ENTRY)* +// * ********** +// flow_mapping_entry? +// ****************** +// FLOW-MAPPING-END +// **************** +// flow_mapping_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? +// * *** * +// +func yaml_parser_parse_flow_mapping_key(parser *yaml_parser_t, event *yaml_event_t, first bool) bool { + if first { + token := peek_token(parser) + parser.marks = append(parser.marks, token.start_mark) + skip_token(parser) + } + + token := peek_token(parser) + if token == nil { + return false + } + + if token.typ != yaml_FLOW_MAPPING_END_TOKEN { + if !first { + if token.typ == yaml_FLOW_ENTRY_TOKEN { + skip_token(parser) + token = peek_token(parser) + if token == nil { + return false + } + } else { + context_mark := parser.marks[len(parser.marks)-1] + parser.marks = parser.marks[:len(parser.marks)-1] + return yaml_parser_set_parser_error_context(parser, + "while parsing a flow mapping", context_mark, + "did not find expected ',' or '}'", token.start_mark) + } + } + + if token.typ == yaml_KEY_TOKEN { + skip_token(parser) + token = peek_token(parser) + if token == nil { + return false + } + if token.typ != yaml_VALUE_TOKEN && + token.typ != yaml_FLOW_ENTRY_TOKEN && + token.typ != yaml_FLOW_MAPPING_END_TOKEN { + parser.states = append(parser.states, yaml_PARSE_FLOW_MAPPING_VALUE_STATE) + return yaml_parser_parse_node(parser, event, false, false) + } else { + parser.state = yaml_PARSE_FLOW_MAPPING_VALUE_STATE + return yaml_parser_process_empty_scalar(parser, event, token.start_mark) + } + } else if token.typ != yaml_FLOW_MAPPING_END_TOKEN { + parser.states = append(parser.states, yaml_PARSE_FLOW_MAPPING_EMPTY_VALUE_STATE) + return yaml_parser_parse_node(parser, event, false, false) + } + } + + parser.state = parser.states[len(parser.states)-1] + parser.states = parser.states[:len(parser.states)-1] + parser.marks = parser.marks[:len(parser.marks)-1] + *event = yaml_event_t{ + typ: yaml_MAPPING_END_EVENT, + start_mark: token.start_mark, + end_mark: token.end_mark, + } + skip_token(parser) + return true +} + +// Parse the productions: +// flow_mapping_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? +// * ***** * +// +func yaml_parser_parse_flow_mapping_value(parser *yaml_parser_t, event *yaml_event_t, empty bool) bool { + token := peek_token(parser) + if token == nil { + return false + } + if empty { + parser.state = yaml_PARSE_FLOW_MAPPING_KEY_STATE + return yaml_parser_process_empty_scalar(parser, event, token.start_mark) + } + if token.typ == yaml_VALUE_TOKEN { + skip_token(parser) + token = peek_token(parser) + if token == nil { + return false + } + if token.typ != yaml_FLOW_ENTRY_TOKEN && token.typ != yaml_FLOW_MAPPING_END_TOKEN { + parser.states = append(parser.states, yaml_PARSE_FLOW_MAPPING_KEY_STATE) + return yaml_parser_parse_node(parser, event, false, false) + } + } + parser.state = yaml_PARSE_FLOW_MAPPING_KEY_STATE + return yaml_parser_process_empty_scalar(parser, event, token.start_mark) +} + +// Generate an empty scalar event. +func yaml_parser_process_empty_scalar(parser *yaml_parser_t, event *yaml_event_t, mark yaml_mark_t) bool { + *event = yaml_event_t{ + typ: yaml_SCALAR_EVENT, + start_mark: mark, + end_mark: mark, + value: nil, // Empty + implicit: true, + style: yaml_style_t(yaml_PLAIN_SCALAR_STYLE), + } + return true +} + +var default_tag_directives = []yaml_tag_directive_t{ + {[]byte("!"), []byte("!")}, + {[]byte("!!"), []byte("tag:yaml.org,2002:")}, +} + +// Parse directives. +func yaml_parser_process_directives(parser *yaml_parser_t, + version_directive_ref **yaml_version_directive_t, + tag_directives_ref *[]yaml_tag_directive_t) bool { + + var version_directive *yaml_version_directive_t + var tag_directives []yaml_tag_directive_t + + token := peek_token(parser) + if token == nil { + return false + } + + for token.typ == yaml_VERSION_DIRECTIVE_TOKEN || token.typ == yaml_TAG_DIRECTIVE_TOKEN { + if token.typ == yaml_VERSION_DIRECTIVE_TOKEN { + if version_directive != nil { + yaml_parser_set_parser_error(parser, + "found duplicate %YAML directive", token.start_mark) + return false + } + if token.major != 1 || token.minor != 1 { + yaml_parser_set_parser_error(parser, + "found incompatible YAML document", token.start_mark) + return false + } + version_directive = &yaml_version_directive_t{ + major: token.major, + minor: token.minor, + } + } else if token.typ == yaml_TAG_DIRECTIVE_TOKEN { + value := yaml_tag_directive_t{ + handle: token.value, + prefix: token.prefix, + } + if !yaml_parser_append_tag_directive(parser, value, false, token.start_mark) { + return false + } + tag_directives = append(tag_directives, value) + } + + skip_token(parser) + token = peek_token(parser) + if token == nil { + return false + } + } + + for i := range default_tag_directives { + if !yaml_parser_append_tag_directive(parser, default_tag_directives[i], true, token.start_mark) { + return false + } + } + + if version_directive_ref != nil { + *version_directive_ref = version_directive + } + if tag_directives_ref != nil { + *tag_directives_ref = tag_directives + } + return true +} + +// Append a tag directive to the directives stack. +func yaml_parser_append_tag_directive(parser *yaml_parser_t, value yaml_tag_directive_t, allow_duplicates bool, mark yaml_mark_t) bool { + for i := range parser.tag_directives { + if bytes.Equal(value.handle, parser.tag_directives[i].handle) { + if allow_duplicates { + return true + } + return yaml_parser_set_parser_error(parser, "found duplicate %TAG directive", mark) + } + } + + // [Go] I suspect the copy is unnecessary. This was likely done + // because there was no way to track ownership of the data. + value_copy := yaml_tag_directive_t{ + handle: make([]byte, len(value.handle)), + prefix: make([]byte, len(value.prefix)), + } + copy(value_copy.handle, value.handle) + copy(value_copy.prefix, value.prefix) + parser.tag_directives = append(parser.tag_directives, value_copy) + return true +} diff --git a/vendor/gopkg.in/yaml.v2/readerc.go b/vendor/gopkg.in/yaml.v2/readerc.go new file mode 100644 index 000000000..7c1f5fac3 --- /dev/null +++ b/vendor/gopkg.in/yaml.v2/readerc.go @@ -0,0 +1,412 @@ +package yaml + +import ( + "io" +) + +// Set the reader error and return 0. +func yaml_parser_set_reader_error(parser *yaml_parser_t, problem string, offset int, value int) bool { + parser.error = yaml_READER_ERROR + parser.problem = problem + parser.problem_offset = offset + parser.problem_value = value + return false +} + +// Byte order marks. +const ( + bom_UTF8 = "\xef\xbb\xbf" + bom_UTF16LE = "\xff\xfe" + bom_UTF16BE = "\xfe\xff" +) + +// Determine the input stream encoding by checking the BOM symbol. If no BOM is +// found, the UTF-8 encoding is assumed. Return 1 on success, 0 on failure. +func yaml_parser_determine_encoding(parser *yaml_parser_t) bool { + // Ensure that we had enough bytes in the raw buffer. + for !parser.eof && len(parser.raw_buffer)-parser.raw_buffer_pos < 3 { + if !yaml_parser_update_raw_buffer(parser) { + return false + } + } + + // Determine the encoding. + buf := parser.raw_buffer + pos := parser.raw_buffer_pos + avail := len(buf) - pos + if avail >= 2 && buf[pos] == bom_UTF16LE[0] && buf[pos+1] == bom_UTF16LE[1] { + parser.encoding = yaml_UTF16LE_ENCODING + parser.raw_buffer_pos += 2 + parser.offset += 2 + } else if avail >= 2 && buf[pos] == bom_UTF16BE[0] && buf[pos+1] == bom_UTF16BE[1] { + parser.encoding = yaml_UTF16BE_ENCODING + parser.raw_buffer_pos += 2 + parser.offset += 2 + } else if avail >= 3 && buf[pos] == bom_UTF8[0] && buf[pos+1] == bom_UTF8[1] && buf[pos+2] == bom_UTF8[2] { + parser.encoding = yaml_UTF8_ENCODING + parser.raw_buffer_pos += 3 + parser.offset += 3 + } else { + parser.encoding = yaml_UTF8_ENCODING + } + return true +} + +// Update the raw buffer. +func yaml_parser_update_raw_buffer(parser *yaml_parser_t) bool { + size_read := 0 + + // Return if the raw buffer is full. + if parser.raw_buffer_pos == 0 && len(parser.raw_buffer) == cap(parser.raw_buffer) { + return true + } + + // Return on EOF. + if parser.eof { + return true + } + + // Move the remaining bytes in the raw buffer to the beginning. + if parser.raw_buffer_pos > 0 && parser.raw_buffer_pos < len(parser.raw_buffer) { + copy(parser.raw_buffer, parser.raw_buffer[parser.raw_buffer_pos:]) + } + parser.raw_buffer = parser.raw_buffer[:len(parser.raw_buffer)-parser.raw_buffer_pos] + parser.raw_buffer_pos = 0 + + // Call the read handler to fill the buffer. + size_read, err := parser.read_handler(parser, parser.raw_buffer[len(parser.raw_buffer):cap(parser.raw_buffer)]) + parser.raw_buffer = parser.raw_buffer[:len(parser.raw_buffer)+size_read] + if err == io.EOF { + parser.eof = true + } else if err != nil { + return yaml_parser_set_reader_error(parser, "input error: "+err.Error(), parser.offset, -1) + } + return true +} + +// Ensure that the buffer contains at least `length` characters. +// Return true on success, false on failure. +// +// The length is supposed to be significantly less that the buffer size. +func yaml_parser_update_buffer(parser *yaml_parser_t, length int) bool { + if parser.read_handler == nil { + panic("read handler must be set") + } + + // [Go] This function was changed to guarantee the requested length size at EOF. + // The fact we need to do this is pretty awful, but the description above implies + // for that to be the case, and there are tests + + // If the EOF flag is set and the raw buffer is empty, do nothing. + if parser.eof && parser.raw_buffer_pos == len(parser.raw_buffer) { + // [Go] ACTUALLY! Read the documentation of this function above. + // This is just broken. To return true, we need to have the + // given length in the buffer. Not doing that means every single + // check that calls this function to make sure the buffer has a + // given length is Go) panicking; or C) accessing invalid memory. + //return true + } + + // Return if the buffer contains enough characters. + if parser.unread >= length { + return true + } + + // Determine the input encoding if it is not known yet. + if parser.encoding == yaml_ANY_ENCODING { + if !yaml_parser_determine_encoding(parser) { + return false + } + } + + // Move the unread characters to the beginning of the buffer. + buffer_len := len(parser.buffer) + if parser.buffer_pos > 0 && parser.buffer_pos < buffer_len { + copy(parser.buffer, parser.buffer[parser.buffer_pos:]) + buffer_len -= parser.buffer_pos + parser.buffer_pos = 0 + } else if parser.buffer_pos == buffer_len { + buffer_len = 0 + parser.buffer_pos = 0 + } + + // Open the whole buffer for writing, and cut it before returning. + parser.buffer = parser.buffer[:cap(parser.buffer)] + + // Fill the buffer until it has enough characters. + first := true + for parser.unread < length { + + // Fill the raw buffer if necessary. + if !first || parser.raw_buffer_pos == len(parser.raw_buffer) { + if !yaml_parser_update_raw_buffer(parser) { + parser.buffer = parser.buffer[:buffer_len] + return false + } + } + first = false + + // Decode the raw buffer. + inner: + for parser.raw_buffer_pos != len(parser.raw_buffer) { + var value rune + var width int + + raw_unread := len(parser.raw_buffer) - parser.raw_buffer_pos + + // Decode the next character. + switch parser.encoding { + case yaml_UTF8_ENCODING: + // Decode a UTF-8 character. Check RFC 3629 + // (http://www.ietf.org/rfc/rfc3629.txt) for more details. + // + // The following table (taken from the RFC) is used for + // decoding. + // + // Char. number range | UTF-8 octet sequence + // (hexadecimal) | (binary) + // --------------------+------------------------------------ + // 0000 0000-0000 007F | 0xxxxxxx + // 0000 0080-0000 07FF | 110xxxxx 10xxxxxx + // 0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx + // 0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx + // + // Additionally, the characters in the range 0xD800-0xDFFF + // are prohibited as they are reserved for use with UTF-16 + // surrogate pairs. + + // Determine the length of the UTF-8 sequence. + octet := parser.raw_buffer[parser.raw_buffer_pos] + switch { + case octet&0x80 == 0x00: + width = 1 + case octet&0xE0 == 0xC0: + width = 2 + case octet&0xF0 == 0xE0: + width = 3 + case octet&0xF8 == 0xF0: + width = 4 + default: + // The leading octet is invalid. + return yaml_parser_set_reader_error(parser, + "invalid leading UTF-8 octet", + parser.offset, int(octet)) + } + + // Check if the raw buffer contains an incomplete character. + if width > raw_unread { + if parser.eof { + return yaml_parser_set_reader_error(parser, + "incomplete UTF-8 octet sequence", + parser.offset, -1) + } + break inner + } + + // Decode the leading octet. + switch { + case octet&0x80 == 0x00: + value = rune(octet & 0x7F) + case octet&0xE0 == 0xC0: + value = rune(octet & 0x1F) + case octet&0xF0 == 0xE0: + value = rune(octet & 0x0F) + case octet&0xF8 == 0xF0: + value = rune(octet & 0x07) + default: + value = 0 + } + + // Check and decode the trailing octets. + for k := 1; k < width; k++ { + octet = parser.raw_buffer[parser.raw_buffer_pos+k] + + // Check if the octet is valid. + if (octet & 0xC0) != 0x80 { + return yaml_parser_set_reader_error(parser, + "invalid trailing UTF-8 octet", + parser.offset+k, int(octet)) + } + + // Decode the octet. + value = (value << 6) + rune(octet&0x3F) + } + + // Check the length of the sequence against the value. + switch { + case width == 1: + case width == 2 && value >= 0x80: + case width == 3 && value >= 0x800: + case width == 4 && value >= 0x10000: + default: + return yaml_parser_set_reader_error(parser, + "invalid length of a UTF-8 sequence", + parser.offset, -1) + } + + // Check the range of the value. + if value >= 0xD800 && value <= 0xDFFF || value > 0x10FFFF { + return yaml_parser_set_reader_error(parser, + "invalid Unicode character", + parser.offset, int(value)) + } + + case yaml_UTF16LE_ENCODING, yaml_UTF16BE_ENCODING: + var low, high int + if parser.encoding == yaml_UTF16LE_ENCODING { + low, high = 0, 1 + } else { + low, high = 1, 0 + } + + // The UTF-16 encoding is not as simple as one might + // naively think. Check RFC 2781 + // (http://www.ietf.org/rfc/rfc2781.txt). + // + // Normally, two subsequent bytes describe a Unicode + // character. However a special technique (called a + // surrogate pair) is used for specifying character + // values larger than 0xFFFF. + // + // A surrogate pair consists of two pseudo-characters: + // high surrogate area (0xD800-0xDBFF) + // low surrogate area (0xDC00-0xDFFF) + // + // The following formulas are used for decoding + // and encoding characters using surrogate pairs: + // + // U = U' + 0x10000 (0x01 00 00 <= U <= 0x10 FF FF) + // U' = yyyyyyyyyyxxxxxxxxxx (0 <= U' <= 0x0F FF FF) + // W1 = 110110yyyyyyyyyy + // W2 = 110111xxxxxxxxxx + // + // where U is the character value, W1 is the high surrogate + // area, W2 is the low surrogate area. + + // Check for incomplete UTF-16 character. + if raw_unread < 2 { + if parser.eof { + return yaml_parser_set_reader_error(parser, + "incomplete UTF-16 character", + parser.offset, -1) + } + break inner + } + + // Get the character. + value = rune(parser.raw_buffer[parser.raw_buffer_pos+low]) + + (rune(parser.raw_buffer[parser.raw_buffer_pos+high]) << 8) + + // Check for unexpected low surrogate area. + if value&0xFC00 == 0xDC00 { + return yaml_parser_set_reader_error(parser, + "unexpected low surrogate area", + parser.offset, int(value)) + } + + // Check for a high surrogate area. + if value&0xFC00 == 0xD800 { + width = 4 + + // Check for incomplete surrogate pair. + if raw_unread < 4 { + if parser.eof { + return yaml_parser_set_reader_error(parser, + "incomplete UTF-16 surrogate pair", + parser.offset, -1) + } + break inner + } + + // Get the next character. + value2 := rune(parser.raw_buffer[parser.raw_buffer_pos+low+2]) + + (rune(parser.raw_buffer[parser.raw_buffer_pos+high+2]) << 8) + + // Check for a low surrogate area. + if value2&0xFC00 != 0xDC00 { + return yaml_parser_set_reader_error(parser, + "expected low surrogate area", + parser.offset+2, int(value2)) + } + + // Generate the value of the surrogate pair. + value = 0x10000 + ((value & 0x3FF) << 10) + (value2 & 0x3FF) + } else { + width = 2 + } + + default: + panic("impossible") + } + + // Check if the character is in the allowed range: + // #x9 | #xA | #xD | [#x20-#x7E] (8 bit) + // | #x85 | [#xA0-#xD7FF] | [#xE000-#xFFFD] (16 bit) + // | [#x10000-#x10FFFF] (32 bit) + switch { + case value == 0x09: + case value == 0x0A: + case value == 0x0D: + case value >= 0x20 && value <= 0x7E: + case value == 0x85: + case value >= 0xA0 && value <= 0xD7FF: + case value >= 0xE000 && value <= 0xFFFD: + case value >= 0x10000 && value <= 0x10FFFF: + default: + return yaml_parser_set_reader_error(parser, + "control characters are not allowed", + parser.offset, int(value)) + } + + // Move the raw pointers. + parser.raw_buffer_pos += width + parser.offset += width + + // Finally put the character into the buffer. + if value <= 0x7F { + // 0000 0000-0000 007F . 0xxxxxxx + parser.buffer[buffer_len+0] = byte(value) + buffer_len += 1 + } else if value <= 0x7FF { + // 0000 0080-0000 07FF . 110xxxxx 10xxxxxx + parser.buffer[buffer_len+0] = byte(0xC0 + (value >> 6)) + parser.buffer[buffer_len+1] = byte(0x80 + (value & 0x3F)) + buffer_len += 2 + } else if value <= 0xFFFF { + // 0000 0800-0000 FFFF . 1110xxxx 10xxxxxx 10xxxxxx + parser.buffer[buffer_len+0] = byte(0xE0 + (value >> 12)) + parser.buffer[buffer_len+1] = byte(0x80 + ((value >> 6) & 0x3F)) + parser.buffer[buffer_len+2] = byte(0x80 + (value & 0x3F)) + buffer_len += 3 + } else { + // 0001 0000-0010 FFFF . 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx + parser.buffer[buffer_len+0] = byte(0xF0 + (value >> 18)) + parser.buffer[buffer_len+1] = byte(0x80 + ((value >> 12) & 0x3F)) + parser.buffer[buffer_len+2] = byte(0x80 + ((value >> 6) & 0x3F)) + parser.buffer[buffer_len+3] = byte(0x80 + (value & 0x3F)) + buffer_len += 4 + } + + parser.unread++ + } + + // On EOF, put NUL into the buffer and return. + if parser.eof { + parser.buffer[buffer_len] = 0 + buffer_len++ + parser.unread++ + break + } + } + // [Go] Read the documentation of this function above. To return true, + // we need to have the given length in the buffer. Not doing that means + // every single check that calls this function to make sure the buffer + // has a given length is Go) panicking; or C) accessing invalid memory. + // This happens here due to the EOF above breaking early. + for buffer_len < length { + parser.buffer[buffer_len] = 0 + buffer_len++ + } + parser.buffer = parser.buffer[:buffer_len] + return true +} diff --git a/vendor/gopkg.in/yaml.v2/resolve.go b/vendor/gopkg.in/yaml.v2/resolve.go new file mode 100644 index 000000000..4120e0c91 --- /dev/null +++ b/vendor/gopkg.in/yaml.v2/resolve.go @@ -0,0 +1,258 @@ +package yaml + +import ( + "encoding/base64" + "math" + "regexp" + "strconv" + "strings" + "time" +) + +type resolveMapItem struct { + value interface{} + tag string +} + +var resolveTable = make([]byte, 256) +var resolveMap = make(map[string]resolveMapItem) + +func init() { + t := resolveTable + t[int('+')] = 'S' // Sign + t[int('-')] = 'S' + for _, c := range "0123456789" { + t[int(c)] = 'D' // Digit + } + for _, c := range "yYnNtTfFoO~" { + t[int(c)] = 'M' // In map + } + t[int('.')] = '.' // Float (potentially in map) + + var resolveMapList = []struct { + v interface{} + tag string + l []string + }{ + {true, yaml_BOOL_TAG, []string{"y", "Y", "yes", "Yes", "YES"}}, + {true, yaml_BOOL_TAG, []string{"true", "True", "TRUE"}}, + {true, yaml_BOOL_TAG, []string{"on", "On", "ON"}}, + {false, yaml_BOOL_TAG, []string{"n", "N", "no", "No", "NO"}}, + {false, yaml_BOOL_TAG, []string{"false", "False", "FALSE"}}, + {false, yaml_BOOL_TAG, []string{"off", "Off", "OFF"}}, + {nil, yaml_NULL_TAG, []string{"", "~", "null", "Null", "NULL"}}, + {math.NaN(), yaml_FLOAT_TAG, []string{".nan", ".NaN", ".NAN"}}, + {math.Inf(+1), yaml_FLOAT_TAG, []string{".inf", ".Inf", ".INF"}}, + {math.Inf(+1), yaml_FLOAT_TAG, []string{"+.inf", "+.Inf", "+.INF"}}, + {math.Inf(-1), yaml_FLOAT_TAG, []string{"-.inf", "-.Inf", "-.INF"}}, + {"<<", yaml_MERGE_TAG, []string{"<<"}}, + } + + m := resolveMap + for _, item := range resolveMapList { + for _, s := range item.l { + m[s] = resolveMapItem{item.v, item.tag} + } + } +} + +const longTagPrefix = "tag:yaml.org,2002:" + +func shortTag(tag string) string { + // TODO This can easily be made faster and produce less garbage. + if strings.HasPrefix(tag, longTagPrefix) { + return "!!" + tag[len(longTagPrefix):] + } + return tag +} + +func longTag(tag string) string { + if strings.HasPrefix(tag, "!!") { + return longTagPrefix + tag[2:] + } + return tag +} + +func resolvableTag(tag string) bool { + switch tag { + case "", yaml_STR_TAG, yaml_BOOL_TAG, yaml_INT_TAG, yaml_FLOAT_TAG, yaml_NULL_TAG, yaml_TIMESTAMP_TAG: + return true + } + return false +} + +var yamlStyleFloat = regexp.MustCompile(`^[-+]?(\.[0-9]+|[0-9]+(\.[0-9]*)?)([eE][-+]?[0-9]+)?$`) + +func resolve(tag string, in string) (rtag string, out interface{}) { + if !resolvableTag(tag) { + return tag, in + } + + defer func() { + switch tag { + case "", rtag, yaml_STR_TAG, yaml_BINARY_TAG: + return + case yaml_FLOAT_TAG: + if rtag == yaml_INT_TAG { + switch v := out.(type) { + case int64: + rtag = yaml_FLOAT_TAG + out = float64(v) + return + case int: + rtag = yaml_FLOAT_TAG + out = float64(v) + return + } + } + } + failf("cannot decode %s `%s` as a %s", shortTag(rtag), in, shortTag(tag)) + }() + + // Any data is accepted as a !!str or !!binary. + // Otherwise, the prefix is enough of a hint about what it might be. + hint := byte('N') + if in != "" { + hint = resolveTable[in[0]] + } + if hint != 0 && tag != yaml_STR_TAG && tag != yaml_BINARY_TAG { + // Handle things we can lookup in a map. + if item, ok := resolveMap[in]; ok { + return item.tag, item.value + } + + // Base 60 floats are a bad idea, were dropped in YAML 1.2, and + // are purposefully unsupported here. They're still quoted on + // the way out for compatibility with other parser, though. + + switch hint { + case 'M': + // We've already checked the map above. + + case '.': + // Not in the map, so maybe a normal float. + floatv, err := strconv.ParseFloat(in, 64) + if err == nil { + return yaml_FLOAT_TAG, floatv + } + + case 'D', 'S': + // Int, float, or timestamp. + // Only try values as a timestamp if the value is unquoted or there's an explicit + // !!timestamp tag. + if tag == "" || tag == yaml_TIMESTAMP_TAG { + t, ok := parseTimestamp(in) + if ok { + return yaml_TIMESTAMP_TAG, t + } + } + + plain := strings.Replace(in, "_", "", -1) + intv, err := strconv.ParseInt(plain, 0, 64) + if err == nil { + if intv == int64(int(intv)) { + return yaml_INT_TAG, int(intv) + } else { + return yaml_INT_TAG, intv + } + } + uintv, err := strconv.ParseUint(plain, 0, 64) + if err == nil { + return yaml_INT_TAG, uintv + } + if yamlStyleFloat.MatchString(plain) { + floatv, err := strconv.ParseFloat(plain, 64) + if err == nil { + return yaml_FLOAT_TAG, floatv + } + } + if strings.HasPrefix(plain, "0b") { + intv, err := strconv.ParseInt(plain[2:], 2, 64) + if err == nil { + if intv == int64(int(intv)) { + return yaml_INT_TAG, int(intv) + } else { + return yaml_INT_TAG, intv + } + } + uintv, err := strconv.ParseUint(plain[2:], 2, 64) + if err == nil { + return yaml_INT_TAG, uintv + } + } else if strings.HasPrefix(plain, "-0b") { + intv, err := strconv.ParseInt("-" + plain[3:], 2, 64) + if err == nil { + if true || intv == int64(int(intv)) { + return yaml_INT_TAG, int(intv) + } else { + return yaml_INT_TAG, intv + } + } + } + default: + panic("resolveTable item not yet handled: " + string(rune(hint)) + " (with " + in + ")") + } + } + return yaml_STR_TAG, in +} + +// encodeBase64 encodes s as base64 that is broken up into multiple lines +// as appropriate for the resulting length. +func encodeBase64(s string) string { + const lineLen = 70 + encLen := base64.StdEncoding.EncodedLen(len(s)) + lines := encLen/lineLen + 1 + buf := make([]byte, encLen*2+lines) + in := buf[0:encLen] + out := buf[encLen:] + base64.StdEncoding.Encode(in, []byte(s)) + k := 0 + for i := 0; i < len(in); i += lineLen { + j := i + lineLen + if j > len(in) { + j = len(in) + } + k += copy(out[k:], in[i:j]) + if lines > 1 { + out[k] = '\n' + k++ + } + } + return string(out[:k]) +} + +// This is a subset of the formats allowed by the regular expression +// defined at http://yaml.org/type/timestamp.html. +var allowedTimestampFormats = []string{ + "2006-1-2T15:4:5.999999999Z07:00", // RCF3339Nano with short date fields. + "2006-1-2t15:4:5.999999999Z07:00", // RFC3339Nano with short date fields and lower-case "t". + "2006-1-2 15:4:5.999999999", // space separated with no time zone + "2006-1-2", // date only + // Notable exception: time.Parse cannot handle: "2001-12-14 21:59:43.10 -5" + // from the set of examples. +} + +// parseTimestamp parses s as a timestamp string and +// returns the timestamp and reports whether it succeeded. +// Timestamp formats are defined at http://yaml.org/type/timestamp.html +func parseTimestamp(s string) (time.Time, bool) { + // TODO write code to check all the formats supported by + // http://yaml.org/type/timestamp.html instead of using time.Parse. + + // Quick check: all date formats start with YYYY-. + i := 0 + for ; i < len(s); i++ { + if c := s[i]; c < '0' || c > '9' { + break + } + } + if i != 4 || i == len(s) || s[i] != '-' { + return time.Time{}, false + } + for _, format := range allowedTimestampFormats { + if t, err := time.Parse(format, s); err == nil { + return t, true + } + } + return time.Time{}, false +} diff --git a/vendor/gopkg.in/yaml.v2/scannerc.go b/vendor/gopkg.in/yaml.v2/scannerc.go new file mode 100644 index 000000000..0b9bb6030 --- /dev/null +++ b/vendor/gopkg.in/yaml.v2/scannerc.go @@ -0,0 +1,2711 @@ +package yaml + +import ( + "bytes" + "fmt" +) + +// Introduction +// ************ +// +// The following notes assume that you are familiar with the YAML specification +// (http://yaml.org/spec/1.2/spec.html). We mostly follow it, although in +// some cases we are less restrictive that it requires. +// +// The process of transforming a YAML stream into a sequence of events is +// divided on two steps: Scanning and Parsing. +// +// The Scanner transforms the input stream into a sequence of tokens, while the +// parser transform the sequence of tokens produced by the Scanner into a +// sequence of parsing events. +// +// The Scanner is rather clever and complicated. The Parser, on the contrary, +// is a straightforward implementation of a recursive-descendant parser (or, +// LL(1) parser, as it is usually called). +// +// Actually there are two issues of Scanning that might be called "clever", the +// rest is quite straightforward. The issues are "block collection start" and +// "simple keys". Both issues are explained below in details. +// +// Here the Scanning step is explained and implemented. We start with the list +// of all the tokens produced by the Scanner together with short descriptions. +// +// Now, tokens: +// +// STREAM-START(encoding) # The stream start. +// STREAM-END # The stream end. +// VERSION-DIRECTIVE(major,minor) # The '%YAML' directive. +// TAG-DIRECTIVE(handle,prefix) # The '%TAG' directive. +// DOCUMENT-START # '---' +// DOCUMENT-END # '...' +// BLOCK-SEQUENCE-START # Indentation increase denoting a block +// BLOCK-MAPPING-START # sequence or a block mapping. +// BLOCK-END # Indentation decrease. +// FLOW-SEQUENCE-START # '[' +// FLOW-SEQUENCE-END # ']' +// BLOCK-SEQUENCE-START # '{' +// BLOCK-SEQUENCE-END # '}' +// BLOCK-ENTRY # '-' +// FLOW-ENTRY # ',' +// KEY # '?' or nothing (simple keys). +// VALUE # ':' +// ALIAS(anchor) # '*anchor' +// ANCHOR(anchor) # '&anchor' +// TAG(handle,suffix) # '!handle!suffix' +// SCALAR(value,style) # A scalar. +// +// The following two tokens are "virtual" tokens denoting the beginning and the +// end of the stream: +// +// STREAM-START(encoding) +// STREAM-END +// +// We pass the information about the input stream encoding with the +// STREAM-START token. +// +// The next two tokens are responsible for tags: +// +// VERSION-DIRECTIVE(major,minor) +// TAG-DIRECTIVE(handle,prefix) +// +// Example: +// +// %YAML 1.1 +// %TAG ! !foo +// %TAG !yaml! tag:yaml.org,2002: +// --- +// +// The correspoding sequence of tokens: +// +// STREAM-START(utf-8) +// VERSION-DIRECTIVE(1,1) +// TAG-DIRECTIVE("!","!foo") +// TAG-DIRECTIVE("!yaml","tag:yaml.org,2002:") +// DOCUMENT-START +// STREAM-END +// +// Note that the VERSION-DIRECTIVE and TAG-DIRECTIVE tokens occupy a whole +// line. +// +// The document start and end indicators are represented by: +// +// DOCUMENT-START +// DOCUMENT-END +// +// Note that if a YAML stream contains an implicit document (without '---' +// and '...' indicators), no DOCUMENT-START and DOCUMENT-END tokens will be +// produced. +// +// In the following examples, we present whole documents together with the +// produced tokens. +// +// 1. An implicit document: +// +// 'a scalar' +// +// Tokens: +// +// STREAM-START(utf-8) +// SCALAR("a scalar",single-quoted) +// STREAM-END +// +// 2. An explicit document: +// +// --- +// 'a scalar' +// ... +// +// Tokens: +// +// STREAM-START(utf-8) +// DOCUMENT-START +// SCALAR("a scalar",single-quoted) +// DOCUMENT-END +// STREAM-END +// +// 3. Several documents in a stream: +// +// 'a scalar' +// --- +// 'another scalar' +// --- +// 'yet another scalar' +// +// Tokens: +// +// STREAM-START(utf-8) +// SCALAR("a scalar",single-quoted) +// DOCUMENT-START +// SCALAR("another scalar",single-quoted) +// DOCUMENT-START +// SCALAR("yet another scalar",single-quoted) +// STREAM-END +// +// We have already introduced the SCALAR token above. The following tokens are +// used to describe aliases, anchors, tag, and scalars: +// +// ALIAS(anchor) +// ANCHOR(anchor) +// TAG(handle,suffix) +// SCALAR(value,style) +// +// The following series of examples illustrate the usage of these tokens: +// +// 1. A recursive sequence: +// +// &A [ *A ] +// +// Tokens: +// +// STREAM-START(utf-8) +// ANCHOR("A") +// FLOW-SEQUENCE-START +// ALIAS("A") +// FLOW-SEQUENCE-END +// STREAM-END +// +// 2. A tagged scalar: +// +// !!float "3.14" # A good approximation. +// +// Tokens: +// +// STREAM-START(utf-8) +// TAG("!!","float") +// SCALAR("3.14",double-quoted) +// STREAM-END +// +// 3. Various scalar styles: +// +// --- # Implicit empty plain scalars do not produce tokens. +// --- a plain scalar +// --- 'a single-quoted scalar' +// --- "a double-quoted scalar" +// --- |- +// a literal scalar +// --- >- +// a folded +// scalar +// +// Tokens: +// +// STREAM-START(utf-8) +// DOCUMENT-START +// DOCUMENT-START +// SCALAR("a plain scalar",plain) +// DOCUMENT-START +// SCALAR("a single-quoted scalar",single-quoted) +// DOCUMENT-START +// SCALAR("a double-quoted scalar",double-quoted) +// DOCUMENT-START +// SCALAR("a literal scalar",literal) +// DOCUMENT-START +// SCALAR("a folded scalar",folded) +// STREAM-END +// +// Now it's time to review collection-related tokens. We will start with +// flow collections: +// +// FLOW-SEQUENCE-START +// FLOW-SEQUENCE-END +// FLOW-MAPPING-START +// FLOW-MAPPING-END +// FLOW-ENTRY +// KEY +// VALUE +// +// The tokens FLOW-SEQUENCE-START, FLOW-SEQUENCE-END, FLOW-MAPPING-START, and +// FLOW-MAPPING-END represent the indicators '[', ']', '{', and '}' +// correspondingly. FLOW-ENTRY represent the ',' indicator. Finally the +// indicators '?' and ':', which are used for denoting mapping keys and values, +// are represented by the KEY and VALUE tokens. +// +// The following examples show flow collections: +// +// 1. A flow sequence: +// +// [item 1, item 2, item 3] +// +// Tokens: +// +// STREAM-START(utf-8) +// FLOW-SEQUENCE-START +// SCALAR("item 1",plain) +// FLOW-ENTRY +// SCALAR("item 2",plain) +// FLOW-ENTRY +// SCALAR("item 3",plain) +// FLOW-SEQUENCE-END +// STREAM-END +// +// 2. A flow mapping: +// +// { +// a simple key: a value, # Note that the KEY token is produced. +// ? a complex key: another value, +// } +// +// Tokens: +// +// STREAM-START(utf-8) +// FLOW-MAPPING-START +// KEY +// SCALAR("a simple key",plain) +// VALUE +// SCALAR("a value",plain) +// FLOW-ENTRY +// KEY +// SCALAR("a complex key",plain) +// VALUE +// SCALAR("another value",plain) +// FLOW-ENTRY +// FLOW-MAPPING-END +// STREAM-END +// +// A simple key is a key which is not denoted by the '?' indicator. Note that +// the Scanner still produce the KEY token whenever it encounters a simple key. +// +// For scanning block collections, the following tokens are used (note that we +// repeat KEY and VALUE here): +// +// BLOCK-SEQUENCE-START +// BLOCK-MAPPING-START +// BLOCK-END +// BLOCK-ENTRY +// KEY +// VALUE +// +// The tokens BLOCK-SEQUENCE-START and BLOCK-MAPPING-START denote indentation +// increase that precedes a block collection (cf. the INDENT token in Python). +// The token BLOCK-END denote indentation decrease that ends a block collection +// (cf. the DEDENT token in Python). However YAML has some syntax pecularities +// that makes detections of these tokens more complex. +// +// The tokens BLOCK-ENTRY, KEY, and VALUE are used to represent the indicators +// '-', '?', and ':' correspondingly. +// +// The following examples show how the tokens BLOCK-SEQUENCE-START, +// BLOCK-MAPPING-START, and BLOCK-END are emitted by the Scanner: +// +// 1. Block sequences: +// +// - item 1 +// - item 2 +// - +// - item 3.1 +// - item 3.2 +// - +// key 1: value 1 +// key 2: value 2 +// +// Tokens: +// +// STREAM-START(utf-8) +// BLOCK-SEQUENCE-START +// BLOCK-ENTRY +// SCALAR("item 1",plain) +// BLOCK-ENTRY +// SCALAR("item 2",plain) +// BLOCK-ENTRY +// BLOCK-SEQUENCE-START +// BLOCK-ENTRY +// SCALAR("item 3.1",plain) +// BLOCK-ENTRY +// SCALAR("item 3.2",plain) +// BLOCK-END +// BLOCK-ENTRY +// BLOCK-MAPPING-START +// KEY +// SCALAR("key 1",plain) +// VALUE +// SCALAR("value 1",plain) +// KEY +// SCALAR("key 2",plain) +// VALUE +// SCALAR("value 2",plain) +// BLOCK-END +// BLOCK-END +// STREAM-END +// +// 2. Block mappings: +// +// a simple key: a value # The KEY token is produced here. +// ? a complex key +// : another value +// a mapping: +// key 1: value 1 +// key 2: value 2 +// a sequence: +// - item 1 +// - item 2 +// +// Tokens: +// +// STREAM-START(utf-8) +// BLOCK-MAPPING-START +// KEY +// SCALAR("a simple key",plain) +// VALUE +// SCALAR("a value",plain) +// KEY +// SCALAR("a complex key",plain) +// VALUE +// SCALAR("another value",plain) +// KEY +// SCALAR("a mapping",plain) +// BLOCK-MAPPING-START +// KEY +// SCALAR("key 1",plain) +// VALUE +// SCALAR("value 1",plain) +// KEY +// SCALAR("key 2",plain) +// VALUE +// SCALAR("value 2",plain) +// BLOCK-END +// KEY +// SCALAR("a sequence",plain) +// VALUE +// BLOCK-SEQUENCE-START +// BLOCK-ENTRY +// SCALAR("item 1",plain) +// BLOCK-ENTRY +// SCALAR("item 2",plain) +// BLOCK-END +// BLOCK-END +// STREAM-END +// +// YAML does not always require to start a new block collection from a new +// line. If the current line contains only '-', '?', and ':' indicators, a new +// block collection may start at the current line. The following examples +// illustrate this case: +// +// 1. Collections in a sequence: +// +// - - item 1 +// - item 2 +// - key 1: value 1 +// key 2: value 2 +// - ? complex key +// : complex value +// +// Tokens: +// +// STREAM-START(utf-8) +// BLOCK-SEQUENCE-START +// BLOCK-ENTRY +// BLOCK-SEQUENCE-START +// BLOCK-ENTRY +// SCALAR("item 1",plain) +// BLOCK-ENTRY +// SCALAR("item 2",plain) +// BLOCK-END +// BLOCK-ENTRY +// BLOCK-MAPPING-START +// KEY +// SCALAR("key 1",plain) +// VALUE +// SCALAR("value 1",plain) +// KEY +// SCALAR("key 2",plain) +// VALUE +// SCALAR("value 2",plain) +// BLOCK-END +// BLOCK-ENTRY +// BLOCK-MAPPING-START +// KEY +// SCALAR("complex key") +// VALUE +// SCALAR("complex value") +// BLOCK-END +// BLOCK-END +// STREAM-END +// +// 2. Collections in a mapping: +// +// ? a sequence +// : - item 1 +// - item 2 +// ? a mapping +// : key 1: value 1 +// key 2: value 2 +// +// Tokens: +// +// STREAM-START(utf-8) +// BLOCK-MAPPING-START +// KEY +// SCALAR("a sequence",plain) +// VALUE +// BLOCK-SEQUENCE-START +// BLOCK-ENTRY +// SCALAR("item 1",plain) +// BLOCK-ENTRY +// SCALAR("item 2",plain) +// BLOCK-END +// KEY +// SCALAR("a mapping",plain) +// VALUE +// BLOCK-MAPPING-START +// KEY +// SCALAR("key 1",plain) +// VALUE +// SCALAR("value 1",plain) +// KEY +// SCALAR("key 2",plain) +// VALUE +// SCALAR("value 2",plain) +// BLOCK-END +// BLOCK-END +// STREAM-END +// +// YAML also permits non-indented sequences if they are included into a block +// mapping. In this case, the token BLOCK-SEQUENCE-START is not produced: +// +// key: +// - item 1 # BLOCK-SEQUENCE-START is NOT produced here. +// - item 2 +// +// Tokens: +// +// STREAM-START(utf-8) +// BLOCK-MAPPING-START +// KEY +// SCALAR("key",plain) +// VALUE +// BLOCK-ENTRY +// SCALAR("item 1",plain) +// BLOCK-ENTRY +// SCALAR("item 2",plain) +// BLOCK-END +// + +// Ensure that the buffer contains the required number of characters. +// Return true on success, false on failure (reader error or memory error). +func cache(parser *yaml_parser_t, length int) bool { + // [Go] This was inlined: !cache(A, B) -> unread < B && !update(A, B) + return parser.unread >= length || yaml_parser_update_buffer(parser, length) +} + +// Advance the buffer pointer. +func skip(parser *yaml_parser_t) { + parser.mark.index++ + parser.mark.column++ + parser.unread-- + parser.buffer_pos += width(parser.buffer[parser.buffer_pos]) +} + +func skip_line(parser *yaml_parser_t) { + if is_crlf(parser.buffer, parser.buffer_pos) { + parser.mark.index += 2 + parser.mark.column = 0 + parser.mark.line++ + parser.unread -= 2 + parser.buffer_pos += 2 + } else if is_break(parser.buffer, parser.buffer_pos) { + parser.mark.index++ + parser.mark.column = 0 + parser.mark.line++ + parser.unread-- + parser.buffer_pos += width(parser.buffer[parser.buffer_pos]) + } +} + +// Copy a character to a string buffer and advance pointers. +func read(parser *yaml_parser_t, s []byte) []byte { + w := width(parser.buffer[parser.buffer_pos]) + if w == 0 { + panic("invalid character sequence") + } + if len(s) == 0 { + s = make([]byte, 0, 32) + } + if w == 1 && len(s)+w <= cap(s) { + s = s[:len(s)+1] + s[len(s)-1] = parser.buffer[parser.buffer_pos] + parser.buffer_pos++ + } else { + s = append(s, parser.buffer[parser.buffer_pos:parser.buffer_pos+w]...) + parser.buffer_pos += w + } + parser.mark.index++ + parser.mark.column++ + parser.unread-- + return s +} + +// Copy a line break character to a string buffer and advance pointers. +func read_line(parser *yaml_parser_t, s []byte) []byte { + buf := parser.buffer + pos := parser.buffer_pos + switch { + case buf[pos] == '\r' && buf[pos+1] == '\n': + // CR LF . LF + s = append(s, '\n') + parser.buffer_pos += 2 + parser.mark.index++ + parser.unread-- + case buf[pos] == '\r' || buf[pos] == '\n': + // CR|LF . LF + s = append(s, '\n') + parser.buffer_pos += 1 + case buf[pos] == '\xC2' && buf[pos+1] == '\x85': + // NEL . LF + s = append(s, '\n') + parser.buffer_pos += 2 + case buf[pos] == '\xE2' && buf[pos+1] == '\x80' && (buf[pos+2] == '\xA8' || buf[pos+2] == '\xA9'): + // LS|PS . LS|PS + s = append(s, buf[parser.buffer_pos:pos+3]...) + parser.buffer_pos += 3 + default: + return s + } + parser.mark.index++ + parser.mark.column = 0 + parser.mark.line++ + parser.unread-- + return s +} + +// Get the next token. +func yaml_parser_scan(parser *yaml_parser_t, token *yaml_token_t) bool { + // Erase the token object. + *token = yaml_token_t{} // [Go] Is this necessary? + + // No tokens after STREAM-END or error. + if parser.stream_end_produced || parser.error != yaml_NO_ERROR { + return true + } + + // Ensure that the tokens queue contains enough tokens. + if !parser.token_available { + if !yaml_parser_fetch_more_tokens(parser) { + return false + } + } + + // Fetch the next token from the queue. + *token = parser.tokens[parser.tokens_head] + parser.tokens_head++ + parser.tokens_parsed++ + parser.token_available = false + + if token.typ == yaml_STREAM_END_TOKEN { + parser.stream_end_produced = true + } + return true +} + +// Set the scanner error and return false. +func yaml_parser_set_scanner_error(parser *yaml_parser_t, context string, context_mark yaml_mark_t, problem string) bool { + parser.error = yaml_SCANNER_ERROR + parser.context = context + parser.context_mark = context_mark + parser.problem = problem + parser.problem_mark = parser.mark + return false +} + +func yaml_parser_set_scanner_tag_error(parser *yaml_parser_t, directive bool, context_mark yaml_mark_t, problem string) bool { + context := "while parsing a tag" + if directive { + context = "while parsing a %TAG directive" + } + return yaml_parser_set_scanner_error(parser, context, context_mark, problem) +} + +func trace(args ...interface{}) func() { + pargs := append([]interface{}{"+++"}, args...) + fmt.Println(pargs...) + pargs = append([]interface{}{"---"}, args...) + return func() { fmt.Println(pargs...) } +} + +// Ensure that the tokens queue contains at least one token which can be +// returned to the Parser. +func yaml_parser_fetch_more_tokens(parser *yaml_parser_t) bool { + // While we need more tokens to fetch, do it. + for { + if parser.tokens_head != len(parser.tokens) { + // If queue is non-empty, check if any potential simple key may + // occupy the head position. + head_tok_idx, ok := parser.simple_keys_by_tok[parser.tokens_parsed] + if !ok { + break + } else if valid, ok := yaml_simple_key_is_valid(parser, &parser.simple_keys[head_tok_idx]); !ok { + return false + } else if !valid { + break + } + } + // Fetch the next token. + if !yaml_parser_fetch_next_token(parser) { + return false + } + } + + parser.token_available = true + return true +} + +// The dispatcher for token fetchers. +func yaml_parser_fetch_next_token(parser *yaml_parser_t) bool { + // Ensure that the buffer is initialized. + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + + // Check if we just started scanning. Fetch STREAM-START then. + if !parser.stream_start_produced { + return yaml_parser_fetch_stream_start(parser) + } + + // Eat whitespaces and comments until we reach the next token. + if !yaml_parser_scan_to_next_token(parser) { + return false + } + + // Check the indentation level against the current column. + if !yaml_parser_unroll_indent(parser, parser.mark.column) { + return false + } + + // Ensure that the buffer contains at least 4 characters. 4 is the length + // of the longest indicators ('--- ' and '... '). + if parser.unread < 4 && !yaml_parser_update_buffer(parser, 4) { + return false + } + + // Is it the end of the stream? + if is_z(parser.buffer, parser.buffer_pos) { + return yaml_parser_fetch_stream_end(parser) + } + + // Is it a directive? + if parser.mark.column == 0 && parser.buffer[parser.buffer_pos] == '%' { + return yaml_parser_fetch_directive(parser) + } + + buf := parser.buffer + pos := parser.buffer_pos + + // Is it the document start indicator? + if parser.mark.column == 0 && buf[pos] == '-' && buf[pos+1] == '-' && buf[pos+2] == '-' && is_blankz(buf, pos+3) { + return yaml_parser_fetch_document_indicator(parser, yaml_DOCUMENT_START_TOKEN) + } + + // Is it the document end indicator? + if parser.mark.column == 0 && buf[pos] == '.' && buf[pos+1] == '.' && buf[pos+2] == '.' && is_blankz(buf, pos+3) { + return yaml_parser_fetch_document_indicator(parser, yaml_DOCUMENT_END_TOKEN) + } + + // Is it the flow sequence start indicator? + if buf[pos] == '[' { + return yaml_parser_fetch_flow_collection_start(parser, yaml_FLOW_SEQUENCE_START_TOKEN) + } + + // Is it the flow mapping start indicator? + if parser.buffer[parser.buffer_pos] == '{' { + return yaml_parser_fetch_flow_collection_start(parser, yaml_FLOW_MAPPING_START_TOKEN) + } + + // Is it the flow sequence end indicator? + if parser.buffer[parser.buffer_pos] == ']' { + return yaml_parser_fetch_flow_collection_end(parser, + yaml_FLOW_SEQUENCE_END_TOKEN) + } + + // Is it the flow mapping end indicator? + if parser.buffer[parser.buffer_pos] == '}' { + return yaml_parser_fetch_flow_collection_end(parser, + yaml_FLOW_MAPPING_END_TOKEN) + } + + // Is it the flow entry indicator? + if parser.buffer[parser.buffer_pos] == ',' { + return yaml_parser_fetch_flow_entry(parser) + } + + // Is it the block entry indicator? + if parser.buffer[parser.buffer_pos] == '-' && is_blankz(parser.buffer, parser.buffer_pos+1) { + return yaml_parser_fetch_block_entry(parser) + } + + // Is it the key indicator? + if parser.buffer[parser.buffer_pos] == '?' && (parser.flow_level > 0 || is_blankz(parser.buffer, parser.buffer_pos+1)) { + return yaml_parser_fetch_key(parser) + } + + // Is it the value indicator? + if parser.buffer[parser.buffer_pos] == ':' && (parser.flow_level > 0 || is_blankz(parser.buffer, parser.buffer_pos+1)) { + return yaml_parser_fetch_value(parser) + } + + // Is it an alias? + if parser.buffer[parser.buffer_pos] == '*' { + return yaml_parser_fetch_anchor(parser, yaml_ALIAS_TOKEN) + } + + // Is it an anchor? + if parser.buffer[parser.buffer_pos] == '&' { + return yaml_parser_fetch_anchor(parser, yaml_ANCHOR_TOKEN) + } + + // Is it a tag? + if parser.buffer[parser.buffer_pos] == '!' { + return yaml_parser_fetch_tag(parser) + } + + // Is it a literal scalar? + if parser.buffer[parser.buffer_pos] == '|' && parser.flow_level == 0 { + return yaml_parser_fetch_block_scalar(parser, true) + } + + // Is it a folded scalar? + if parser.buffer[parser.buffer_pos] == '>' && parser.flow_level == 0 { + return yaml_parser_fetch_block_scalar(parser, false) + } + + // Is it a single-quoted scalar? + if parser.buffer[parser.buffer_pos] == '\'' { + return yaml_parser_fetch_flow_scalar(parser, true) + } + + // Is it a double-quoted scalar? + if parser.buffer[parser.buffer_pos] == '"' { + return yaml_parser_fetch_flow_scalar(parser, false) + } + + // Is it a plain scalar? + // + // A plain scalar may start with any non-blank characters except + // + // '-', '?', ':', ',', '[', ']', '{', '}', + // '#', '&', '*', '!', '|', '>', '\'', '\"', + // '%', '@', '`'. + // + // In the block context (and, for the '-' indicator, in the flow context + // too), it may also start with the characters + // + // '-', '?', ':' + // + // if it is followed by a non-space character. + // + // The last rule is more restrictive than the specification requires. + // [Go] Make this logic more reasonable. + //switch parser.buffer[parser.buffer_pos] { + //case '-', '?', ':', ',', '?', '-', ',', ':', ']', '[', '}', '{', '&', '#', '!', '*', '>', '|', '"', '\'', '@', '%', '-', '`': + //} + if !(is_blankz(parser.buffer, parser.buffer_pos) || parser.buffer[parser.buffer_pos] == '-' || + parser.buffer[parser.buffer_pos] == '?' || parser.buffer[parser.buffer_pos] == ':' || + parser.buffer[parser.buffer_pos] == ',' || parser.buffer[parser.buffer_pos] == '[' || + parser.buffer[parser.buffer_pos] == ']' || parser.buffer[parser.buffer_pos] == '{' || + parser.buffer[parser.buffer_pos] == '}' || parser.buffer[parser.buffer_pos] == '#' || + parser.buffer[parser.buffer_pos] == '&' || parser.buffer[parser.buffer_pos] == '*' || + parser.buffer[parser.buffer_pos] == '!' || parser.buffer[parser.buffer_pos] == '|' || + parser.buffer[parser.buffer_pos] == '>' || parser.buffer[parser.buffer_pos] == '\'' || + parser.buffer[parser.buffer_pos] == '"' || parser.buffer[parser.buffer_pos] == '%' || + parser.buffer[parser.buffer_pos] == '@' || parser.buffer[parser.buffer_pos] == '`') || + (parser.buffer[parser.buffer_pos] == '-' && !is_blank(parser.buffer, parser.buffer_pos+1)) || + (parser.flow_level == 0 && + (parser.buffer[parser.buffer_pos] == '?' || parser.buffer[parser.buffer_pos] == ':') && + !is_blankz(parser.buffer, parser.buffer_pos+1)) { + return yaml_parser_fetch_plain_scalar(parser) + } + + // If we don't determine the token type so far, it is an error. + return yaml_parser_set_scanner_error(parser, + "while scanning for the next token", parser.mark, + "found character that cannot start any token") +} + +func yaml_simple_key_is_valid(parser *yaml_parser_t, simple_key *yaml_simple_key_t) (valid, ok bool) { + if !simple_key.possible { + return false, true + } + + // The 1.2 specification says: + // + // "If the ? indicator is omitted, parsing needs to see past the + // implicit key to recognize it as such. To limit the amount of + // lookahead required, the “:” indicator must appear at most 1024 + // Unicode characters beyond the start of the key. In addition, the key + // is restricted to a single line." + // + if simple_key.mark.line < parser.mark.line || simple_key.mark.index+1024 < parser.mark.index { + // Check if the potential simple key to be removed is required. + if simple_key.required { + return false, yaml_parser_set_scanner_error(parser, + "while scanning a simple key", simple_key.mark, + "could not find expected ':'") + } + simple_key.possible = false + return false, true + } + return true, true +} + +// Check if a simple key may start at the current position and add it if +// needed. +func yaml_parser_save_simple_key(parser *yaml_parser_t) bool { + // A simple key is required at the current position if the scanner is in + // the block context and the current column coincides with the indentation + // level. + + required := parser.flow_level == 0 && parser.indent == parser.mark.column + + // + // If the current position may start a simple key, save it. + // + if parser.simple_key_allowed { + simple_key := yaml_simple_key_t{ + possible: true, + required: required, + token_number: parser.tokens_parsed + (len(parser.tokens) - parser.tokens_head), + mark: parser.mark, + } + + if !yaml_parser_remove_simple_key(parser) { + return false + } + parser.simple_keys[len(parser.simple_keys)-1] = simple_key + parser.simple_keys_by_tok[simple_key.token_number] = len(parser.simple_keys) - 1 + } + return true +} + +// Remove a potential simple key at the current flow level. +func yaml_parser_remove_simple_key(parser *yaml_parser_t) bool { + i := len(parser.simple_keys) - 1 + if parser.simple_keys[i].possible { + // If the key is required, it is an error. + if parser.simple_keys[i].required { + return yaml_parser_set_scanner_error(parser, + "while scanning a simple key", parser.simple_keys[i].mark, + "could not find expected ':'") + } + // Remove the key from the stack. + parser.simple_keys[i].possible = false + delete(parser.simple_keys_by_tok, parser.simple_keys[i].token_number) + } + return true +} + +// max_flow_level limits the flow_level +const max_flow_level = 10000 + +// Increase the flow level and resize the simple key list if needed. +func yaml_parser_increase_flow_level(parser *yaml_parser_t) bool { + // Reset the simple key on the next level. + parser.simple_keys = append(parser.simple_keys, yaml_simple_key_t{ + possible: false, + required: false, + token_number: parser.tokens_parsed + (len(parser.tokens) - parser.tokens_head), + mark: parser.mark, + }) + + // Increase the flow level. + parser.flow_level++ + if parser.flow_level > max_flow_level { + return yaml_parser_set_scanner_error(parser, + "while increasing flow level", parser.simple_keys[len(parser.simple_keys)-1].mark, + fmt.Sprintf("exceeded max depth of %d", max_flow_level)) + } + return true +} + +// Decrease the flow level. +func yaml_parser_decrease_flow_level(parser *yaml_parser_t) bool { + if parser.flow_level > 0 { + parser.flow_level-- + last := len(parser.simple_keys) - 1 + delete(parser.simple_keys_by_tok, parser.simple_keys[last].token_number) + parser.simple_keys = parser.simple_keys[:last] + } + return true +} + +// max_indents limits the indents stack size +const max_indents = 10000 + +// Push the current indentation level to the stack and set the new level +// the current column is greater than the indentation level. In this case, +// append or insert the specified token into the token queue. +func yaml_parser_roll_indent(parser *yaml_parser_t, column, number int, typ yaml_token_type_t, mark yaml_mark_t) bool { + // In the flow context, do nothing. + if parser.flow_level > 0 { + return true + } + + if parser.indent < column { + // Push the current indentation level to the stack and set the new + // indentation level. + parser.indents = append(parser.indents, parser.indent) + parser.indent = column + if len(parser.indents) > max_indents { + return yaml_parser_set_scanner_error(parser, + "while increasing indent level", parser.simple_keys[len(parser.simple_keys)-1].mark, + fmt.Sprintf("exceeded max depth of %d", max_indents)) + } + + // Create a token and insert it into the queue. + token := yaml_token_t{ + typ: typ, + start_mark: mark, + end_mark: mark, + } + if number > -1 { + number -= parser.tokens_parsed + } + yaml_insert_token(parser, number, &token) + } + return true +} + +// Pop indentation levels from the indents stack until the current level +// becomes less or equal to the column. For each indentation level, append +// the BLOCK-END token. +func yaml_parser_unroll_indent(parser *yaml_parser_t, column int) bool { + // In the flow context, do nothing. + if parser.flow_level > 0 { + return true + } + + // Loop through the indentation levels in the stack. + for parser.indent > column { + // Create a token and append it to the queue. + token := yaml_token_t{ + typ: yaml_BLOCK_END_TOKEN, + start_mark: parser.mark, + end_mark: parser.mark, + } + yaml_insert_token(parser, -1, &token) + + // Pop the indentation level. + parser.indent = parser.indents[len(parser.indents)-1] + parser.indents = parser.indents[:len(parser.indents)-1] + } + return true +} + +// Initialize the scanner and produce the STREAM-START token. +func yaml_parser_fetch_stream_start(parser *yaml_parser_t) bool { + + // Set the initial indentation. + parser.indent = -1 + + // Initialize the simple key stack. + parser.simple_keys = append(parser.simple_keys, yaml_simple_key_t{}) + + parser.simple_keys_by_tok = make(map[int]int) + + // A simple key is allowed at the beginning of the stream. + parser.simple_key_allowed = true + + // We have started. + parser.stream_start_produced = true + + // Create the STREAM-START token and append it to the queue. + token := yaml_token_t{ + typ: yaml_STREAM_START_TOKEN, + start_mark: parser.mark, + end_mark: parser.mark, + encoding: parser.encoding, + } + yaml_insert_token(parser, -1, &token) + return true +} + +// Produce the STREAM-END token and shut down the scanner. +func yaml_parser_fetch_stream_end(parser *yaml_parser_t) bool { + + // Force new line. + if parser.mark.column != 0 { + parser.mark.column = 0 + parser.mark.line++ + } + + // Reset the indentation level. + if !yaml_parser_unroll_indent(parser, -1) { + return false + } + + // Reset simple keys. + if !yaml_parser_remove_simple_key(parser) { + return false + } + + parser.simple_key_allowed = false + + // Create the STREAM-END token and append it to the queue. + token := yaml_token_t{ + typ: yaml_STREAM_END_TOKEN, + start_mark: parser.mark, + end_mark: parser.mark, + } + yaml_insert_token(parser, -1, &token) + return true +} + +// Produce a VERSION-DIRECTIVE or TAG-DIRECTIVE token. +func yaml_parser_fetch_directive(parser *yaml_parser_t) bool { + // Reset the indentation level. + if !yaml_parser_unroll_indent(parser, -1) { + return false + } + + // Reset simple keys. + if !yaml_parser_remove_simple_key(parser) { + return false + } + + parser.simple_key_allowed = false + + // Create the YAML-DIRECTIVE or TAG-DIRECTIVE token. + token := yaml_token_t{} + if !yaml_parser_scan_directive(parser, &token) { + return false + } + // Append the token to the queue. + yaml_insert_token(parser, -1, &token) + return true +} + +// Produce the DOCUMENT-START or DOCUMENT-END token. +func yaml_parser_fetch_document_indicator(parser *yaml_parser_t, typ yaml_token_type_t) bool { + // Reset the indentation level. + if !yaml_parser_unroll_indent(parser, -1) { + return false + } + + // Reset simple keys. + if !yaml_parser_remove_simple_key(parser) { + return false + } + + parser.simple_key_allowed = false + + // Consume the token. + start_mark := parser.mark + + skip(parser) + skip(parser) + skip(parser) + + end_mark := parser.mark + + // Create the DOCUMENT-START or DOCUMENT-END token. + token := yaml_token_t{ + typ: typ, + start_mark: start_mark, + end_mark: end_mark, + } + // Append the token to the queue. + yaml_insert_token(parser, -1, &token) + return true +} + +// Produce the FLOW-SEQUENCE-START or FLOW-MAPPING-START token. +func yaml_parser_fetch_flow_collection_start(parser *yaml_parser_t, typ yaml_token_type_t) bool { + // The indicators '[' and '{' may start a simple key. + if !yaml_parser_save_simple_key(parser) { + return false + } + + // Increase the flow level. + if !yaml_parser_increase_flow_level(parser) { + return false + } + + // A simple key may follow the indicators '[' and '{'. + parser.simple_key_allowed = true + + // Consume the token. + start_mark := parser.mark + skip(parser) + end_mark := parser.mark + + // Create the FLOW-SEQUENCE-START of FLOW-MAPPING-START token. + token := yaml_token_t{ + typ: typ, + start_mark: start_mark, + end_mark: end_mark, + } + // Append the token to the queue. + yaml_insert_token(parser, -1, &token) + return true +} + +// Produce the FLOW-SEQUENCE-END or FLOW-MAPPING-END token. +func yaml_parser_fetch_flow_collection_end(parser *yaml_parser_t, typ yaml_token_type_t) bool { + // Reset any potential simple key on the current flow level. + if !yaml_parser_remove_simple_key(parser) { + return false + } + + // Decrease the flow level. + if !yaml_parser_decrease_flow_level(parser) { + return false + } + + // No simple keys after the indicators ']' and '}'. + parser.simple_key_allowed = false + + // Consume the token. + + start_mark := parser.mark + skip(parser) + end_mark := parser.mark + + // Create the FLOW-SEQUENCE-END of FLOW-MAPPING-END token. + token := yaml_token_t{ + typ: typ, + start_mark: start_mark, + end_mark: end_mark, + } + // Append the token to the queue. + yaml_insert_token(parser, -1, &token) + return true +} + +// Produce the FLOW-ENTRY token. +func yaml_parser_fetch_flow_entry(parser *yaml_parser_t) bool { + // Reset any potential simple keys on the current flow level. + if !yaml_parser_remove_simple_key(parser) { + return false + } + + // Simple keys are allowed after ','. + parser.simple_key_allowed = true + + // Consume the token. + start_mark := parser.mark + skip(parser) + end_mark := parser.mark + + // Create the FLOW-ENTRY token and append it to the queue. + token := yaml_token_t{ + typ: yaml_FLOW_ENTRY_TOKEN, + start_mark: start_mark, + end_mark: end_mark, + } + yaml_insert_token(parser, -1, &token) + return true +} + +// Produce the BLOCK-ENTRY token. +func yaml_parser_fetch_block_entry(parser *yaml_parser_t) bool { + // Check if the scanner is in the block context. + if parser.flow_level == 0 { + // Check if we are allowed to start a new entry. + if !parser.simple_key_allowed { + return yaml_parser_set_scanner_error(parser, "", parser.mark, + "block sequence entries are not allowed in this context") + } + // Add the BLOCK-SEQUENCE-START token if needed. + if !yaml_parser_roll_indent(parser, parser.mark.column, -1, yaml_BLOCK_SEQUENCE_START_TOKEN, parser.mark) { + return false + } + } else { + // It is an error for the '-' indicator to occur in the flow context, + // but we let the Parser detect and report about it because the Parser + // is able to point to the context. + } + + // Reset any potential simple keys on the current flow level. + if !yaml_parser_remove_simple_key(parser) { + return false + } + + // Simple keys are allowed after '-'. + parser.simple_key_allowed = true + + // Consume the token. + start_mark := parser.mark + skip(parser) + end_mark := parser.mark + + // Create the BLOCK-ENTRY token and append it to the queue. + token := yaml_token_t{ + typ: yaml_BLOCK_ENTRY_TOKEN, + start_mark: start_mark, + end_mark: end_mark, + } + yaml_insert_token(parser, -1, &token) + return true +} + +// Produce the KEY token. +func yaml_parser_fetch_key(parser *yaml_parser_t) bool { + + // In the block context, additional checks are required. + if parser.flow_level == 0 { + // Check if we are allowed to start a new key (not nessesary simple). + if !parser.simple_key_allowed { + return yaml_parser_set_scanner_error(parser, "", parser.mark, + "mapping keys are not allowed in this context") + } + // Add the BLOCK-MAPPING-START token if needed. + if !yaml_parser_roll_indent(parser, parser.mark.column, -1, yaml_BLOCK_MAPPING_START_TOKEN, parser.mark) { + return false + } + } + + // Reset any potential simple keys on the current flow level. + if !yaml_parser_remove_simple_key(parser) { + return false + } + + // Simple keys are allowed after '?' in the block context. + parser.simple_key_allowed = parser.flow_level == 0 + + // Consume the token. + start_mark := parser.mark + skip(parser) + end_mark := parser.mark + + // Create the KEY token and append it to the queue. + token := yaml_token_t{ + typ: yaml_KEY_TOKEN, + start_mark: start_mark, + end_mark: end_mark, + } + yaml_insert_token(parser, -1, &token) + return true +} + +// Produce the VALUE token. +func yaml_parser_fetch_value(parser *yaml_parser_t) bool { + + simple_key := &parser.simple_keys[len(parser.simple_keys)-1] + + // Have we found a simple key? + if valid, ok := yaml_simple_key_is_valid(parser, simple_key); !ok { + return false + + } else if valid { + + // Create the KEY token and insert it into the queue. + token := yaml_token_t{ + typ: yaml_KEY_TOKEN, + start_mark: simple_key.mark, + end_mark: simple_key.mark, + } + yaml_insert_token(parser, simple_key.token_number-parser.tokens_parsed, &token) + + // In the block context, we may need to add the BLOCK-MAPPING-START token. + if !yaml_parser_roll_indent(parser, simple_key.mark.column, + simple_key.token_number, + yaml_BLOCK_MAPPING_START_TOKEN, simple_key.mark) { + return false + } + + // Remove the simple key. + simple_key.possible = false + delete(parser.simple_keys_by_tok, simple_key.token_number) + + // A simple key cannot follow another simple key. + parser.simple_key_allowed = false + + } else { + // The ':' indicator follows a complex key. + + // In the block context, extra checks are required. + if parser.flow_level == 0 { + + // Check if we are allowed to start a complex value. + if !parser.simple_key_allowed { + return yaml_parser_set_scanner_error(parser, "", parser.mark, + "mapping values are not allowed in this context") + } + + // Add the BLOCK-MAPPING-START token if needed. + if !yaml_parser_roll_indent(parser, parser.mark.column, -1, yaml_BLOCK_MAPPING_START_TOKEN, parser.mark) { + return false + } + } + + // Simple keys after ':' are allowed in the block context. + parser.simple_key_allowed = parser.flow_level == 0 + } + + // Consume the token. + start_mark := parser.mark + skip(parser) + end_mark := parser.mark + + // Create the VALUE token and append it to the queue. + token := yaml_token_t{ + typ: yaml_VALUE_TOKEN, + start_mark: start_mark, + end_mark: end_mark, + } + yaml_insert_token(parser, -1, &token) + return true +} + +// Produce the ALIAS or ANCHOR token. +func yaml_parser_fetch_anchor(parser *yaml_parser_t, typ yaml_token_type_t) bool { + // An anchor or an alias could be a simple key. + if !yaml_parser_save_simple_key(parser) { + return false + } + + // A simple key cannot follow an anchor or an alias. + parser.simple_key_allowed = false + + // Create the ALIAS or ANCHOR token and append it to the queue. + var token yaml_token_t + if !yaml_parser_scan_anchor(parser, &token, typ) { + return false + } + yaml_insert_token(parser, -1, &token) + return true +} + +// Produce the TAG token. +func yaml_parser_fetch_tag(parser *yaml_parser_t) bool { + // A tag could be a simple key. + if !yaml_parser_save_simple_key(parser) { + return false + } + + // A simple key cannot follow a tag. + parser.simple_key_allowed = false + + // Create the TAG token and append it to the queue. + var token yaml_token_t + if !yaml_parser_scan_tag(parser, &token) { + return false + } + yaml_insert_token(parser, -1, &token) + return true +} + +// Produce the SCALAR(...,literal) or SCALAR(...,folded) tokens. +func yaml_parser_fetch_block_scalar(parser *yaml_parser_t, literal bool) bool { + // Remove any potential simple keys. + if !yaml_parser_remove_simple_key(parser) { + return false + } + + // A simple key may follow a block scalar. + parser.simple_key_allowed = true + + // Create the SCALAR token and append it to the queue. + var token yaml_token_t + if !yaml_parser_scan_block_scalar(parser, &token, literal) { + return false + } + yaml_insert_token(parser, -1, &token) + return true +} + +// Produce the SCALAR(...,single-quoted) or SCALAR(...,double-quoted) tokens. +func yaml_parser_fetch_flow_scalar(parser *yaml_parser_t, single bool) bool { + // A plain scalar could be a simple key. + if !yaml_parser_save_simple_key(parser) { + return false + } + + // A simple key cannot follow a flow scalar. + parser.simple_key_allowed = false + + // Create the SCALAR token and append it to the queue. + var token yaml_token_t + if !yaml_parser_scan_flow_scalar(parser, &token, single) { + return false + } + yaml_insert_token(parser, -1, &token) + return true +} + +// Produce the SCALAR(...,plain) token. +func yaml_parser_fetch_plain_scalar(parser *yaml_parser_t) bool { + // A plain scalar could be a simple key. + if !yaml_parser_save_simple_key(parser) { + return false + } + + // A simple key cannot follow a flow scalar. + parser.simple_key_allowed = false + + // Create the SCALAR token and append it to the queue. + var token yaml_token_t + if !yaml_parser_scan_plain_scalar(parser, &token) { + return false + } + yaml_insert_token(parser, -1, &token) + return true +} + +// Eat whitespaces and comments until the next token is found. +func yaml_parser_scan_to_next_token(parser *yaml_parser_t) bool { + + // Until the next token is not found. + for { + // Allow the BOM mark to start a line. + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + if parser.mark.column == 0 && is_bom(parser.buffer, parser.buffer_pos) { + skip(parser) + } + + // Eat whitespaces. + // Tabs are allowed: + // - in the flow context + // - in the block context, but not at the beginning of the line or + // after '-', '?', or ':' (complex value). + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + + for parser.buffer[parser.buffer_pos] == ' ' || ((parser.flow_level > 0 || !parser.simple_key_allowed) && parser.buffer[parser.buffer_pos] == '\t') { + skip(parser) + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + } + + // Eat a comment until a line break. + if parser.buffer[parser.buffer_pos] == '#' { + for !is_breakz(parser.buffer, parser.buffer_pos) { + skip(parser) + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + } + } + + // If it is a line break, eat it. + if is_break(parser.buffer, parser.buffer_pos) { + if parser.unread < 2 && !yaml_parser_update_buffer(parser, 2) { + return false + } + skip_line(parser) + + // In the block context, a new line may start a simple key. + if parser.flow_level == 0 { + parser.simple_key_allowed = true + } + } else { + break // We have found a token. + } + } + + return true +} + +// Scan a YAML-DIRECTIVE or TAG-DIRECTIVE token. +// +// Scope: +// %YAML 1.1 # a comment \n +// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +// %TAG !yaml! tag:yaml.org,2002: \n +// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +// +func yaml_parser_scan_directive(parser *yaml_parser_t, token *yaml_token_t) bool { + // Eat '%'. + start_mark := parser.mark + skip(parser) + + // Scan the directive name. + var name []byte + if !yaml_parser_scan_directive_name(parser, start_mark, &name) { + return false + } + + // Is it a YAML directive? + if bytes.Equal(name, []byte("YAML")) { + // Scan the VERSION directive value. + var major, minor int8 + if !yaml_parser_scan_version_directive_value(parser, start_mark, &major, &minor) { + return false + } + end_mark := parser.mark + + // Create a VERSION-DIRECTIVE token. + *token = yaml_token_t{ + typ: yaml_VERSION_DIRECTIVE_TOKEN, + start_mark: start_mark, + end_mark: end_mark, + major: major, + minor: minor, + } + + // Is it a TAG directive? + } else if bytes.Equal(name, []byte("TAG")) { + // Scan the TAG directive value. + var handle, prefix []byte + if !yaml_parser_scan_tag_directive_value(parser, start_mark, &handle, &prefix) { + return false + } + end_mark := parser.mark + + // Create a TAG-DIRECTIVE token. + *token = yaml_token_t{ + typ: yaml_TAG_DIRECTIVE_TOKEN, + start_mark: start_mark, + end_mark: end_mark, + value: handle, + prefix: prefix, + } + + // Unknown directive. + } else { + yaml_parser_set_scanner_error(parser, "while scanning a directive", + start_mark, "found unknown directive name") + return false + } + + // Eat the rest of the line including any comments. + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + + for is_blank(parser.buffer, parser.buffer_pos) { + skip(parser) + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + } + + if parser.buffer[parser.buffer_pos] == '#' { + for !is_breakz(parser.buffer, parser.buffer_pos) { + skip(parser) + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + } + } + + // Check if we are at the end of the line. + if !is_breakz(parser.buffer, parser.buffer_pos) { + yaml_parser_set_scanner_error(parser, "while scanning a directive", + start_mark, "did not find expected comment or line break") + return false + } + + // Eat a line break. + if is_break(parser.buffer, parser.buffer_pos) { + if parser.unread < 2 && !yaml_parser_update_buffer(parser, 2) { + return false + } + skip_line(parser) + } + + return true +} + +// Scan the directive name. +// +// Scope: +// %YAML 1.1 # a comment \n +// ^^^^ +// %TAG !yaml! tag:yaml.org,2002: \n +// ^^^ +// +func yaml_parser_scan_directive_name(parser *yaml_parser_t, start_mark yaml_mark_t, name *[]byte) bool { + // Consume the directive name. + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + + var s []byte + for is_alpha(parser.buffer, parser.buffer_pos) { + s = read(parser, s) + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + } + + // Check if the name is empty. + if len(s) == 0 { + yaml_parser_set_scanner_error(parser, "while scanning a directive", + start_mark, "could not find expected directive name") + return false + } + + // Check for an blank character after the name. + if !is_blankz(parser.buffer, parser.buffer_pos) { + yaml_parser_set_scanner_error(parser, "while scanning a directive", + start_mark, "found unexpected non-alphabetical character") + return false + } + *name = s + return true +} + +// Scan the value of VERSION-DIRECTIVE. +// +// Scope: +// %YAML 1.1 # a comment \n +// ^^^^^^ +func yaml_parser_scan_version_directive_value(parser *yaml_parser_t, start_mark yaml_mark_t, major, minor *int8) bool { + // Eat whitespaces. + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + for is_blank(parser.buffer, parser.buffer_pos) { + skip(parser) + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + } + + // Consume the major version number. + if !yaml_parser_scan_version_directive_number(parser, start_mark, major) { + return false + } + + // Eat '.'. + if parser.buffer[parser.buffer_pos] != '.' { + return yaml_parser_set_scanner_error(parser, "while scanning a %YAML directive", + start_mark, "did not find expected digit or '.' character") + } + + skip(parser) + + // Consume the minor version number. + if !yaml_parser_scan_version_directive_number(parser, start_mark, minor) { + return false + } + return true +} + +const max_number_length = 2 + +// Scan the version number of VERSION-DIRECTIVE. +// +// Scope: +// %YAML 1.1 # a comment \n +// ^ +// %YAML 1.1 # a comment \n +// ^ +func yaml_parser_scan_version_directive_number(parser *yaml_parser_t, start_mark yaml_mark_t, number *int8) bool { + + // Repeat while the next character is digit. + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + var value, length int8 + for is_digit(parser.buffer, parser.buffer_pos) { + // Check if the number is too long. + length++ + if length > max_number_length { + return yaml_parser_set_scanner_error(parser, "while scanning a %YAML directive", + start_mark, "found extremely long version number") + } + value = value*10 + int8(as_digit(parser.buffer, parser.buffer_pos)) + skip(parser) + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + } + + // Check if the number was present. + if length == 0 { + return yaml_parser_set_scanner_error(parser, "while scanning a %YAML directive", + start_mark, "did not find expected version number") + } + *number = value + return true +} + +// Scan the value of a TAG-DIRECTIVE token. +// +// Scope: +// %TAG !yaml! tag:yaml.org,2002: \n +// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +// +func yaml_parser_scan_tag_directive_value(parser *yaml_parser_t, start_mark yaml_mark_t, handle, prefix *[]byte) bool { + var handle_value, prefix_value []byte + + // Eat whitespaces. + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + + for is_blank(parser.buffer, parser.buffer_pos) { + skip(parser) + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + } + + // Scan a handle. + if !yaml_parser_scan_tag_handle(parser, true, start_mark, &handle_value) { + return false + } + + // Expect a whitespace. + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + if !is_blank(parser.buffer, parser.buffer_pos) { + yaml_parser_set_scanner_error(parser, "while scanning a %TAG directive", + start_mark, "did not find expected whitespace") + return false + } + + // Eat whitespaces. + for is_blank(parser.buffer, parser.buffer_pos) { + skip(parser) + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + } + + // Scan a prefix. + if !yaml_parser_scan_tag_uri(parser, true, nil, start_mark, &prefix_value) { + return false + } + + // Expect a whitespace or line break. + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + if !is_blankz(parser.buffer, parser.buffer_pos) { + yaml_parser_set_scanner_error(parser, "while scanning a %TAG directive", + start_mark, "did not find expected whitespace or line break") + return false + } + + *handle = handle_value + *prefix = prefix_value + return true +} + +func yaml_parser_scan_anchor(parser *yaml_parser_t, token *yaml_token_t, typ yaml_token_type_t) bool { + var s []byte + + // Eat the indicator character. + start_mark := parser.mark + skip(parser) + + // Consume the value. + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + + for is_alpha(parser.buffer, parser.buffer_pos) { + s = read(parser, s) + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + } + + end_mark := parser.mark + + /* + * Check if length of the anchor is greater than 0 and it is followed by + * a whitespace character or one of the indicators: + * + * '?', ':', ',', ']', '}', '%', '@', '`'. + */ + + if len(s) == 0 || + !(is_blankz(parser.buffer, parser.buffer_pos) || parser.buffer[parser.buffer_pos] == '?' || + parser.buffer[parser.buffer_pos] == ':' || parser.buffer[parser.buffer_pos] == ',' || + parser.buffer[parser.buffer_pos] == ']' || parser.buffer[parser.buffer_pos] == '}' || + parser.buffer[parser.buffer_pos] == '%' || parser.buffer[parser.buffer_pos] == '@' || + parser.buffer[parser.buffer_pos] == '`') { + context := "while scanning an alias" + if typ == yaml_ANCHOR_TOKEN { + context = "while scanning an anchor" + } + yaml_parser_set_scanner_error(parser, context, start_mark, + "did not find expected alphabetic or numeric character") + return false + } + + // Create a token. + *token = yaml_token_t{ + typ: typ, + start_mark: start_mark, + end_mark: end_mark, + value: s, + } + + return true +} + +/* + * Scan a TAG token. + */ + +func yaml_parser_scan_tag(parser *yaml_parser_t, token *yaml_token_t) bool { + var handle, suffix []byte + + start_mark := parser.mark + + // Check if the tag is in the canonical form. + if parser.unread < 2 && !yaml_parser_update_buffer(parser, 2) { + return false + } + + if parser.buffer[parser.buffer_pos+1] == '<' { + // Keep the handle as '' + + // Eat '!<' + skip(parser) + skip(parser) + + // Consume the tag value. + if !yaml_parser_scan_tag_uri(parser, false, nil, start_mark, &suffix) { + return false + } + + // Check for '>' and eat it. + if parser.buffer[parser.buffer_pos] != '>' { + yaml_parser_set_scanner_error(parser, "while scanning a tag", + start_mark, "did not find the expected '>'") + return false + } + + skip(parser) + } else { + // The tag has either the '!suffix' or the '!handle!suffix' form. + + // First, try to scan a handle. + if !yaml_parser_scan_tag_handle(parser, false, start_mark, &handle) { + return false + } + + // Check if it is, indeed, handle. + if handle[0] == '!' && len(handle) > 1 && handle[len(handle)-1] == '!' { + // Scan the suffix now. + if !yaml_parser_scan_tag_uri(parser, false, nil, start_mark, &suffix) { + return false + } + } else { + // It wasn't a handle after all. Scan the rest of the tag. + if !yaml_parser_scan_tag_uri(parser, false, handle, start_mark, &suffix) { + return false + } + + // Set the handle to '!'. + handle = []byte{'!'} + + // A special case: the '!' tag. Set the handle to '' and the + // suffix to '!'. + if len(suffix) == 0 { + handle, suffix = suffix, handle + } + } + } + + // Check the character which ends the tag. + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + if !is_blankz(parser.buffer, parser.buffer_pos) { + yaml_parser_set_scanner_error(parser, "while scanning a tag", + start_mark, "did not find expected whitespace or line break") + return false + } + + end_mark := parser.mark + + // Create a token. + *token = yaml_token_t{ + typ: yaml_TAG_TOKEN, + start_mark: start_mark, + end_mark: end_mark, + value: handle, + suffix: suffix, + } + return true +} + +// Scan a tag handle. +func yaml_parser_scan_tag_handle(parser *yaml_parser_t, directive bool, start_mark yaml_mark_t, handle *[]byte) bool { + // Check the initial '!' character. + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + if parser.buffer[parser.buffer_pos] != '!' { + yaml_parser_set_scanner_tag_error(parser, directive, + start_mark, "did not find expected '!'") + return false + } + + var s []byte + + // Copy the '!' character. + s = read(parser, s) + + // Copy all subsequent alphabetical and numerical characters. + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + for is_alpha(parser.buffer, parser.buffer_pos) { + s = read(parser, s) + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + } + + // Check if the trailing character is '!' and copy it. + if parser.buffer[parser.buffer_pos] == '!' { + s = read(parser, s) + } else { + // It's either the '!' tag or not really a tag handle. If it's a %TAG + // directive, it's an error. If it's a tag token, it must be a part of URI. + if directive && string(s) != "!" { + yaml_parser_set_scanner_tag_error(parser, directive, + start_mark, "did not find expected '!'") + return false + } + } + + *handle = s + return true +} + +// Scan a tag. +func yaml_parser_scan_tag_uri(parser *yaml_parser_t, directive bool, head []byte, start_mark yaml_mark_t, uri *[]byte) bool { + //size_t length = head ? strlen((char *)head) : 0 + var s []byte + hasTag := len(head) > 0 + + // Copy the head if needed. + // + // Note that we don't copy the leading '!' character. + if len(head) > 1 { + s = append(s, head[1:]...) + } + + // Scan the tag. + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + + // The set of characters that may appear in URI is as follows: + // + // '0'-'9', 'A'-'Z', 'a'-'z', '_', '-', ';', '/', '?', ':', '@', '&', + // '=', '+', '$', ',', '.', '!', '~', '*', '\'', '(', ')', '[', ']', + // '%'. + // [Go] Convert this into more reasonable logic. + for is_alpha(parser.buffer, parser.buffer_pos) || parser.buffer[parser.buffer_pos] == ';' || + parser.buffer[parser.buffer_pos] == '/' || parser.buffer[parser.buffer_pos] == '?' || + parser.buffer[parser.buffer_pos] == ':' || parser.buffer[parser.buffer_pos] == '@' || + parser.buffer[parser.buffer_pos] == '&' || parser.buffer[parser.buffer_pos] == '=' || + parser.buffer[parser.buffer_pos] == '+' || parser.buffer[parser.buffer_pos] == '$' || + parser.buffer[parser.buffer_pos] == ',' || parser.buffer[parser.buffer_pos] == '.' || + parser.buffer[parser.buffer_pos] == '!' || parser.buffer[parser.buffer_pos] == '~' || + parser.buffer[parser.buffer_pos] == '*' || parser.buffer[parser.buffer_pos] == '\'' || + parser.buffer[parser.buffer_pos] == '(' || parser.buffer[parser.buffer_pos] == ')' || + parser.buffer[parser.buffer_pos] == '[' || parser.buffer[parser.buffer_pos] == ']' || + parser.buffer[parser.buffer_pos] == '%' { + // Check if it is a URI-escape sequence. + if parser.buffer[parser.buffer_pos] == '%' { + if !yaml_parser_scan_uri_escapes(parser, directive, start_mark, &s) { + return false + } + } else { + s = read(parser, s) + } + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + hasTag = true + } + + if !hasTag { + yaml_parser_set_scanner_tag_error(parser, directive, + start_mark, "did not find expected tag URI") + return false + } + *uri = s + return true +} + +// Decode an URI-escape sequence corresponding to a single UTF-8 character. +func yaml_parser_scan_uri_escapes(parser *yaml_parser_t, directive bool, start_mark yaml_mark_t, s *[]byte) bool { + + // Decode the required number of characters. + w := 1024 + for w > 0 { + // Check for a URI-escaped octet. + if parser.unread < 3 && !yaml_parser_update_buffer(parser, 3) { + return false + } + + if !(parser.buffer[parser.buffer_pos] == '%' && + is_hex(parser.buffer, parser.buffer_pos+1) && + is_hex(parser.buffer, parser.buffer_pos+2)) { + return yaml_parser_set_scanner_tag_error(parser, directive, + start_mark, "did not find URI escaped octet") + } + + // Get the octet. + octet := byte((as_hex(parser.buffer, parser.buffer_pos+1) << 4) + as_hex(parser.buffer, parser.buffer_pos+2)) + + // If it is the leading octet, determine the length of the UTF-8 sequence. + if w == 1024 { + w = width(octet) + if w == 0 { + return yaml_parser_set_scanner_tag_error(parser, directive, + start_mark, "found an incorrect leading UTF-8 octet") + } + } else { + // Check if the trailing octet is correct. + if octet&0xC0 != 0x80 { + return yaml_parser_set_scanner_tag_error(parser, directive, + start_mark, "found an incorrect trailing UTF-8 octet") + } + } + + // Copy the octet and move the pointers. + *s = append(*s, octet) + skip(parser) + skip(parser) + skip(parser) + w-- + } + return true +} + +// Scan a block scalar. +func yaml_parser_scan_block_scalar(parser *yaml_parser_t, token *yaml_token_t, literal bool) bool { + // Eat the indicator '|' or '>'. + start_mark := parser.mark + skip(parser) + + // Scan the additional block scalar indicators. + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + + // Check for a chomping indicator. + var chomping, increment int + if parser.buffer[parser.buffer_pos] == '+' || parser.buffer[parser.buffer_pos] == '-' { + // Set the chomping method and eat the indicator. + if parser.buffer[parser.buffer_pos] == '+' { + chomping = +1 + } else { + chomping = -1 + } + skip(parser) + + // Check for an indentation indicator. + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + if is_digit(parser.buffer, parser.buffer_pos) { + // Check that the indentation is greater than 0. + if parser.buffer[parser.buffer_pos] == '0' { + yaml_parser_set_scanner_error(parser, "while scanning a block scalar", + start_mark, "found an indentation indicator equal to 0") + return false + } + + // Get the indentation level and eat the indicator. + increment = as_digit(parser.buffer, parser.buffer_pos) + skip(parser) + } + + } else if is_digit(parser.buffer, parser.buffer_pos) { + // Do the same as above, but in the opposite order. + + if parser.buffer[parser.buffer_pos] == '0' { + yaml_parser_set_scanner_error(parser, "while scanning a block scalar", + start_mark, "found an indentation indicator equal to 0") + return false + } + increment = as_digit(parser.buffer, parser.buffer_pos) + skip(parser) + + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + if parser.buffer[parser.buffer_pos] == '+' || parser.buffer[parser.buffer_pos] == '-' { + if parser.buffer[parser.buffer_pos] == '+' { + chomping = +1 + } else { + chomping = -1 + } + skip(parser) + } + } + + // Eat whitespaces and comments to the end of the line. + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + for is_blank(parser.buffer, parser.buffer_pos) { + skip(parser) + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + } + if parser.buffer[parser.buffer_pos] == '#' { + for !is_breakz(parser.buffer, parser.buffer_pos) { + skip(parser) + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + } + } + + // Check if we are at the end of the line. + if !is_breakz(parser.buffer, parser.buffer_pos) { + yaml_parser_set_scanner_error(parser, "while scanning a block scalar", + start_mark, "did not find expected comment or line break") + return false + } + + // Eat a line break. + if is_break(parser.buffer, parser.buffer_pos) { + if parser.unread < 2 && !yaml_parser_update_buffer(parser, 2) { + return false + } + skip_line(parser) + } + + end_mark := parser.mark + + // Set the indentation level if it was specified. + var indent int + if increment > 0 { + if parser.indent >= 0 { + indent = parser.indent + increment + } else { + indent = increment + } + } + + // Scan the leading line breaks and determine the indentation level if needed. + var s, leading_break, trailing_breaks []byte + if !yaml_parser_scan_block_scalar_breaks(parser, &indent, &trailing_breaks, start_mark, &end_mark) { + return false + } + + // Scan the block scalar content. + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + var leading_blank, trailing_blank bool + for parser.mark.column == indent && !is_z(parser.buffer, parser.buffer_pos) { + // We are at the beginning of a non-empty line. + + // Is it a trailing whitespace? + trailing_blank = is_blank(parser.buffer, parser.buffer_pos) + + // Check if we need to fold the leading line break. + if !literal && !leading_blank && !trailing_blank && len(leading_break) > 0 && leading_break[0] == '\n' { + // Do we need to join the lines by space? + if len(trailing_breaks) == 0 { + s = append(s, ' ') + } + } else { + s = append(s, leading_break...) + } + leading_break = leading_break[:0] + + // Append the remaining line breaks. + s = append(s, trailing_breaks...) + trailing_breaks = trailing_breaks[:0] + + // Is it a leading whitespace? + leading_blank = is_blank(parser.buffer, parser.buffer_pos) + + // Consume the current line. + for !is_breakz(parser.buffer, parser.buffer_pos) { + s = read(parser, s) + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + } + + // Consume the line break. + if parser.unread < 2 && !yaml_parser_update_buffer(parser, 2) { + return false + } + + leading_break = read_line(parser, leading_break) + + // Eat the following indentation spaces and line breaks. + if !yaml_parser_scan_block_scalar_breaks(parser, &indent, &trailing_breaks, start_mark, &end_mark) { + return false + } + } + + // Chomp the tail. + if chomping != -1 { + s = append(s, leading_break...) + } + if chomping == 1 { + s = append(s, trailing_breaks...) + } + + // Create a token. + *token = yaml_token_t{ + typ: yaml_SCALAR_TOKEN, + start_mark: start_mark, + end_mark: end_mark, + value: s, + style: yaml_LITERAL_SCALAR_STYLE, + } + if !literal { + token.style = yaml_FOLDED_SCALAR_STYLE + } + return true +} + +// Scan indentation spaces and line breaks for a block scalar. Determine the +// indentation level if needed. +func yaml_parser_scan_block_scalar_breaks(parser *yaml_parser_t, indent *int, breaks *[]byte, start_mark yaml_mark_t, end_mark *yaml_mark_t) bool { + *end_mark = parser.mark + + // Eat the indentation spaces and line breaks. + max_indent := 0 + for { + // Eat the indentation spaces. + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + for (*indent == 0 || parser.mark.column < *indent) && is_space(parser.buffer, parser.buffer_pos) { + skip(parser) + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + } + if parser.mark.column > max_indent { + max_indent = parser.mark.column + } + + // Check for a tab character messing the indentation. + if (*indent == 0 || parser.mark.column < *indent) && is_tab(parser.buffer, parser.buffer_pos) { + return yaml_parser_set_scanner_error(parser, "while scanning a block scalar", + start_mark, "found a tab character where an indentation space is expected") + } + + // Have we found a non-empty line? + if !is_break(parser.buffer, parser.buffer_pos) { + break + } + + // Consume the line break. + if parser.unread < 2 && !yaml_parser_update_buffer(parser, 2) { + return false + } + // [Go] Should really be returning breaks instead. + *breaks = read_line(parser, *breaks) + *end_mark = parser.mark + } + + // Determine the indentation level if needed. + if *indent == 0 { + *indent = max_indent + if *indent < parser.indent+1 { + *indent = parser.indent + 1 + } + if *indent < 1 { + *indent = 1 + } + } + return true +} + +// Scan a quoted scalar. +func yaml_parser_scan_flow_scalar(parser *yaml_parser_t, token *yaml_token_t, single bool) bool { + // Eat the left quote. + start_mark := parser.mark + skip(parser) + + // Consume the content of the quoted scalar. + var s, leading_break, trailing_breaks, whitespaces []byte + for { + // Check that there are no document indicators at the beginning of the line. + if parser.unread < 4 && !yaml_parser_update_buffer(parser, 4) { + return false + } + + if parser.mark.column == 0 && + ((parser.buffer[parser.buffer_pos+0] == '-' && + parser.buffer[parser.buffer_pos+1] == '-' && + parser.buffer[parser.buffer_pos+2] == '-') || + (parser.buffer[parser.buffer_pos+0] == '.' && + parser.buffer[parser.buffer_pos+1] == '.' && + parser.buffer[parser.buffer_pos+2] == '.')) && + is_blankz(parser.buffer, parser.buffer_pos+3) { + yaml_parser_set_scanner_error(parser, "while scanning a quoted scalar", + start_mark, "found unexpected document indicator") + return false + } + + // Check for EOF. + if is_z(parser.buffer, parser.buffer_pos) { + yaml_parser_set_scanner_error(parser, "while scanning a quoted scalar", + start_mark, "found unexpected end of stream") + return false + } + + // Consume non-blank characters. + leading_blanks := false + for !is_blankz(parser.buffer, parser.buffer_pos) { + if single && parser.buffer[parser.buffer_pos] == '\'' && parser.buffer[parser.buffer_pos+1] == '\'' { + // Is is an escaped single quote. + s = append(s, '\'') + skip(parser) + skip(parser) + + } else if single && parser.buffer[parser.buffer_pos] == '\'' { + // It is a right single quote. + break + } else if !single && parser.buffer[parser.buffer_pos] == '"' { + // It is a right double quote. + break + + } else if !single && parser.buffer[parser.buffer_pos] == '\\' && is_break(parser.buffer, parser.buffer_pos+1) { + // It is an escaped line break. + if parser.unread < 3 && !yaml_parser_update_buffer(parser, 3) { + return false + } + skip(parser) + skip_line(parser) + leading_blanks = true + break + + } else if !single && parser.buffer[parser.buffer_pos] == '\\' { + // It is an escape sequence. + code_length := 0 + + // Check the escape character. + switch parser.buffer[parser.buffer_pos+1] { + case '0': + s = append(s, 0) + case 'a': + s = append(s, '\x07') + case 'b': + s = append(s, '\x08') + case 't', '\t': + s = append(s, '\x09') + case 'n': + s = append(s, '\x0A') + case 'v': + s = append(s, '\x0B') + case 'f': + s = append(s, '\x0C') + case 'r': + s = append(s, '\x0D') + case 'e': + s = append(s, '\x1B') + case ' ': + s = append(s, '\x20') + case '"': + s = append(s, '"') + case '\'': + s = append(s, '\'') + case '\\': + s = append(s, '\\') + case 'N': // NEL (#x85) + s = append(s, '\xC2') + s = append(s, '\x85') + case '_': // #xA0 + s = append(s, '\xC2') + s = append(s, '\xA0') + case 'L': // LS (#x2028) + s = append(s, '\xE2') + s = append(s, '\x80') + s = append(s, '\xA8') + case 'P': // PS (#x2029) + s = append(s, '\xE2') + s = append(s, '\x80') + s = append(s, '\xA9') + case 'x': + code_length = 2 + case 'u': + code_length = 4 + case 'U': + code_length = 8 + default: + yaml_parser_set_scanner_error(parser, "while parsing a quoted scalar", + start_mark, "found unknown escape character") + return false + } + + skip(parser) + skip(parser) + + // Consume an arbitrary escape code. + if code_length > 0 { + var value int + + // Scan the character value. + if parser.unread < code_length && !yaml_parser_update_buffer(parser, code_length) { + return false + } + for k := 0; k < code_length; k++ { + if !is_hex(parser.buffer, parser.buffer_pos+k) { + yaml_parser_set_scanner_error(parser, "while parsing a quoted scalar", + start_mark, "did not find expected hexdecimal number") + return false + } + value = (value << 4) + as_hex(parser.buffer, parser.buffer_pos+k) + } + + // Check the value and write the character. + if (value >= 0xD800 && value <= 0xDFFF) || value > 0x10FFFF { + yaml_parser_set_scanner_error(parser, "while parsing a quoted scalar", + start_mark, "found invalid Unicode character escape code") + return false + } + if value <= 0x7F { + s = append(s, byte(value)) + } else if value <= 0x7FF { + s = append(s, byte(0xC0+(value>>6))) + s = append(s, byte(0x80+(value&0x3F))) + } else if value <= 0xFFFF { + s = append(s, byte(0xE0+(value>>12))) + s = append(s, byte(0x80+((value>>6)&0x3F))) + s = append(s, byte(0x80+(value&0x3F))) + } else { + s = append(s, byte(0xF0+(value>>18))) + s = append(s, byte(0x80+((value>>12)&0x3F))) + s = append(s, byte(0x80+((value>>6)&0x3F))) + s = append(s, byte(0x80+(value&0x3F))) + } + + // Advance the pointer. + for k := 0; k < code_length; k++ { + skip(parser) + } + } + } else { + // It is a non-escaped non-blank character. + s = read(parser, s) + } + if parser.unread < 2 && !yaml_parser_update_buffer(parser, 2) { + return false + } + } + + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + + // Check if we are at the end of the scalar. + if single { + if parser.buffer[parser.buffer_pos] == '\'' { + break + } + } else { + if parser.buffer[parser.buffer_pos] == '"' { + break + } + } + + // Consume blank characters. + for is_blank(parser.buffer, parser.buffer_pos) || is_break(parser.buffer, parser.buffer_pos) { + if is_blank(parser.buffer, parser.buffer_pos) { + // Consume a space or a tab character. + if !leading_blanks { + whitespaces = read(parser, whitespaces) + } else { + skip(parser) + } + } else { + if parser.unread < 2 && !yaml_parser_update_buffer(parser, 2) { + return false + } + + // Check if it is a first line break. + if !leading_blanks { + whitespaces = whitespaces[:0] + leading_break = read_line(parser, leading_break) + leading_blanks = true + } else { + trailing_breaks = read_line(parser, trailing_breaks) + } + } + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + } + + // Join the whitespaces or fold line breaks. + if leading_blanks { + // Do we need to fold line breaks? + if len(leading_break) > 0 && leading_break[0] == '\n' { + if len(trailing_breaks) == 0 { + s = append(s, ' ') + } else { + s = append(s, trailing_breaks...) + } + } else { + s = append(s, leading_break...) + s = append(s, trailing_breaks...) + } + trailing_breaks = trailing_breaks[:0] + leading_break = leading_break[:0] + } else { + s = append(s, whitespaces...) + whitespaces = whitespaces[:0] + } + } + + // Eat the right quote. + skip(parser) + end_mark := parser.mark + + // Create a token. + *token = yaml_token_t{ + typ: yaml_SCALAR_TOKEN, + start_mark: start_mark, + end_mark: end_mark, + value: s, + style: yaml_SINGLE_QUOTED_SCALAR_STYLE, + } + if !single { + token.style = yaml_DOUBLE_QUOTED_SCALAR_STYLE + } + return true +} + +// Scan a plain scalar. +func yaml_parser_scan_plain_scalar(parser *yaml_parser_t, token *yaml_token_t) bool { + + var s, leading_break, trailing_breaks, whitespaces []byte + var leading_blanks bool + var indent = parser.indent + 1 + + start_mark := parser.mark + end_mark := parser.mark + + // Consume the content of the plain scalar. + for { + // Check for a document indicator. + if parser.unread < 4 && !yaml_parser_update_buffer(parser, 4) { + return false + } + if parser.mark.column == 0 && + ((parser.buffer[parser.buffer_pos+0] == '-' && + parser.buffer[parser.buffer_pos+1] == '-' && + parser.buffer[parser.buffer_pos+2] == '-') || + (parser.buffer[parser.buffer_pos+0] == '.' && + parser.buffer[parser.buffer_pos+1] == '.' && + parser.buffer[parser.buffer_pos+2] == '.')) && + is_blankz(parser.buffer, parser.buffer_pos+3) { + break + } + + // Check for a comment. + if parser.buffer[parser.buffer_pos] == '#' { + break + } + + // Consume non-blank characters. + for !is_blankz(parser.buffer, parser.buffer_pos) { + + // Check for indicators that may end a plain scalar. + if (parser.buffer[parser.buffer_pos] == ':' && is_blankz(parser.buffer, parser.buffer_pos+1)) || + (parser.flow_level > 0 && + (parser.buffer[parser.buffer_pos] == ',' || + parser.buffer[parser.buffer_pos] == '?' || parser.buffer[parser.buffer_pos] == '[' || + parser.buffer[parser.buffer_pos] == ']' || parser.buffer[parser.buffer_pos] == '{' || + parser.buffer[parser.buffer_pos] == '}')) { + break + } + + // Check if we need to join whitespaces and breaks. + if leading_blanks || len(whitespaces) > 0 { + if leading_blanks { + // Do we need to fold line breaks? + if leading_break[0] == '\n' { + if len(trailing_breaks) == 0 { + s = append(s, ' ') + } else { + s = append(s, trailing_breaks...) + } + } else { + s = append(s, leading_break...) + s = append(s, trailing_breaks...) + } + trailing_breaks = trailing_breaks[:0] + leading_break = leading_break[:0] + leading_blanks = false + } else { + s = append(s, whitespaces...) + whitespaces = whitespaces[:0] + } + } + + // Copy the character. + s = read(parser, s) + + end_mark = parser.mark + if parser.unread < 2 && !yaml_parser_update_buffer(parser, 2) { + return false + } + } + + // Is it the end? + if !(is_blank(parser.buffer, parser.buffer_pos) || is_break(parser.buffer, parser.buffer_pos)) { + break + } + + // Consume blank characters. + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + + for is_blank(parser.buffer, parser.buffer_pos) || is_break(parser.buffer, parser.buffer_pos) { + if is_blank(parser.buffer, parser.buffer_pos) { + + // Check for tab characters that abuse indentation. + if leading_blanks && parser.mark.column < indent && is_tab(parser.buffer, parser.buffer_pos) { + yaml_parser_set_scanner_error(parser, "while scanning a plain scalar", + start_mark, "found a tab character that violates indentation") + return false + } + + // Consume a space or a tab character. + if !leading_blanks { + whitespaces = read(parser, whitespaces) + } else { + skip(parser) + } + } else { + if parser.unread < 2 && !yaml_parser_update_buffer(parser, 2) { + return false + } + + // Check if it is a first line break. + if !leading_blanks { + whitespaces = whitespaces[:0] + leading_break = read_line(parser, leading_break) + leading_blanks = true + } else { + trailing_breaks = read_line(parser, trailing_breaks) + } + } + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + } + + // Check indentation level. + if parser.flow_level == 0 && parser.mark.column < indent { + break + } + } + + // Create a token. + *token = yaml_token_t{ + typ: yaml_SCALAR_TOKEN, + start_mark: start_mark, + end_mark: end_mark, + value: s, + style: yaml_PLAIN_SCALAR_STYLE, + } + + // Note that we change the 'simple_key_allowed' flag. + if leading_blanks { + parser.simple_key_allowed = true + } + return true +} diff --git a/vendor/gopkg.in/yaml.v2/sorter.go b/vendor/gopkg.in/yaml.v2/sorter.go new file mode 100644 index 000000000..4c45e660a --- /dev/null +++ b/vendor/gopkg.in/yaml.v2/sorter.go @@ -0,0 +1,113 @@ +package yaml + +import ( + "reflect" + "unicode" +) + +type keyList []reflect.Value + +func (l keyList) Len() int { return len(l) } +func (l keyList) Swap(i, j int) { l[i], l[j] = l[j], l[i] } +func (l keyList) Less(i, j int) bool { + a := l[i] + b := l[j] + ak := a.Kind() + bk := b.Kind() + for (ak == reflect.Interface || ak == reflect.Ptr) && !a.IsNil() { + a = a.Elem() + ak = a.Kind() + } + for (bk == reflect.Interface || bk == reflect.Ptr) && !b.IsNil() { + b = b.Elem() + bk = b.Kind() + } + af, aok := keyFloat(a) + bf, bok := keyFloat(b) + if aok && bok { + if af != bf { + return af < bf + } + if ak != bk { + return ak < bk + } + return numLess(a, b) + } + if ak != reflect.String || bk != reflect.String { + return ak < bk + } + ar, br := []rune(a.String()), []rune(b.String()) + for i := 0; i < len(ar) && i < len(br); i++ { + if ar[i] == br[i] { + continue + } + al := unicode.IsLetter(ar[i]) + bl := unicode.IsLetter(br[i]) + if al && bl { + return ar[i] < br[i] + } + if al || bl { + return bl + } + var ai, bi int + var an, bn int64 + if ar[i] == '0' || br[i] == '0' { + for j := i-1; j >= 0 && unicode.IsDigit(ar[j]); j-- { + if ar[j] != '0' { + an = 1 + bn = 1 + break + } + } + } + for ai = i; ai < len(ar) && unicode.IsDigit(ar[ai]); ai++ { + an = an*10 + int64(ar[ai]-'0') + } + for bi = i; bi < len(br) && unicode.IsDigit(br[bi]); bi++ { + bn = bn*10 + int64(br[bi]-'0') + } + if an != bn { + return an < bn + } + if ai != bi { + return ai < bi + } + return ar[i] < br[i] + } + return len(ar) < len(br) +} + +// keyFloat returns a float value for v if it is a number/bool +// and whether it is a number/bool or not. +func keyFloat(v reflect.Value) (f float64, ok bool) { + switch v.Kind() { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return float64(v.Int()), true + case reflect.Float32, reflect.Float64: + return v.Float(), true + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + return float64(v.Uint()), true + case reflect.Bool: + if v.Bool() { + return 1, true + } + return 0, true + } + return 0, false +} + +// numLess returns whether a < b. +// a and b must necessarily have the same kind. +func numLess(a, b reflect.Value) bool { + switch a.Kind() { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return a.Int() < b.Int() + case reflect.Float32, reflect.Float64: + return a.Float() < b.Float() + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + return a.Uint() < b.Uint() + case reflect.Bool: + return !a.Bool() && b.Bool() + } + panic("not a number") +} diff --git a/vendor/gopkg.in/yaml.v2/writerc.go b/vendor/gopkg.in/yaml.v2/writerc.go new file mode 100644 index 000000000..a2dde608c --- /dev/null +++ b/vendor/gopkg.in/yaml.v2/writerc.go @@ -0,0 +1,26 @@ +package yaml + +// Set the writer error and return false. +func yaml_emitter_set_writer_error(emitter *yaml_emitter_t, problem string) bool { + emitter.error = yaml_WRITER_ERROR + emitter.problem = problem + return false +} + +// Flush the output buffer. +func yaml_emitter_flush(emitter *yaml_emitter_t) bool { + if emitter.write_handler == nil { + panic("write handler not set") + } + + // Check if the buffer is empty. + if emitter.buffer_pos == 0 { + return true + } + + if err := emitter.write_handler(emitter, emitter.buffer[:emitter.buffer_pos]); err != nil { + return yaml_emitter_set_writer_error(emitter, "write error: "+err.Error()) + } + emitter.buffer_pos = 0 + return true +} diff --git a/vendor/gopkg.in/yaml.v2/yaml.go b/vendor/gopkg.in/yaml.v2/yaml.go new file mode 100644 index 000000000..30813884c --- /dev/null +++ b/vendor/gopkg.in/yaml.v2/yaml.go @@ -0,0 +1,478 @@ +// Package yaml implements YAML support for the Go language. +// +// Source code and other details for the project are available at GitHub: +// +// https://github.com/go-yaml/yaml +// +package yaml + +import ( + "errors" + "fmt" + "io" + "reflect" + "strings" + "sync" +) + +// MapSlice encodes and decodes as a YAML map. +// The order of keys is preserved when encoding and decoding. +type MapSlice []MapItem + +// MapItem is an item in a MapSlice. +type MapItem struct { + Key, Value interface{} +} + +// The Unmarshaler interface may be implemented by types to customize their +// behavior when being unmarshaled from a YAML document. The UnmarshalYAML +// method receives a function that may be called to unmarshal the original +// YAML value into a field or variable. It is safe to call the unmarshal +// function parameter more than once if necessary. +type Unmarshaler interface { + UnmarshalYAML(unmarshal func(interface{}) error) error +} + +// The Marshaler interface may be implemented by types to customize their +// behavior when being marshaled into a YAML document. The returned value +// is marshaled in place of the original value implementing Marshaler. +// +// If an error is returned by MarshalYAML, the marshaling procedure stops +// and returns with the provided error. +type Marshaler interface { + MarshalYAML() (interface{}, error) +} + +// Unmarshal decodes the first document found within the in byte slice +// and assigns decoded values into the out value. +// +// Maps and pointers (to a struct, string, int, etc) are accepted as out +// values. If an internal pointer within a struct is not initialized, +// the yaml package will initialize it if necessary for unmarshalling +// the provided data. The out parameter must not be nil. +// +// The type of the decoded values should be compatible with the respective +// values in out. If one or more values cannot be decoded due to a type +// mismatches, decoding continues partially until the end of the YAML +// content, and a *yaml.TypeError is returned with details for all +// missed values. +// +// Struct fields are only unmarshalled if they are exported (have an +// upper case first letter), and are unmarshalled using the field name +// lowercased as the default key. Custom keys may be defined via the +// "yaml" name in the field tag: the content preceding the first comma +// is used as the key, and the following comma-separated options are +// used to tweak the marshalling process (see Marshal). +// Conflicting names result in a runtime error. +// +// For example: +// +// type T struct { +// F int `yaml:"a,omitempty"` +// B int +// } +// var t T +// yaml.Unmarshal([]byte("a: 1\nb: 2"), &t) +// +// See the documentation of Marshal for the format of tags and a list of +// supported tag options. +// +func Unmarshal(in []byte, out interface{}) (err error) { + return unmarshal(in, out, false) +} + +// UnmarshalStrict is like Unmarshal except that any fields that are found +// in the data that do not have corresponding struct members, or mapping +// keys that are duplicates, will result in +// an error. +func UnmarshalStrict(in []byte, out interface{}) (err error) { + return unmarshal(in, out, true) +} + +// A Decoder reads and decodes YAML values from an input stream. +type Decoder struct { + strict bool + parser *parser +} + +// NewDecoder returns a new decoder that reads from r. +// +// The decoder introduces its own buffering and may read +// data from r beyond the YAML values requested. +func NewDecoder(r io.Reader) *Decoder { + return &Decoder{ + parser: newParserFromReader(r), + } +} + +// SetStrict sets whether strict decoding behaviour is enabled when +// decoding items in the data (see UnmarshalStrict). By default, decoding is not strict. +func (dec *Decoder) SetStrict(strict bool) { + dec.strict = strict +} + +// Decode reads the next YAML-encoded value from its input +// and stores it in the value pointed to by v. +// +// See the documentation for Unmarshal for details about the +// conversion of YAML into a Go value. +func (dec *Decoder) Decode(v interface{}) (err error) { + d := newDecoder(dec.strict) + defer handleErr(&err) + node := dec.parser.parse() + if node == nil { + return io.EOF + } + out := reflect.ValueOf(v) + if out.Kind() == reflect.Ptr && !out.IsNil() { + out = out.Elem() + } + d.unmarshal(node, out) + if len(d.terrors) > 0 { + return &TypeError{d.terrors} + } + return nil +} + +func unmarshal(in []byte, out interface{}, strict bool) (err error) { + defer handleErr(&err) + d := newDecoder(strict) + p := newParser(in) + defer p.destroy() + node := p.parse() + if node != nil { + v := reflect.ValueOf(out) + if v.Kind() == reflect.Ptr && !v.IsNil() { + v = v.Elem() + } + d.unmarshal(node, v) + } + if len(d.terrors) > 0 { + return &TypeError{d.terrors} + } + return nil +} + +// Marshal serializes the value provided into a YAML document. The structure +// of the generated document will reflect the structure of the value itself. +// Maps and pointers (to struct, string, int, etc) are accepted as the in value. +// +// Struct fields are only marshalled if they are exported (have an upper case +// first letter), and are marshalled using the field name lowercased as the +// default key. Custom keys may be defined via the "yaml" name in the field +// tag: the content preceding the first comma is used as the key, and the +// following comma-separated options are used to tweak the marshalling process. +// Conflicting names result in a runtime error. +// +// The field tag format accepted is: +// +// `(...) yaml:"[][,[,]]" (...)` +// +// The following flags are currently supported: +// +// omitempty Only include the field if it's not set to the zero +// value for the type or to empty slices or maps. +// Zero valued structs will be omitted if all their public +// fields are zero, unless they implement an IsZero +// method (see the IsZeroer interface type), in which +// case the field will be excluded if IsZero returns true. +// +// flow Marshal using a flow style (useful for structs, +// sequences and maps). +// +// inline Inline the field, which must be a struct or a map, +// causing all of its fields or keys to be processed as if +// they were part of the outer struct. For maps, keys must +// not conflict with the yaml keys of other struct fields. +// +// In addition, if the key is "-", the field is ignored. +// +// For example: +// +// type T struct { +// F int `yaml:"a,omitempty"` +// B int +// } +// yaml.Marshal(&T{B: 2}) // Returns "b: 2\n" +// yaml.Marshal(&T{F: 1}} // Returns "a: 1\nb: 0\n" +// +func Marshal(in interface{}) (out []byte, err error) { + defer handleErr(&err) + e := newEncoder() + defer e.destroy() + e.marshalDoc("", reflect.ValueOf(in)) + e.finish() + out = e.out + return +} + +// An Encoder writes YAML values to an output stream. +type Encoder struct { + encoder *encoder +} + +// NewEncoder returns a new encoder that writes to w. +// The Encoder should be closed after use to flush all data +// to w. +func NewEncoder(w io.Writer) *Encoder { + return &Encoder{ + encoder: newEncoderWithWriter(w), + } +} + +// Encode writes the YAML encoding of v to the stream. +// If multiple items are encoded to the stream, the +// second and subsequent document will be preceded +// with a "---" document separator, but the first will not. +// +// See the documentation for Marshal for details about the conversion of Go +// values to YAML. +func (e *Encoder) Encode(v interface{}) (err error) { + defer handleErr(&err) + e.encoder.marshalDoc("", reflect.ValueOf(v)) + return nil +} + +// Close closes the encoder by writing any remaining data. +// It does not write a stream terminating string "...". +func (e *Encoder) Close() (err error) { + defer handleErr(&err) + e.encoder.finish() + return nil +} + +func handleErr(err *error) { + if v := recover(); v != nil { + if e, ok := v.(yamlError); ok { + *err = e.err + } else { + panic(v) + } + } +} + +type yamlError struct { + err error +} + +func fail(err error) { + panic(yamlError{err}) +} + +func failf(format string, args ...interface{}) { + panic(yamlError{fmt.Errorf("yaml: "+format, args...)}) +} + +// A TypeError is returned by Unmarshal when one or more fields in +// the YAML document cannot be properly decoded into the requested +// types. When this error is returned, the value is still +// unmarshaled partially. +type TypeError struct { + Errors []string +} + +func (e *TypeError) Error() string { + return fmt.Sprintf("yaml: unmarshal errors:\n %s", strings.Join(e.Errors, "\n ")) +} + +// -------------------------------------------------------------------------- +// Maintain a mapping of keys to structure field indexes + +// The code in this section was copied from mgo/bson. + +// structInfo holds details for the serialization of fields of +// a given struct. +type structInfo struct { + FieldsMap map[string]fieldInfo + FieldsList []fieldInfo + + // InlineMap is the number of the field in the struct that + // contains an ,inline map, or -1 if there's none. + InlineMap int +} + +type fieldInfo struct { + Key string + Num int + OmitEmpty bool + Flow bool + // Id holds the unique field identifier, so we can cheaply + // check for field duplicates without maintaining an extra map. + Id int + + // Inline holds the field index if the field is part of an inlined struct. + Inline []int +} + +var structMap = make(map[reflect.Type]*structInfo) +var fieldMapMutex sync.RWMutex + +func getStructInfo(st reflect.Type) (*structInfo, error) { + fieldMapMutex.RLock() + sinfo, found := structMap[st] + fieldMapMutex.RUnlock() + if found { + return sinfo, nil + } + + n := st.NumField() + fieldsMap := make(map[string]fieldInfo) + fieldsList := make([]fieldInfo, 0, n) + inlineMap := -1 + for i := 0; i != n; i++ { + field := st.Field(i) + if field.PkgPath != "" && !field.Anonymous { + continue // Private field + } + + info := fieldInfo{Num: i} + + tag := field.Tag.Get("yaml") + if tag == "" && strings.Index(string(field.Tag), ":") < 0 { + tag = string(field.Tag) + } + if tag == "-" { + continue + } + + inline := false + fields := strings.Split(tag, ",") + if len(fields) > 1 { + for _, flag := range fields[1:] { + switch flag { + case "omitempty": + info.OmitEmpty = true + case "flow": + info.Flow = true + case "inline": + inline = true + default: + return nil, errors.New(fmt.Sprintf("Unsupported flag %q in tag %q of type %s", flag, tag, st)) + } + } + tag = fields[0] + } + + if inline { + switch field.Type.Kind() { + case reflect.Map: + if inlineMap >= 0 { + return nil, errors.New("Multiple ,inline maps in struct " + st.String()) + } + if field.Type.Key() != reflect.TypeOf("") { + return nil, errors.New("Option ,inline needs a map with string keys in struct " + st.String()) + } + inlineMap = info.Num + case reflect.Struct: + sinfo, err := getStructInfo(field.Type) + if err != nil { + return nil, err + } + for _, finfo := range sinfo.FieldsList { + if _, found := fieldsMap[finfo.Key]; found { + msg := "Duplicated key '" + finfo.Key + "' in struct " + st.String() + return nil, errors.New(msg) + } + if finfo.Inline == nil { + finfo.Inline = []int{i, finfo.Num} + } else { + finfo.Inline = append([]int{i}, finfo.Inline...) + } + finfo.Id = len(fieldsList) + fieldsMap[finfo.Key] = finfo + fieldsList = append(fieldsList, finfo) + } + default: + //return nil, errors.New("Option ,inline needs a struct value or map field") + return nil, errors.New("Option ,inline needs a struct value field") + } + continue + } + + if tag != "" { + info.Key = tag + } else { + info.Key = strings.ToLower(field.Name) + } + + if _, found = fieldsMap[info.Key]; found { + msg := "Duplicated key '" + info.Key + "' in struct " + st.String() + return nil, errors.New(msg) + } + + info.Id = len(fieldsList) + fieldsList = append(fieldsList, info) + fieldsMap[info.Key] = info + } + + sinfo = &structInfo{ + FieldsMap: fieldsMap, + FieldsList: fieldsList, + InlineMap: inlineMap, + } + + fieldMapMutex.Lock() + structMap[st] = sinfo + fieldMapMutex.Unlock() + return sinfo, nil +} + +// IsZeroer is used to check whether an object is zero to +// determine whether it should be omitted when marshaling +// with the omitempty flag. One notable implementation +// is time.Time. +type IsZeroer interface { + IsZero() bool +} + +func isZero(v reflect.Value) bool { + kind := v.Kind() + if z, ok := v.Interface().(IsZeroer); ok { + if (kind == reflect.Ptr || kind == reflect.Interface) && v.IsNil() { + return true + } + return z.IsZero() + } + switch kind { + case reflect.String: + return len(v.String()) == 0 + case reflect.Interface, reflect.Ptr: + return v.IsNil() + case reflect.Slice: + return v.Len() == 0 + case reflect.Map: + return v.Len() == 0 + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return v.Int() == 0 + case reflect.Float32, reflect.Float64: + return v.Float() == 0 + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + return v.Uint() == 0 + case reflect.Bool: + return !v.Bool() + case reflect.Struct: + vt := v.Type() + for i := v.NumField() - 1; i >= 0; i-- { + if vt.Field(i).PkgPath != "" { + continue // Private field + } + if !isZero(v.Field(i)) { + return false + } + } + return true + } + return false +} + +// FutureLineWrap globally disables line wrapping when encoding long strings. +// This is a temporary and thus deprecated method introduced to faciliate +// migration towards v3, which offers more control of line lengths on +// individual encodings, and has a default matching the behavior introduced +// by this function. +// +// The default formatting of v2 was erroneously changed in v2.3.0 and reverted +// in v2.4.0, at which point this function was introduced to help migration. +func FutureLineWrap() { + disableLineWrapping = true +} diff --git a/vendor/gopkg.in/yaml.v2/yamlh.go b/vendor/gopkg.in/yaml.v2/yamlh.go new file mode 100644 index 000000000..f6a9c8e34 --- /dev/null +++ b/vendor/gopkg.in/yaml.v2/yamlh.go @@ -0,0 +1,739 @@ +package yaml + +import ( + "fmt" + "io" +) + +// The version directive data. +type yaml_version_directive_t struct { + major int8 // The major version number. + minor int8 // The minor version number. +} + +// The tag directive data. +type yaml_tag_directive_t struct { + handle []byte // The tag handle. + prefix []byte // The tag prefix. +} + +type yaml_encoding_t int + +// The stream encoding. +const ( + // Let the parser choose the encoding. + yaml_ANY_ENCODING yaml_encoding_t = iota + + yaml_UTF8_ENCODING // The default UTF-8 encoding. + yaml_UTF16LE_ENCODING // The UTF-16-LE encoding with BOM. + yaml_UTF16BE_ENCODING // The UTF-16-BE encoding with BOM. +) + +type yaml_break_t int + +// Line break types. +const ( + // Let the parser choose the break type. + yaml_ANY_BREAK yaml_break_t = iota + + yaml_CR_BREAK // Use CR for line breaks (Mac style). + yaml_LN_BREAK // Use LN for line breaks (Unix style). + yaml_CRLN_BREAK // Use CR LN for line breaks (DOS style). +) + +type yaml_error_type_t int + +// Many bad things could happen with the parser and emitter. +const ( + // No error is produced. + yaml_NO_ERROR yaml_error_type_t = iota + + yaml_MEMORY_ERROR // Cannot allocate or reallocate a block of memory. + yaml_READER_ERROR // Cannot read or decode the input stream. + yaml_SCANNER_ERROR // Cannot scan the input stream. + yaml_PARSER_ERROR // Cannot parse the input stream. + yaml_COMPOSER_ERROR // Cannot compose a YAML document. + yaml_WRITER_ERROR // Cannot write to the output stream. + yaml_EMITTER_ERROR // Cannot emit a YAML stream. +) + +// The pointer position. +type yaml_mark_t struct { + index int // The position index. + line int // The position line. + column int // The position column. +} + +// Node Styles + +type yaml_style_t int8 + +type yaml_scalar_style_t yaml_style_t + +// Scalar styles. +const ( + // Let the emitter choose the style. + yaml_ANY_SCALAR_STYLE yaml_scalar_style_t = iota + + yaml_PLAIN_SCALAR_STYLE // The plain scalar style. + yaml_SINGLE_QUOTED_SCALAR_STYLE // The single-quoted scalar style. + yaml_DOUBLE_QUOTED_SCALAR_STYLE // The double-quoted scalar style. + yaml_LITERAL_SCALAR_STYLE // The literal scalar style. + yaml_FOLDED_SCALAR_STYLE // The folded scalar style. +) + +type yaml_sequence_style_t yaml_style_t + +// Sequence styles. +const ( + // Let the emitter choose the style. + yaml_ANY_SEQUENCE_STYLE yaml_sequence_style_t = iota + + yaml_BLOCK_SEQUENCE_STYLE // The block sequence style. + yaml_FLOW_SEQUENCE_STYLE // The flow sequence style. +) + +type yaml_mapping_style_t yaml_style_t + +// Mapping styles. +const ( + // Let the emitter choose the style. + yaml_ANY_MAPPING_STYLE yaml_mapping_style_t = iota + + yaml_BLOCK_MAPPING_STYLE // The block mapping style. + yaml_FLOW_MAPPING_STYLE // The flow mapping style. +) + +// Tokens + +type yaml_token_type_t int + +// Token types. +const ( + // An empty token. + yaml_NO_TOKEN yaml_token_type_t = iota + + yaml_STREAM_START_TOKEN // A STREAM-START token. + yaml_STREAM_END_TOKEN // A STREAM-END token. + + yaml_VERSION_DIRECTIVE_TOKEN // A VERSION-DIRECTIVE token. + yaml_TAG_DIRECTIVE_TOKEN // A TAG-DIRECTIVE token. + yaml_DOCUMENT_START_TOKEN // A DOCUMENT-START token. + yaml_DOCUMENT_END_TOKEN // A DOCUMENT-END token. + + yaml_BLOCK_SEQUENCE_START_TOKEN // A BLOCK-SEQUENCE-START token. + yaml_BLOCK_MAPPING_START_TOKEN // A BLOCK-SEQUENCE-END token. + yaml_BLOCK_END_TOKEN // A BLOCK-END token. + + yaml_FLOW_SEQUENCE_START_TOKEN // A FLOW-SEQUENCE-START token. + yaml_FLOW_SEQUENCE_END_TOKEN // A FLOW-SEQUENCE-END token. + yaml_FLOW_MAPPING_START_TOKEN // A FLOW-MAPPING-START token. + yaml_FLOW_MAPPING_END_TOKEN // A FLOW-MAPPING-END token. + + yaml_BLOCK_ENTRY_TOKEN // A BLOCK-ENTRY token. + yaml_FLOW_ENTRY_TOKEN // A FLOW-ENTRY token. + yaml_KEY_TOKEN // A KEY token. + yaml_VALUE_TOKEN // A VALUE token. + + yaml_ALIAS_TOKEN // An ALIAS token. + yaml_ANCHOR_TOKEN // An ANCHOR token. + yaml_TAG_TOKEN // A TAG token. + yaml_SCALAR_TOKEN // A SCALAR token. +) + +func (tt yaml_token_type_t) String() string { + switch tt { + case yaml_NO_TOKEN: + return "yaml_NO_TOKEN" + case yaml_STREAM_START_TOKEN: + return "yaml_STREAM_START_TOKEN" + case yaml_STREAM_END_TOKEN: + return "yaml_STREAM_END_TOKEN" + case yaml_VERSION_DIRECTIVE_TOKEN: + return "yaml_VERSION_DIRECTIVE_TOKEN" + case yaml_TAG_DIRECTIVE_TOKEN: + return "yaml_TAG_DIRECTIVE_TOKEN" + case yaml_DOCUMENT_START_TOKEN: + return "yaml_DOCUMENT_START_TOKEN" + case yaml_DOCUMENT_END_TOKEN: + return "yaml_DOCUMENT_END_TOKEN" + case yaml_BLOCK_SEQUENCE_START_TOKEN: + return "yaml_BLOCK_SEQUENCE_START_TOKEN" + case yaml_BLOCK_MAPPING_START_TOKEN: + return "yaml_BLOCK_MAPPING_START_TOKEN" + case yaml_BLOCK_END_TOKEN: + return "yaml_BLOCK_END_TOKEN" + case yaml_FLOW_SEQUENCE_START_TOKEN: + return "yaml_FLOW_SEQUENCE_START_TOKEN" + case yaml_FLOW_SEQUENCE_END_TOKEN: + return "yaml_FLOW_SEQUENCE_END_TOKEN" + case yaml_FLOW_MAPPING_START_TOKEN: + return "yaml_FLOW_MAPPING_START_TOKEN" + case yaml_FLOW_MAPPING_END_TOKEN: + return "yaml_FLOW_MAPPING_END_TOKEN" + case yaml_BLOCK_ENTRY_TOKEN: + return "yaml_BLOCK_ENTRY_TOKEN" + case yaml_FLOW_ENTRY_TOKEN: + return "yaml_FLOW_ENTRY_TOKEN" + case yaml_KEY_TOKEN: + return "yaml_KEY_TOKEN" + case yaml_VALUE_TOKEN: + return "yaml_VALUE_TOKEN" + case yaml_ALIAS_TOKEN: + return "yaml_ALIAS_TOKEN" + case yaml_ANCHOR_TOKEN: + return "yaml_ANCHOR_TOKEN" + case yaml_TAG_TOKEN: + return "yaml_TAG_TOKEN" + case yaml_SCALAR_TOKEN: + return "yaml_SCALAR_TOKEN" + } + return "" +} + +// The token structure. +type yaml_token_t struct { + // The token type. + typ yaml_token_type_t + + // The start/end of the token. + start_mark, end_mark yaml_mark_t + + // The stream encoding (for yaml_STREAM_START_TOKEN). + encoding yaml_encoding_t + + // The alias/anchor/scalar value or tag/tag directive handle + // (for yaml_ALIAS_TOKEN, yaml_ANCHOR_TOKEN, yaml_SCALAR_TOKEN, yaml_TAG_TOKEN, yaml_TAG_DIRECTIVE_TOKEN). + value []byte + + // The tag suffix (for yaml_TAG_TOKEN). + suffix []byte + + // The tag directive prefix (for yaml_TAG_DIRECTIVE_TOKEN). + prefix []byte + + // The scalar style (for yaml_SCALAR_TOKEN). + style yaml_scalar_style_t + + // The version directive major/minor (for yaml_VERSION_DIRECTIVE_TOKEN). + major, minor int8 +} + +// Events + +type yaml_event_type_t int8 + +// Event types. +const ( + // An empty event. + yaml_NO_EVENT yaml_event_type_t = iota + + yaml_STREAM_START_EVENT // A STREAM-START event. + yaml_STREAM_END_EVENT // A STREAM-END event. + yaml_DOCUMENT_START_EVENT // A DOCUMENT-START event. + yaml_DOCUMENT_END_EVENT // A DOCUMENT-END event. + yaml_ALIAS_EVENT // An ALIAS event. + yaml_SCALAR_EVENT // A SCALAR event. + yaml_SEQUENCE_START_EVENT // A SEQUENCE-START event. + yaml_SEQUENCE_END_EVENT // A SEQUENCE-END event. + yaml_MAPPING_START_EVENT // A MAPPING-START event. + yaml_MAPPING_END_EVENT // A MAPPING-END event. +) + +var eventStrings = []string{ + yaml_NO_EVENT: "none", + yaml_STREAM_START_EVENT: "stream start", + yaml_STREAM_END_EVENT: "stream end", + yaml_DOCUMENT_START_EVENT: "document start", + yaml_DOCUMENT_END_EVENT: "document end", + yaml_ALIAS_EVENT: "alias", + yaml_SCALAR_EVENT: "scalar", + yaml_SEQUENCE_START_EVENT: "sequence start", + yaml_SEQUENCE_END_EVENT: "sequence end", + yaml_MAPPING_START_EVENT: "mapping start", + yaml_MAPPING_END_EVENT: "mapping end", +} + +func (e yaml_event_type_t) String() string { + if e < 0 || int(e) >= len(eventStrings) { + return fmt.Sprintf("unknown event %d", e) + } + return eventStrings[e] +} + +// The event structure. +type yaml_event_t struct { + + // The event type. + typ yaml_event_type_t + + // The start and end of the event. + start_mark, end_mark yaml_mark_t + + // The document encoding (for yaml_STREAM_START_EVENT). + encoding yaml_encoding_t + + // The version directive (for yaml_DOCUMENT_START_EVENT). + version_directive *yaml_version_directive_t + + // The list of tag directives (for yaml_DOCUMENT_START_EVENT). + tag_directives []yaml_tag_directive_t + + // The anchor (for yaml_SCALAR_EVENT, yaml_SEQUENCE_START_EVENT, yaml_MAPPING_START_EVENT, yaml_ALIAS_EVENT). + anchor []byte + + // The tag (for yaml_SCALAR_EVENT, yaml_SEQUENCE_START_EVENT, yaml_MAPPING_START_EVENT). + tag []byte + + // The scalar value (for yaml_SCALAR_EVENT). + value []byte + + // Is the document start/end indicator implicit, or the tag optional? + // (for yaml_DOCUMENT_START_EVENT, yaml_DOCUMENT_END_EVENT, yaml_SEQUENCE_START_EVENT, yaml_MAPPING_START_EVENT, yaml_SCALAR_EVENT). + implicit bool + + // Is the tag optional for any non-plain style? (for yaml_SCALAR_EVENT). + quoted_implicit bool + + // The style (for yaml_SCALAR_EVENT, yaml_SEQUENCE_START_EVENT, yaml_MAPPING_START_EVENT). + style yaml_style_t +} + +func (e *yaml_event_t) scalar_style() yaml_scalar_style_t { return yaml_scalar_style_t(e.style) } +func (e *yaml_event_t) sequence_style() yaml_sequence_style_t { return yaml_sequence_style_t(e.style) } +func (e *yaml_event_t) mapping_style() yaml_mapping_style_t { return yaml_mapping_style_t(e.style) } + +// Nodes + +const ( + yaml_NULL_TAG = "tag:yaml.org,2002:null" // The tag !!null with the only possible value: null. + yaml_BOOL_TAG = "tag:yaml.org,2002:bool" // The tag !!bool with the values: true and false. + yaml_STR_TAG = "tag:yaml.org,2002:str" // The tag !!str for string values. + yaml_INT_TAG = "tag:yaml.org,2002:int" // The tag !!int for integer values. + yaml_FLOAT_TAG = "tag:yaml.org,2002:float" // The tag !!float for float values. + yaml_TIMESTAMP_TAG = "tag:yaml.org,2002:timestamp" // The tag !!timestamp for date and time values. + + yaml_SEQ_TAG = "tag:yaml.org,2002:seq" // The tag !!seq is used to denote sequences. + yaml_MAP_TAG = "tag:yaml.org,2002:map" // The tag !!map is used to denote mapping. + + // Not in original libyaml. + yaml_BINARY_TAG = "tag:yaml.org,2002:binary" + yaml_MERGE_TAG = "tag:yaml.org,2002:merge" + + yaml_DEFAULT_SCALAR_TAG = yaml_STR_TAG // The default scalar tag is !!str. + yaml_DEFAULT_SEQUENCE_TAG = yaml_SEQ_TAG // The default sequence tag is !!seq. + yaml_DEFAULT_MAPPING_TAG = yaml_MAP_TAG // The default mapping tag is !!map. +) + +type yaml_node_type_t int + +// Node types. +const ( + // An empty node. + yaml_NO_NODE yaml_node_type_t = iota + + yaml_SCALAR_NODE // A scalar node. + yaml_SEQUENCE_NODE // A sequence node. + yaml_MAPPING_NODE // A mapping node. +) + +// An element of a sequence node. +type yaml_node_item_t int + +// An element of a mapping node. +type yaml_node_pair_t struct { + key int // The key of the element. + value int // The value of the element. +} + +// The node structure. +type yaml_node_t struct { + typ yaml_node_type_t // The node type. + tag []byte // The node tag. + + // The node data. + + // The scalar parameters (for yaml_SCALAR_NODE). + scalar struct { + value []byte // The scalar value. + length int // The length of the scalar value. + style yaml_scalar_style_t // The scalar style. + } + + // The sequence parameters (for YAML_SEQUENCE_NODE). + sequence struct { + items_data []yaml_node_item_t // The stack of sequence items. + style yaml_sequence_style_t // The sequence style. + } + + // The mapping parameters (for yaml_MAPPING_NODE). + mapping struct { + pairs_data []yaml_node_pair_t // The stack of mapping pairs (key, value). + pairs_start *yaml_node_pair_t // The beginning of the stack. + pairs_end *yaml_node_pair_t // The end of the stack. + pairs_top *yaml_node_pair_t // The top of the stack. + style yaml_mapping_style_t // The mapping style. + } + + start_mark yaml_mark_t // The beginning of the node. + end_mark yaml_mark_t // The end of the node. + +} + +// The document structure. +type yaml_document_t struct { + + // The document nodes. + nodes []yaml_node_t + + // The version directive. + version_directive *yaml_version_directive_t + + // The list of tag directives. + tag_directives_data []yaml_tag_directive_t + tag_directives_start int // The beginning of the tag directives list. + tag_directives_end int // The end of the tag directives list. + + start_implicit int // Is the document start indicator implicit? + end_implicit int // Is the document end indicator implicit? + + // The start/end of the document. + start_mark, end_mark yaml_mark_t +} + +// The prototype of a read handler. +// +// The read handler is called when the parser needs to read more bytes from the +// source. The handler should write not more than size bytes to the buffer. +// The number of written bytes should be set to the size_read variable. +// +// [in,out] data A pointer to an application data specified by +// yaml_parser_set_input(). +// [out] buffer The buffer to write the data from the source. +// [in] size The size of the buffer. +// [out] size_read The actual number of bytes read from the source. +// +// On success, the handler should return 1. If the handler failed, +// the returned value should be 0. On EOF, the handler should set the +// size_read to 0 and return 1. +type yaml_read_handler_t func(parser *yaml_parser_t, buffer []byte) (n int, err error) + +// This structure holds information about a potential simple key. +type yaml_simple_key_t struct { + possible bool // Is a simple key possible? + required bool // Is a simple key required? + token_number int // The number of the token. + mark yaml_mark_t // The position mark. +} + +// The states of the parser. +type yaml_parser_state_t int + +const ( + yaml_PARSE_STREAM_START_STATE yaml_parser_state_t = iota + + yaml_PARSE_IMPLICIT_DOCUMENT_START_STATE // Expect the beginning of an implicit document. + yaml_PARSE_DOCUMENT_START_STATE // Expect DOCUMENT-START. + yaml_PARSE_DOCUMENT_CONTENT_STATE // Expect the content of a document. + yaml_PARSE_DOCUMENT_END_STATE // Expect DOCUMENT-END. + yaml_PARSE_BLOCK_NODE_STATE // Expect a block node. + yaml_PARSE_BLOCK_NODE_OR_INDENTLESS_SEQUENCE_STATE // Expect a block node or indentless sequence. + yaml_PARSE_FLOW_NODE_STATE // Expect a flow node. + yaml_PARSE_BLOCK_SEQUENCE_FIRST_ENTRY_STATE // Expect the first entry of a block sequence. + yaml_PARSE_BLOCK_SEQUENCE_ENTRY_STATE // Expect an entry of a block sequence. + yaml_PARSE_INDENTLESS_SEQUENCE_ENTRY_STATE // Expect an entry of an indentless sequence. + yaml_PARSE_BLOCK_MAPPING_FIRST_KEY_STATE // Expect the first key of a block mapping. + yaml_PARSE_BLOCK_MAPPING_KEY_STATE // Expect a block mapping key. + yaml_PARSE_BLOCK_MAPPING_VALUE_STATE // Expect a block mapping value. + yaml_PARSE_FLOW_SEQUENCE_FIRST_ENTRY_STATE // Expect the first entry of a flow sequence. + yaml_PARSE_FLOW_SEQUENCE_ENTRY_STATE // Expect an entry of a flow sequence. + yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_KEY_STATE // Expect a key of an ordered mapping. + yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_VALUE_STATE // Expect a value of an ordered mapping. + yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_END_STATE // Expect the and of an ordered mapping entry. + yaml_PARSE_FLOW_MAPPING_FIRST_KEY_STATE // Expect the first key of a flow mapping. + yaml_PARSE_FLOW_MAPPING_KEY_STATE // Expect a key of a flow mapping. + yaml_PARSE_FLOW_MAPPING_VALUE_STATE // Expect a value of a flow mapping. + yaml_PARSE_FLOW_MAPPING_EMPTY_VALUE_STATE // Expect an empty value of a flow mapping. + yaml_PARSE_END_STATE // Expect nothing. +) + +func (ps yaml_parser_state_t) String() string { + switch ps { + case yaml_PARSE_STREAM_START_STATE: + return "yaml_PARSE_STREAM_START_STATE" + case yaml_PARSE_IMPLICIT_DOCUMENT_START_STATE: + return "yaml_PARSE_IMPLICIT_DOCUMENT_START_STATE" + case yaml_PARSE_DOCUMENT_START_STATE: + return "yaml_PARSE_DOCUMENT_START_STATE" + case yaml_PARSE_DOCUMENT_CONTENT_STATE: + return "yaml_PARSE_DOCUMENT_CONTENT_STATE" + case yaml_PARSE_DOCUMENT_END_STATE: + return "yaml_PARSE_DOCUMENT_END_STATE" + case yaml_PARSE_BLOCK_NODE_STATE: + return "yaml_PARSE_BLOCK_NODE_STATE" + case yaml_PARSE_BLOCK_NODE_OR_INDENTLESS_SEQUENCE_STATE: + return "yaml_PARSE_BLOCK_NODE_OR_INDENTLESS_SEQUENCE_STATE" + case yaml_PARSE_FLOW_NODE_STATE: + return "yaml_PARSE_FLOW_NODE_STATE" + case yaml_PARSE_BLOCK_SEQUENCE_FIRST_ENTRY_STATE: + return "yaml_PARSE_BLOCK_SEQUENCE_FIRST_ENTRY_STATE" + case yaml_PARSE_BLOCK_SEQUENCE_ENTRY_STATE: + return "yaml_PARSE_BLOCK_SEQUENCE_ENTRY_STATE" + case yaml_PARSE_INDENTLESS_SEQUENCE_ENTRY_STATE: + return "yaml_PARSE_INDENTLESS_SEQUENCE_ENTRY_STATE" + case yaml_PARSE_BLOCK_MAPPING_FIRST_KEY_STATE: + return "yaml_PARSE_BLOCK_MAPPING_FIRST_KEY_STATE" + case yaml_PARSE_BLOCK_MAPPING_KEY_STATE: + return "yaml_PARSE_BLOCK_MAPPING_KEY_STATE" + case yaml_PARSE_BLOCK_MAPPING_VALUE_STATE: + return "yaml_PARSE_BLOCK_MAPPING_VALUE_STATE" + case yaml_PARSE_FLOW_SEQUENCE_FIRST_ENTRY_STATE: + return "yaml_PARSE_FLOW_SEQUENCE_FIRST_ENTRY_STATE" + case yaml_PARSE_FLOW_SEQUENCE_ENTRY_STATE: + return "yaml_PARSE_FLOW_SEQUENCE_ENTRY_STATE" + case yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_KEY_STATE: + return "yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_KEY_STATE" + case yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_VALUE_STATE: + return "yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_VALUE_STATE" + case yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_END_STATE: + return "yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_END_STATE" + case yaml_PARSE_FLOW_MAPPING_FIRST_KEY_STATE: + return "yaml_PARSE_FLOW_MAPPING_FIRST_KEY_STATE" + case yaml_PARSE_FLOW_MAPPING_KEY_STATE: + return "yaml_PARSE_FLOW_MAPPING_KEY_STATE" + case yaml_PARSE_FLOW_MAPPING_VALUE_STATE: + return "yaml_PARSE_FLOW_MAPPING_VALUE_STATE" + case yaml_PARSE_FLOW_MAPPING_EMPTY_VALUE_STATE: + return "yaml_PARSE_FLOW_MAPPING_EMPTY_VALUE_STATE" + case yaml_PARSE_END_STATE: + return "yaml_PARSE_END_STATE" + } + return "" +} + +// This structure holds aliases data. +type yaml_alias_data_t struct { + anchor []byte // The anchor. + index int // The node id. + mark yaml_mark_t // The anchor mark. +} + +// The parser structure. +// +// All members are internal. Manage the structure using the +// yaml_parser_ family of functions. +type yaml_parser_t struct { + + // Error handling + + error yaml_error_type_t // Error type. + + problem string // Error description. + + // The byte about which the problem occurred. + problem_offset int + problem_value int + problem_mark yaml_mark_t + + // The error context. + context string + context_mark yaml_mark_t + + // Reader stuff + + read_handler yaml_read_handler_t // Read handler. + + input_reader io.Reader // File input data. + input []byte // String input data. + input_pos int + + eof bool // EOF flag + + buffer []byte // The working buffer. + buffer_pos int // The current position of the buffer. + + unread int // The number of unread characters in the buffer. + + raw_buffer []byte // The raw buffer. + raw_buffer_pos int // The current position of the buffer. + + encoding yaml_encoding_t // The input encoding. + + offset int // The offset of the current position (in bytes). + mark yaml_mark_t // The mark of the current position. + + // Scanner stuff + + stream_start_produced bool // Have we started to scan the input stream? + stream_end_produced bool // Have we reached the end of the input stream? + + flow_level int // The number of unclosed '[' and '{' indicators. + + tokens []yaml_token_t // The tokens queue. + tokens_head int // The head of the tokens queue. + tokens_parsed int // The number of tokens fetched from the queue. + token_available bool // Does the tokens queue contain a token ready for dequeueing. + + indent int // The current indentation level. + indents []int // The indentation levels stack. + + simple_key_allowed bool // May a simple key occur at the current position? + simple_keys []yaml_simple_key_t // The stack of simple keys. + simple_keys_by_tok map[int]int // possible simple_key indexes indexed by token_number + + // Parser stuff + + state yaml_parser_state_t // The current parser state. + states []yaml_parser_state_t // The parser states stack. + marks []yaml_mark_t // The stack of marks. + tag_directives []yaml_tag_directive_t // The list of TAG directives. + + // Dumper stuff + + aliases []yaml_alias_data_t // The alias data. + + document *yaml_document_t // The currently parsed document. +} + +// Emitter Definitions + +// The prototype of a write handler. +// +// The write handler is called when the emitter needs to flush the accumulated +// characters to the output. The handler should write @a size bytes of the +// @a buffer to the output. +// +// @param[in,out] data A pointer to an application data specified by +// yaml_emitter_set_output(). +// @param[in] buffer The buffer with bytes to be written. +// @param[in] size The size of the buffer. +// +// @returns On success, the handler should return @c 1. If the handler failed, +// the returned value should be @c 0. +// +type yaml_write_handler_t func(emitter *yaml_emitter_t, buffer []byte) error + +type yaml_emitter_state_t int + +// The emitter states. +const ( + // Expect STREAM-START. + yaml_EMIT_STREAM_START_STATE yaml_emitter_state_t = iota + + yaml_EMIT_FIRST_DOCUMENT_START_STATE // Expect the first DOCUMENT-START or STREAM-END. + yaml_EMIT_DOCUMENT_START_STATE // Expect DOCUMENT-START or STREAM-END. + yaml_EMIT_DOCUMENT_CONTENT_STATE // Expect the content of a document. + yaml_EMIT_DOCUMENT_END_STATE // Expect DOCUMENT-END. + yaml_EMIT_FLOW_SEQUENCE_FIRST_ITEM_STATE // Expect the first item of a flow sequence. + yaml_EMIT_FLOW_SEQUENCE_ITEM_STATE // Expect an item of a flow sequence. + yaml_EMIT_FLOW_MAPPING_FIRST_KEY_STATE // Expect the first key of a flow mapping. + yaml_EMIT_FLOW_MAPPING_KEY_STATE // Expect a key of a flow mapping. + yaml_EMIT_FLOW_MAPPING_SIMPLE_VALUE_STATE // Expect a value for a simple key of a flow mapping. + yaml_EMIT_FLOW_MAPPING_VALUE_STATE // Expect a value of a flow mapping. + yaml_EMIT_BLOCK_SEQUENCE_FIRST_ITEM_STATE // Expect the first item of a block sequence. + yaml_EMIT_BLOCK_SEQUENCE_ITEM_STATE // Expect an item of a block sequence. + yaml_EMIT_BLOCK_MAPPING_FIRST_KEY_STATE // Expect the first key of a block mapping. + yaml_EMIT_BLOCK_MAPPING_KEY_STATE // Expect the key of a block mapping. + yaml_EMIT_BLOCK_MAPPING_SIMPLE_VALUE_STATE // Expect a value for a simple key of a block mapping. + yaml_EMIT_BLOCK_MAPPING_VALUE_STATE // Expect a value of a block mapping. + yaml_EMIT_END_STATE // Expect nothing. +) + +// The emitter structure. +// +// All members are internal. Manage the structure using the @c yaml_emitter_ +// family of functions. +type yaml_emitter_t struct { + + // Error handling + + error yaml_error_type_t // Error type. + problem string // Error description. + + // Writer stuff + + write_handler yaml_write_handler_t // Write handler. + + output_buffer *[]byte // String output data. + output_writer io.Writer // File output data. + + buffer []byte // The working buffer. + buffer_pos int // The current position of the buffer. + + raw_buffer []byte // The raw buffer. + raw_buffer_pos int // The current position of the buffer. + + encoding yaml_encoding_t // The stream encoding. + + // Emitter stuff + + canonical bool // If the output is in the canonical style? + best_indent int // The number of indentation spaces. + best_width int // The preferred width of the output lines. + unicode bool // Allow unescaped non-ASCII characters? + line_break yaml_break_t // The preferred line break. + + state yaml_emitter_state_t // The current emitter state. + states []yaml_emitter_state_t // The stack of states. + + events []yaml_event_t // The event queue. + events_head int // The head of the event queue. + + indents []int // The stack of indentation levels. + + tag_directives []yaml_tag_directive_t // The list of tag directives. + + indent int // The current indentation level. + + flow_level int // The current flow level. + + root_context bool // Is it the document root context? + sequence_context bool // Is it a sequence context? + mapping_context bool // Is it a mapping context? + simple_key_context bool // Is it a simple mapping key context? + + line int // The current line. + column int // The current column. + whitespace bool // If the last character was a whitespace? + indention bool // If the last character was an indentation character (' ', '-', '?', ':')? + open_ended bool // If an explicit document end is required? + + // Anchor analysis. + anchor_data struct { + anchor []byte // The anchor value. + alias bool // Is it an alias? + } + + // Tag analysis. + tag_data struct { + handle []byte // The tag handle. + suffix []byte // The tag suffix. + } + + // Scalar analysis. + scalar_data struct { + value []byte // The scalar value. + multiline bool // Does the scalar contain line breaks? + flow_plain_allowed bool // Can the scalar be expessed in the flow plain style? + block_plain_allowed bool // Can the scalar be expressed in the block plain style? + single_quoted_allowed bool // Can the scalar be expressed in the single quoted style? + block_allowed bool // Can the scalar be expressed in the literal or folded styles? + style yaml_scalar_style_t // The output style. + } + + // Dumper stuff + + opened bool // If the stream was already opened? + closed bool // If the stream was already closed? + + // The information associated with the document nodes. + anchors *struct { + references int // The number of references. + anchor int // The anchor id. + serialized bool // If the node has been emitted? + } + + last_anchor_id int // The last assigned anchor id. + + document *yaml_document_t // The currently emitted document. +} diff --git a/vendor/gopkg.in/yaml.v2/yamlprivateh.go b/vendor/gopkg.in/yaml.v2/yamlprivateh.go new file mode 100644 index 000000000..8110ce3c3 --- /dev/null +++ b/vendor/gopkg.in/yaml.v2/yamlprivateh.go @@ -0,0 +1,173 @@ +package yaml + +const ( + // The size of the input raw buffer. + input_raw_buffer_size = 512 + + // The size of the input buffer. + // It should be possible to decode the whole raw buffer. + input_buffer_size = input_raw_buffer_size * 3 + + // The size of the output buffer. + output_buffer_size = 128 + + // The size of the output raw buffer. + // It should be possible to encode the whole output buffer. + output_raw_buffer_size = (output_buffer_size*2 + 2) + + // The size of other stacks and queues. + initial_stack_size = 16 + initial_queue_size = 16 + initial_string_size = 16 +) + +// Check if the character at the specified position is an alphabetical +// character, a digit, '_', or '-'. +func is_alpha(b []byte, i int) bool { + return b[i] >= '0' && b[i] <= '9' || b[i] >= 'A' && b[i] <= 'Z' || b[i] >= 'a' && b[i] <= 'z' || b[i] == '_' || b[i] == '-' +} + +// Check if the character at the specified position is a digit. +func is_digit(b []byte, i int) bool { + return b[i] >= '0' && b[i] <= '9' +} + +// Get the value of a digit. +func as_digit(b []byte, i int) int { + return int(b[i]) - '0' +} + +// Check if the character at the specified position is a hex-digit. +func is_hex(b []byte, i int) bool { + return b[i] >= '0' && b[i] <= '9' || b[i] >= 'A' && b[i] <= 'F' || b[i] >= 'a' && b[i] <= 'f' +} + +// Get the value of a hex-digit. +func as_hex(b []byte, i int) int { + bi := b[i] + if bi >= 'A' && bi <= 'F' { + return int(bi) - 'A' + 10 + } + if bi >= 'a' && bi <= 'f' { + return int(bi) - 'a' + 10 + } + return int(bi) - '0' +} + +// Check if the character is ASCII. +func is_ascii(b []byte, i int) bool { + return b[i] <= 0x7F +} + +// Check if the character at the start of the buffer can be printed unescaped. +func is_printable(b []byte, i int) bool { + return ((b[i] == 0x0A) || // . == #x0A + (b[i] >= 0x20 && b[i] <= 0x7E) || // #x20 <= . <= #x7E + (b[i] == 0xC2 && b[i+1] >= 0xA0) || // #0xA0 <= . <= #xD7FF + (b[i] > 0xC2 && b[i] < 0xED) || + (b[i] == 0xED && b[i+1] < 0xA0) || + (b[i] == 0xEE) || + (b[i] == 0xEF && // #xE000 <= . <= #xFFFD + !(b[i+1] == 0xBB && b[i+2] == 0xBF) && // && . != #xFEFF + !(b[i+1] == 0xBF && (b[i+2] == 0xBE || b[i+2] == 0xBF)))) +} + +// Check if the character at the specified position is NUL. +func is_z(b []byte, i int) bool { + return b[i] == 0x00 +} + +// Check if the beginning of the buffer is a BOM. +func is_bom(b []byte, i int) bool { + return b[0] == 0xEF && b[1] == 0xBB && b[2] == 0xBF +} + +// Check if the character at the specified position is space. +func is_space(b []byte, i int) bool { + return b[i] == ' ' +} + +// Check if the character at the specified position is tab. +func is_tab(b []byte, i int) bool { + return b[i] == '\t' +} + +// Check if the character at the specified position is blank (space or tab). +func is_blank(b []byte, i int) bool { + //return is_space(b, i) || is_tab(b, i) + return b[i] == ' ' || b[i] == '\t' +} + +// Check if the character at the specified position is a line break. +func is_break(b []byte, i int) bool { + return (b[i] == '\r' || // CR (#xD) + b[i] == '\n' || // LF (#xA) + b[i] == 0xC2 && b[i+1] == 0x85 || // NEL (#x85) + b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA8 || // LS (#x2028) + b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA9) // PS (#x2029) +} + +func is_crlf(b []byte, i int) bool { + return b[i] == '\r' && b[i+1] == '\n' +} + +// Check if the character is a line break or NUL. +func is_breakz(b []byte, i int) bool { + //return is_break(b, i) || is_z(b, i) + return ( // is_break: + b[i] == '\r' || // CR (#xD) + b[i] == '\n' || // LF (#xA) + b[i] == 0xC2 && b[i+1] == 0x85 || // NEL (#x85) + b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA8 || // LS (#x2028) + b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA9 || // PS (#x2029) + // is_z: + b[i] == 0) +} + +// Check if the character is a line break, space, or NUL. +func is_spacez(b []byte, i int) bool { + //return is_space(b, i) || is_breakz(b, i) + return ( // is_space: + b[i] == ' ' || + // is_breakz: + b[i] == '\r' || // CR (#xD) + b[i] == '\n' || // LF (#xA) + b[i] == 0xC2 && b[i+1] == 0x85 || // NEL (#x85) + b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA8 || // LS (#x2028) + b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA9 || // PS (#x2029) + b[i] == 0) +} + +// Check if the character is a line break, space, tab, or NUL. +func is_blankz(b []byte, i int) bool { + //return is_blank(b, i) || is_breakz(b, i) + return ( // is_blank: + b[i] == ' ' || b[i] == '\t' || + // is_breakz: + b[i] == '\r' || // CR (#xD) + b[i] == '\n' || // LF (#xA) + b[i] == 0xC2 && b[i+1] == 0x85 || // NEL (#x85) + b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA8 || // LS (#x2028) + b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA9 || // PS (#x2029) + b[i] == 0) +} + +// Determine the width of the character. +func width(b byte) int { + // Don't replace these by a switch without first + // confirming that it is being inlined. + if b&0x80 == 0x00 { + return 1 + } + if b&0xE0 == 0xC0 { + return 2 + } + if b&0xF0 == 0xE0 { + return 3 + } + if b&0xF8 == 0xF0 { + return 4 + } + return 0 + +} diff --git a/vendor/modules.txt b/vendor/modules.txt index 8b40cd01c..9473fee7b 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -521,6 +521,21 @@ github.com/prometheus/common/model github.com/prometheus/procfs github.com/prometheus/procfs/internal/fs github.com/prometheus/procfs/internal/util +# github.com/scaleway/scaleway-sdk-go v1.0.0-beta.23 +## explicit; go 1.17 +github.com/scaleway/scaleway-sdk-go/api/block/v1alpha1 +github.com/scaleway/scaleway-sdk-go/api/instance/v1 +github.com/scaleway/scaleway-sdk-go/api/marketplace/v1 +github.com/scaleway/scaleway-sdk-go/internal/async +github.com/scaleway/scaleway-sdk-go/internal/auth +github.com/scaleway/scaleway-sdk-go/internal/errors +github.com/scaleway/scaleway-sdk-go/internal/generic +github.com/scaleway/scaleway-sdk-go/internal/marshaler +github.com/scaleway/scaleway-sdk-go/internal/parameter +github.com/scaleway/scaleway-sdk-go/logger +github.com/scaleway/scaleway-sdk-go/namegenerator +github.com/scaleway/scaleway-sdk-go/scw +github.com/scaleway/scaleway-sdk-go/validation # github.com/spf13/cobra v1.1.3 ## explicit; go 1.12 github.com/spf13/cobra @@ -935,6 +950,9 @@ google.golang.org/protobuf/types/known/durationpb google.golang.org/protobuf/types/known/emptypb google.golang.org/protobuf/types/known/fieldmaskpb google.golang.org/protobuf/types/known/timestamppb +# gopkg.in/yaml.v2 v2.4.0 +## explicit; go 1.15 +gopkg.in/yaml.v2 # gopkg.in/yaml.v3 v3.0.1 ## explicit gopkg.in/yaml.v3 From 19b37d1ead634a9b53bd8ac75622bfbaa86b4e57 Mon Sep 17 00:00:00 2001 From: Mathieu Tortuyaux Date: Mon, 19 Feb 2024 15:53:41 +0100 Subject: [PATCH 5/8] platform/api: add scaleway API Signed-off-by: Mathieu Tortuyaux --- platform/api/scaleway/api.go | 96 ++++++++++++ platform/api/scaleway/instance.go | 242 ++++++++++++++++++++++++++++++ 2 files changed, 338 insertions(+) create mode 100644 platform/api/scaleway/api.go create mode 100644 platform/api/scaleway/instance.go diff --git a/platform/api/scaleway/api.go b/platform/api/scaleway/api.go new file mode 100644 index 000000000..aac5a9447 --- /dev/null +++ b/platform/api/scaleway/api.go @@ -0,0 +1,96 @@ +// Copyright The Mantle Authors. +// SPDX-License-Identifier: Apache-2.0 + +package scaleway + +import ( + "fmt" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/credentials" + "github.com/aws/aws-sdk-go/aws/session" + "github.com/aws/aws-sdk-go/service/s3" + "github.com/scaleway/scaleway-sdk-go/api/instance/v1" + "github.com/scaleway/scaleway-sdk-go/scw" + + "github.com/flatcar/mantle/platform" + maws "github.com/flatcar/mantle/platform/api/aws" +) + +var ( + endpoint = "https://s3.%s.scw.cloud" +) + +// Options hold the specific Scaleway options. +type Options struct { + *platform.Options + // AccessKey is the Scaleway access key in the AWS format. + // Get the credentials at https://console.scaleway.com/iam/api-keys + AccessKey string + // Image is the ID of the Scaleway image to deploy. + Image string + // InstanceType is the type of the instance (e.g DEV1-S). + InstanceType string + // OrganizationID is the ID of the organization. + // Get the ID at https://console.scaleway.com/organization/settings + OrganizationID string + // ProjectID is used as "default" project for all requests. + ProjectID string + // Region is used as "default" region for all requests. + Region string + // SecretKey is the Scaleway secret key in the AWS format. + // Get the credentials at https://console.scaleway.com/iam/api-keys + SecretKey string + // Zone is used as "default" zone for all requests. + Zone string +} + +// API is a wrapper around Scaleway instance API and +// S3 AWS API (for object storage operation). +type API struct { + opts *Options + *maws.API + instance *instance.API +} + +// New returns a Scaleway API instance. +func New(opts *Options) (*API, error) { + region, err := scw.ParseRegion(opts.Region) + if err != nil { + return nil, fmt.Errorf("parsing Scaleway region: %w", err) + } + + zone, err := scw.ParseZone(opts.Zone) + if err != nil { + return nil, fmt.Errorf("parsing Scaleway zone: %w", err) + } + + client, err := scw.NewClient( + scw.WithDefaultOrganizationID(opts.OrganizationID), + scw.WithAuth(opts.AccessKey, opts.SecretKey), + scw.WithDefaultRegion(region), + scw.WithDefaultZone(zone), + ) + if err != nil { + return nil, fmt.Errorf("creating Scaleway client: %w", err) + } + + cfg := aws.Config{ + Region: aws.String(opts.Region), + Endpoint: aws.String(fmt.Sprintf(endpoint, opts.Region)), + Credentials: credentials.NewStaticCredentials(opts.AccessKey, opts.SecretKey, ""), + } + + sess, err := session.NewSessionWithOptions(session.Options{Config: cfg}) + if err != nil { + return nil, fmt.Errorf("creating s3 session: %w", err) + } + + api := &API{ + API: &maws.API{S3: s3.New(sess)}, + opts: opts, + instance: instance.NewAPI(client), + } + + return api, nil +} diff --git a/platform/api/scaleway/instance.go b/platform/api/scaleway/instance.go new file mode 100644 index 000000000..89e192369 --- /dev/null +++ b/platform/api/scaleway/instance.go @@ -0,0 +1,242 @@ +// Copyright The Mantle Authors. +// SPDX-License-Identifier: Apache-2.0 +package scaleway + +import ( + "context" + "errors" + "fmt" + "strings" + "time" + + "github.com/coreos/pkg/capnslog" + "github.com/flatcar/mantle/util" + "github.com/scaleway/scaleway-sdk-go/api/instance/v1" + "github.com/scaleway/scaleway-sdk-go/scw" +) + +const ( + userDataKey = "cloud-init" +) + +var ( + plog = capnslog.NewPackageLogger("github.com/flatcar/mantle", "platform/api/scaleway") + tags = []string{"mantle"} +) + +// Server is a wrapper around Scaleway instance Server struct. +type Server struct { + *instance.Server +} + +// CreateServer acts in four steps: +// 1. Create the server +// 2. Set the metadata on the server +// 3. Start the server +// 4. Get the server to update the IP fields +func (a *API) CreateServer(ctx context.Context, name, userData string) (*Server, error) { + volumeName := "flatcar_production_scaleway_image.qcow2" + res, err := a.instance.CreateServer(&instance.CreateServerRequest{ + Name: name, + Tags: tags, + Volumes: map[string]*instance.VolumeServerTemplate{ + "0": { + BaseSnapshot: &a.opts.Image, + VolumeType: instance.VolumeVolumeTypeLSSD, + Name: &volumeName, + }, + }, + CommercialType: a.opts.InstanceType, + }, scw.WithContext(ctx)) + if err != nil { + return nil, fmt.Errorf("creating server: %w", err) + } + + if res.Server == nil { + return nil, errors.New("unable to get server from API response") + } + + id := res.Server.ID + + plog.Infof("setting server userdata: %s", id) + if err := a.instance.SetServerUserData(&instance.SetServerUserDataRequest{ + ServerID: id, + Key: userDataKey, + Content: strings.NewReader(userData), + }, scw.WithContext(ctx)); err != nil { + return nil, fmt.Errorf("setting user-data configuration: %w", err) + } + + timeout := 2 * time.Minute + if err := a.instance.ServerActionAndWait(&instance.ServerActionAndWaitRequest{ + ServerID: id, + Action: instance.ServerActionPoweron, + Timeout: &timeout, + }, scw.WithContext(ctx)); err != nil { + return nil, fmt.Errorf("starting server: %w", err) + } + plog.Infof("server started: %s", id) + + // This is required to get an IP. + sres, err := a.instance.GetServer(&instance.GetServerRequest{ + ServerID: id, + }, scw.WithContext(ctx)) + if err != nil { + return nil, fmt.Errorf("refreshing server data: %w", err) + } + + if sres.Server == nil { + return nil, errors.New("unable to refresh server data from API response") + } + + return &Server{sres.Server}, nil +} + +// DeleteServer acts in three steps: +// 1. Power off the instance +// 2. Actually delete the server +// 3. Delete the associated volumes +func (a *API) DeleteServer(ctx context.Context, id string) error { + timeout := 2 * time.Minute + if err := a.instance.ServerActionAndWait(&instance.ServerActionAndWaitRequest{ + ServerID: id, + Action: instance.ServerActionPoweroff, + Timeout: &timeout, + }, scw.WithContext(ctx)); err != nil { + return fmt.Errorf("stopping server: %w", err) + } + + res, err := a.instance.GetServer(&instance.GetServerRequest{ + ServerID: id, + }, scw.WithContext(ctx)) + if err != nil { + return fmt.Errorf("refreshing server data: %w", err) + } + + if res.Server == nil { + return errors.New("unable to refresh server data from API response") + } + + if err := a.instance.DeleteServer(&instance.DeleteServerRequest{ + ServerID: id, + }, scw.WithContext(ctx)); err != nil { + return fmt.Errorf("deleting server: %w", err) + } + + for _, volume := range res.Server.Volumes { + if err := a.instance.DeleteVolume(&instance.DeleteVolumeRequest{ + VolumeID: volume.ID, + }, scw.WithContext(ctx)); err != nil { + return fmt.Errorf("deleting volume: %w", err) + } + } + + return nil +} + +func (a *API) DeleteSnapshot(ctx context.Context, id string) error { + if err := a.instance.DeleteSnapshot(&instance.DeleteSnapshotRequest{ + SnapshotID: id, + }, scw.WithContext(ctx)); err != nil { + return fmt.Errorf("deleting snapshot: %w", err) + } + + return nil +} + +// CreateSnapshot will create the snapshot used by the instance. +// It waits for the snapshot to be available before returning its ID. +func (a *API) CreateSnapshot(ctx context.Context, bucket, key string) (string, error) { + res, err := a.instance.CreateSnapshot(&instance.CreateSnapshotRequest{ + Name: key, + Bucket: &bucket, + Key: &key, + VolumeType: instance.SnapshotVolumeTypeLSSD, + Tags: &tags, + }, scw.WithContext(ctx)) + if err != nil { + return "", fmt.Errorf("creating snapshot: %v", err) + } + + if res.Snapshot == nil { + return "", errors.New("unable to get snapshot from API response") + } + + snapshot := res.Snapshot + plog.Infof("snaphost created: %s", snapshot.ID) + + if err := util.WaitUntilReady(5*time.Minute, 5*time.Second, func() (bool, error) { + res, err := a.instance.GetSnapshot(&instance.GetSnapshotRequest{ + SnapshotID: snapshot.ID, + }, scw.WithContext(ctx)) + if err != nil { + return false, fmt.Errorf("getting snapshot: %v", err) + } + + if res.Snapshot == nil { + return false, errors.New("unable to get snapshot from API response") + } + + return res.Snapshot.State == instance.SnapshotStateAvailable, nil + }); err != nil { + return "", fmt.Errorf("getting snapshot available: %w", err) + } + + plog.Infof("snaphost ready: %s", snapshot.ID) + return snapshot.ID, nil +} + +func (a *API) GC(ctx context.Context, gracePeriod time.Duration) error { + threshold := time.Now().Add(-gracePeriod) + + servers, err := a.instance.ListServers(&instance.ListServersRequest{ + Tags: tags, + }, scw.WithContext(ctx)) + if err != nil { + return fmt.Errorf("listing servers: %w", err) + } + + if servers.Servers == nil { + return errors.New("unable to list servers from API response") + } + + for _, server := range servers.Servers { + if server.CreationDate.After(threshold) { + continue + } + + plog.Infof("deleting server: %s", server.ID) + if err := a.DeleteServer(ctx, server.ID); err != nil { + return err + } + } + + // SDK / APIs are not consistent regarding the tags. + // CreateServer -> []string + // CreateSnapshot -> *[]string + // ListSnapshot -> *string + t := strings.Join(tags, " ") + snapshots, err := a.instance.ListSnapshots(&instance.ListSnapshotsRequest{ + Tags: &t, + }, scw.WithContext(ctx)) + if err != nil { + return fmt.Errorf("listing snapshots: %w", err) + } + + if servers.Servers == nil { + return errors.New("unable to list snapshots from API response") + } + + for _, snapshot := range snapshots.Snapshots { + if snapshot.CreationDate.After(threshold) { + continue + } + + plog.Infof("deleting snapshot: %s", snapshot.ID) + if err := a.DeleteSnapshot(ctx, snapshot.ID); err != nil { + return err + } + } + + return nil +} From ed7e052edc4f5a946bedfdf972d95114fcf46dcb Mon Sep 17 00:00:00 2001 From: Mathieu Tortuyaux Date: Mon, 19 Feb 2024 15:59:04 +0100 Subject: [PATCH 6/8] cmd/ore: add scaleway create-image Signed-off-by: Mathieu Tortuyaux --- cmd/ore/scaleway.go | 12 ++++++ cmd/ore/scaleway/create-image.go | 72 ++++++++++++++++++++++++++++++++ cmd/ore/scaleway/scaleway.go | 60 ++++++++++++++++++++++++++ 3 files changed, 144 insertions(+) create mode 100644 cmd/ore/scaleway.go create mode 100644 cmd/ore/scaleway/create-image.go create mode 100644 cmd/ore/scaleway/scaleway.go diff --git a/cmd/ore/scaleway.go b/cmd/ore/scaleway.go new file mode 100644 index 000000000..65eaa18a8 --- /dev/null +++ b/cmd/ore/scaleway.go @@ -0,0 +1,12 @@ +// Copyright The Mantle Authors. +// SPDX-License-Identifier: Apache-2.0 + +package main + +import ( + "github.com/flatcar/mantle/cmd/ore/scaleway" +) + +func init() { + root.AddCommand(scaleway.Scaleway) +} diff --git a/cmd/ore/scaleway/create-image.go b/cmd/ore/scaleway/create-image.go new file mode 100644 index 000000000..1039ca4fa --- /dev/null +++ b/cmd/ore/scaleway/create-image.go @@ -0,0 +1,72 @@ +// Copyright The Mantle Authors. +// SPDX-License-Identifier: Apache-2.0 + +package scaleway + +import ( + "context" + "fmt" + "os" + "path/filepath" + + "github.com/spf13/cobra" +) + +const bucket = "flatcar-testing" + +var ( + cmdCreate = &cobra.Command{ + Use: "create-image", + Short: "Create Scaleway image", + RunE: runCreate, + Example: `IMAGE_ID=$(ore scaleway \ + --scaleway-access-key "${SCALEWAY_ACCESS_KEY}" \ + --scaleway-secret-key "${SCALEWAY_SECRET_KEY}" \ + --scaleway-organization-id "${SCALEWAY_ORGANIZATION_ID}" \ + create-image --channel beta)`, + } + channel string + version string + board string + file string +) + +func init() { + Scaleway.AddCommand(cmdCreate) + + cmdCreate.Flags().StringVar(&channel, "channel", "stable", "Flatcar channel") + cmdCreate.Flags().StringVar(&version, "version", "current", "Flatcar version") + cmdCreate.Flags().StringVar(&board, "board", "amd64-usr", "board used for naming with default prefix and AMI architecture") + cmdCreate.Flags().StringVar(&file, "file", "flatcar_production_scaleway_image.qcow2", "path to local Flatcar image (.qcow2)") +} + +func runCreate(cmd *cobra.Command, args []string) error { + if err := API.InitializeBucket(bucket); err != nil { + return fmt.Errorf("creating bucket %s: %v", bucket, err) + } + + f, err := os.Open(file) + if err != nil { + return fmt.Errorf("opening Flatcar image file %s: %v", file, err) + } + + defer f.Close() + + key := fmt.Sprintf("%s/%s/%s/%s", channel, version, board, filepath.Base(file)) + if err := API.UploadObject(f, bucket, key, true); err != nil { + return fmt.Errorf("uploading Flatcar image file %s: %v", file, err) + } + + ID, err := API.CreateSnapshot(context.Background(), bucket, key) + if err != nil { + return fmt.Errorf("creating Flatcar image: %v", err) + } + + if err := API.DeleteObject(bucket, key); err != nil { + return fmt.Errorf("deleting Flatcar image from s3 bucket: %s", fmt.Sprintf("s3://%s/%s", bucket, key)) + } + + fmt.Println(ID) + + return nil +} diff --git a/cmd/ore/scaleway/scaleway.go b/cmd/ore/scaleway/scaleway.go new file mode 100644 index 000000000..f650a86b7 --- /dev/null +++ b/cmd/ore/scaleway/scaleway.go @@ -0,0 +1,60 @@ +// Copyright The Mantle Authors. +// SPDX-License-Identifier: Apache-2.0 +package scaleway + +import ( + "fmt" + "os" + + "github.com/coreos/pkg/capnslog" + "github.com/flatcar/mantle/cli" + "github.com/flatcar/mantle/platform" + "github.com/flatcar/mantle/platform/api/scaleway" + "github.com/spf13/cobra" +) + +var ( + plog = capnslog.NewPackageLogger("github.com/flatcar/mantle", "ore/scaleway") + + Scaleway = &cobra.Command{ + Use: "scaleway [command]", + Short: "scaleway image utilities", + } + + API *scaleway.API + region string + zone string + accessKey string + secretKey string + organizationID string + projectID string +) + +func init() { + cli.WrapPreRun(Scaleway, preflightCheck) + Scaleway.PersistentFlags().StringVar(®ion, "scaleway-region", "fr-par", "Scaleway region") + Scaleway.PersistentFlags().StringVar(&zone, "scaleway-zone", "fr-par-1", "Scaleway region") + Scaleway.PersistentFlags().StringVar(&accessKey, "scaleway-access-key", "", "Scaleway access key") + Scaleway.PersistentFlags().StringVar(&secretKey, "scaleway-secret-key", "", "Scaleway secret key") + Scaleway.PersistentFlags().StringVar(&organizationID, "scaleway-organization-id", "", "Scaleway organization ID") + Scaleway.PersistentFlags().StringVar(&projectID, "scaleway-project-id", "", "Scaleway project ID") +} + +func preflightCheck(cmd *cobra.Command, args []string) error { + api, err := scaleway.New(&scaleway.Options{ + Region: region, + Zone: zone, + AccessKey: accessKey, + SecretKey: secretKey, + OrganizationID: organizationID, + ProjectID: projectID, + Options: &platform.Options{}, + }) + if err != nil { + fmt.Fprintf(os.Stderr, "could not create Scaleway API client: %v\n", err) + os.Exit(1) + } + + API = api + return nil +} From b21d1c0e94fab451e3911c78725457e7308eb96b Mon Sep 17 00:00:00 2001 From: Mathieu Tortuyaux Date: Mon, 19 Feb 2024 16:04:34 +0100 Subject: [PATCH 7/8] platform: implement the kola elements for Scaleway Signed-off-by: Mathieu Tortuyaux --- cmd/kola/options.go | 13 +++- kola/harness.go | 5 ++ platform/machine/scaleway/cluster.go | 91 +++++++++++++++++++++++++ platform/machine/scaleway/flight.go | 69 +++++++++++++++++++ platform/machine/scaleway/machine.go | 99 ++++++++++++++++++++++++++++ 5 files changed, 276 insertions(+), 1 deletion(-) create mode 100644 platform/machine/scaleway/cluster.go create mode 100644 platform/machine/scaleway/flight.go create mode 100644 platform/machine/scaleway/machine.go diff --git a/cmd/kola/options.go b/cmd/kola/options.go index c904e3a3a..e6c8bbcf7 100644 --- a/cmd/kola/options.go +++ b/cmd/kola/options.go @@ -40,7 +40,7 @@ var ( kolaOffering string defaultTargetBoard = sdk.DefaultBoard() kolaArchitectures = []string{"amd64"} - kolaPlatforms = []string{"aws", "azure", "brightbox", "do", "esx", "external", "gce", "openstack", "equinixmetal", "qemu", "qemu-unpriv"} + kolaPlatforms = []string{"aws", "azure", "brightbox", "do", "esx", "external", "gce", "openstack", "equinixmetal", "qemu", "qemu-unpriv", "scaleway"} kolaDistros = []string{"cl", "fcos", "rhcos"} kolaChannels = []string{"alpha", "beta", "stable", "edge", "lts"} kolaOfferings = []string{"basic", "pro"} @@ -233,6 +233,16 @@ func init() { sv(&kola.BrightboxOptions.ClientSecret, "brightbox-client-secret", "", "Brightbox client secret") sv(&kola.BrightboxOptions.Image, "brightbox-image", "", "Brightbox image ref") sv(&kola.BrightboxOptions.ServerType, "brightbox-server-type", "2gb.ssd", "Brightbox server type") + + // Scaleway specific options + sv(&kola.ScalewayOptions.OrganizationID, "scaleway-organization-id", "", "Scaleway organization ID") + sv(&kola.ScalewayOptions.ProjectID, "scaleway-project-id", "", "Scaleway organization ID") + sv(&kola.ScalewayOptions.Region, "scaleway-region", "fr-par", "Scaleway region") + sv(&kola.ScalewayOptions.Zone, "scaleway-zone", "fr-par-1", "Scaleway region") + sv(&kola.ScalewayOptions.AccessKey, "scaleway-access-key", "", "Scaleway credentials access key") + sv(&kola.ScalewayOptions.SecretKey, "scaleway-secret-key", "", "Scaleway credentials secret key") + sv(&kola.ScalewayOptions.Image, "scaleway-image", "", "Scaleway image ID") + sv(&kola.ScalewayOptions.InstanceType, "scaleway-instance-type", "DEV1-S", "Scaleway instance type") } // Sync up the command line options if there is dependency @@ -252,6 +262,7 @@ func syncOptions() error { kola.EquinixMetalOptions.Board = board kola.EquinixMetalOptions.GSOptions = &kola.GCEOptions kola.BrightboxOptions.Board = board + kola.ScalewayOptions.Board = board validateOption := func(name, item string, valid []string) error { for _, v := range valid { diff --git a/kola/harness.go b/kola/harness.go index 015de71dc..cb59f5f5d 100644 --- a/kola/harness.go +++ b/kola/harness.go @@ -46,6 +46,7 @@ import ( esxapi "github.com/flatcar/mantle/platform/api/esx" gcloudapi "github.com/flatcar/mantle/platform/api/gcloud" openstackapi "github.com/flatcar/mantle/platform/api/openstack" + scalewayapi "github.com/flatcar/mantle/platform/api/scaleway" "github.com/flatcar/mantle/platform/conf" "github.com/flatcar/mantle/platform/machine/aws" "github.com/flatcar/mantle/platform/machine/azure" @@ -57,6 +58,7 @@ import ( "github.com/flatcar/mantle/platform/machine/gcloud" "github.com/flatcar/mantle/platform/machine/openstack" "github.com/flatcar/mantle/platform/machine/qemu" + "github.com/flatcar/mantle/platform/machine/scaleway" "github.com/flatcar/mantle/platform/machine/unprivqemu" "github.com/flatcar/mantle/system" ) @@ -75,6 +77,7 @@ var ( OpenStackOptions = openstackapi.Options{Options: &Options} // glue to set platform options from main EquinixMetalOptions = equinixmetalapi.Options{Options: &Options} // glue to set platform options from main QEMUOptions = qemu.Options{Options: &Options} // glue to set platform options from main + ScalewayOptions = scalewayapi.Options{Options: &Options} // glue to set platform options from main TestParallelism int //glue var to set test parallelism from main TAPFile string // if not "", write TAP results here @@ -246,6 +249,8 @@ func NewFlight(pltfrm string) (flight platform.Flight, err error) { flight, err = qemu.NewFlight(&QEMUOptions) case "qemu-unpriv": flight, err = unprivqemu.NewFlight(&QEMUOptions) + case "scaleway": + flight, err = scaleway.NewFlight(&ScalewayOptions) default: err = fmt.Errorf("invalid platform %q", pltfrm) } diff --git a/platform/machine/scaleway/cluster.go b/platform/machine/scaleway/cluster.go new file mode 100644 index 000000000..0587f6389 --- /dev/null +++ b/platform/machine/scaleway/cluster.go @@ -0,0 +1,91 @@ +// Copyright The Mantle Authors. +// SPDX-License-Identifier: Apache-2.0 + +package scaleway + +import ( + "context" + "crypto/rand" + "fmt" + "os" + "path/filepath" + + "github.com/flatcar/mantle/platform" + "github.com/flatcar/mantle/platform/conf" +) + +type cluster struct { + *platform.BaseCluster + flight *flight +} + +func (bc *cluster) NewMachine(userdata *conf.UserData) (platform.Machine, error) { + conf, err := bc.RenderUserData(userdata, map[string]string{ + "$public_ipv4": "${COREOS_CUSTOM_PUBLIC_IPV4}", + "$private_ipv4": "${COREOS_CUSTOM_PRIVATE_IPV4}", + }) + if err != nil { + return nil, err + } + + // Hack to workaround CT inheritance. + // Can be dropped once we remove CT dependency. + // https://github.com/flatcar/Flatcar/issues/1386 + conf.AddSystemdUnitDropin("coreos-metadata.service", "00-custom-metadata.conf", `[Service] +ExecStartPost=/usr/bin/sed -i "s/SCALEWAY/CUSTOM/" /run/metadata/flatcar +ExecStartPost=/usr/bin/sed -i "s/IPV4_PRIVATE/PRIVATE_IPV4/" /run/metadata/flatcar +ExecStartPost=/usr/bin/sed -i "s/IPV4_PUBLIC/PUBLIC_IPV4/" /run/metadata/flatcar +`) + + instance, err := bc.flight.api.CreateServer(context.TODO(), bc.vmname(), conf.String()) + if err != nil { + return nil, err + } + + mach := &machine{ + cluster: bc, + mach: instance, + } + + // machine to destroy + m := mach + defer func() { + if m != nil { + m.Destroy() + } + }() + + mach.dir = filepath.Join(bc.RuntimeConf().OutputDir, mach.ID()) + if err := os.Mkdir(mach.dir, 0777); err != nil { + return nil, err + } + + confPath := filepath.Join(mach.dir, "ignition.json") + if err := conf.WriteFile(confPath); err != nil { + return nil, err + } + + if mach.journal, err = platform.NewJournal(mach.dir); err != nil { + return nil, err + } + + if err := platform.StartMachine(mach, mach.journal); err != nil { + return nil, err + } + + m = nil + bc.AddMach(mach) + + return mach, nil +} + +func (bc *cluster) vmname() string { + b := make([]byte, 5) + rand.Read(b) + return fmt.Sprintf("%s-%x", bc.Name()[0:13], b) +} + +func (bc *cluster) Destroy() { + bc.BaseCluster.Destroy() + bc.flight.DelCluster(bc) +} diff --git a/platform/machine/scaleway/flight.go b/platform/machine/scaleway/flight.go new file mode 100644 index 000000000..5f26ef218 --- /dev/null +++ b/platform/machine/scaleway/flight.go @@ -0,0 +1,69 @@ +// Copyright The Mantle Authors. +// SPDX-License-Identifier: Apache-2.0 + +package scaleway + +import ( + "fmt" + + "github.com/coreos/pkg/capnslog" + ctplatform "github.com/flatcar/container-linux-config-transpiler/config/platform" + + "github.com/flatcar/mantle/platform" + "github.com/flatcar/mantle/platform/api/scaleway" +) + +const ( + Platform platform.Name = "scaleway" +) + +var ( + plog = capnslog.NewPackageLogger("github.com/flatcar/mantle", "platform/machine/scaleway") +) + +type flight struct { + *platform.BaseFlight + api *scaleway.API +} + +func NewFlight(opts *scaleway.Options) (platform.Flight, error) { + api, err := scaleway.New(opts) + if err != nil { + return nil, fmt.Errorf("creating scaleway API client: %w", err) + } + + // TODO: Rework the Base Flight to remove the CT dependency. + base, err := platform.NewBaseFlight(opts.Options, Platform, ctplatform.Custom) + if err != nil { + return nil, fmt.Errorf("creating base flight: %w", err) + } + + bf := &flight{ + BaseFlight: base, + api: api, + } + + return bf, nil +} + +// NewCluster creates an instance of a Cluster suitable for spawning +// instances on the Scaleway platform. +func (bf *flight) NewCluster(rconf *platform.RuntimeConfig) (platform.Cluster, error) { + bc, err := platform.NewBaseCluster(bf.BaseFlight, rconf) + if err != nil { + return nil, fmt.Errorf("creating scaleway base cluster: %w", err) + } + + c := &cluster{ + BaseCluster: bc, + flight: bf, + } + + bf.AddCluster(c) + + return c, nil +} + +func (bf *flight) Destroy() { + bf.BaseFlight.Destroy() +} diff --git a/platform/machine/scaleway/machine.go b/platform/machine/scaleway/machine.go new file mode 100644 index 000000000..b8fd500f9 --- /dev/null +++ b/platform/machine/scaleway/machine.go @@ -0,0 +1,99 @@ +// Copyright The Mantle Authors. +// SPDX-License-Identifier: Apache-2.0 + +package scaleway + +import ( + "context" + + "golang.org/x/crypto/ssh" + + "github.com/flatcar/mantle/platform" + "github.com/flatcar/mantle/platform/api/scaleway" +) + +type machine struct { + cluster *cluster + mach *scaleway.Server + dir string + journal *platform.Journal + console string +} + +// ID returns the ID of the machine. +func (bm *machine) ID() string { + return bm.mach.Server.ID +} + +// IP returns the IP of the machine. +func (bm *machine) IP() string { + if bm.mach.Server.PublicIP != nil { + return bm.mach.Server.PublicIP.Address.String() + } + + return "" +} + +// IP returns the private IP of the machine. +func (bm *machine) PrivateIP() string { + if bm.mach.Server.PrivateIP != nil { + return *bm.mach.Server.PrivateIP + } + + return "" +} + +// RuntimeConf returns the runtime configuration of the cluster. +func (bm *machine) RuntimeConf() *platform.RuntimeConfig { + return bm.cluster.RuntimeConf() +} + +func (bm *machine) SSHClient() (*ssh.Client, error) { + return bm.cluster.SSHClient(bm.IP()) +} + +func (bm *machine) PasswordSSHClient(user string, password string) (*ssh.Client, error) { + return bm.cluster.PasswordSSHClient(bm.IP(), user, password) +} + +func (bm *machine) SSH(cmd string) ([]byte, []byte, error) { + return bm.cluster.SSH(bm, cmd) +} + +func (bm *machine) Reboot() error { + return platform.RebootMachine(bm, bm.journal) +} + +func (bm *machine) Destroy() { + // TODO: Add "saveConsole" logic here when Scaleway API will support fetching the console output. + + if err := bm.cluster.flight.api.DeleteServer(context.TODO(), bm.ID()); err != nil { + plog.Errorf("deleting server %v: %v", bm.ID(), err) + } + + if bm.journal != nil { + bm.journal.Destroy() + } + + bm.cluster.DelMach(bm) +} + +func (bm *machine) ConsoleOutput() string { + return bm.console +} + +func (bm *machine) JournalOutput() string { + if bm.journal == nil { + return "" + } + + data, err := bm.journal.Read() + if err != nil { + plog.Errorf("Reading journal for instance %v: %v", bm.ID(), err) + } + return string(data) +} + +func (bm *machine) Board() string { + return bm.cluster.flight.Options().Board +} From 490eacb5e3e16061fcd946470b80095ec24ecc99 Mon Sep 17 00:00:00 2001 From: Mathieu Tortuyaux Date: Tue, 9 Apr 2024 17:36:44 +0200 Subject: [PATCH 8/8] cmd/ore: add scaleway garbage collection Signed-off-by: Mathieu Tortuyaux --- cmd/ore/scaleway/gc.go | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 cmd/ore/scaleway/gc.go diff --git a/cmd/ore/scaleway/gc.go b/cmd/ore/scaleway/gc.go new file mode 100644 index 000000000..c407954db --- /dev/null +++ b/cmd/ore/scaleway/gc.go @@ -0,0 +1,36 @@ +// Copyright The Mantle Authors. +// SPDX-License-Identifier: Apache-2.0 + +package scaleway + +import ( + "context" + "fmt" + "time" + + "github.com/spf13/cobra" +) + +var ( + cmdGC = &cobra.Command{ + Use: "gc", + Short: "GC resources in Scaleway", + Long: `Delete instances and images created over the given duration ago`, + RunE: runGC, + } + + gcDuration time.Duration +) + +func init() { + Scaleway.AddCommand(cmdGC) + cmdGC.Flags().DurationVar(&gcDuration, "duration", 5*time.Hour, "how old resources must be before they're considered garbage") +} + +func runGC(cmd *cobra.Command, args []string) error { + if err := API.GC(context.Background(), gcDuration); err != nil { + return fmt.Errorf("running garbage collection: %w", err) + } + + return nil +}