From f8e31bff1218dc888b06039a99033a1f758c31be Mon Sep 17 00:00:00 2001 From: Patrik Date: Wed, 9 Sep 2020 10:27:48 +0200 Subject: [PATCH] feat(builder): bump scaleway to new sdk (#9902) --- builder/scaleway/artifact.go | 27 +- builder/scaleway/artifact_test.go | 2 +- builder/scaleway/builder.go | 23 +- builder/scaleway/builder_test.go | 24 +- builder/scaleway/config.go | 115 +- builder/scaleway/config.hcl2spec.go | 22 +- builder/scaleway/step_create_image.go | 27 +- builder/scaleway/step_create_server.go | 51 +- builder/scaleway/step_remove_volume.go | 10 +- builder/scaleway/step_server_info.go | 25 +- builder/scaleway/step_shutdown.go | 23 +- builder/scaleway/step_snapshot.go | 17 +- go.mod | 9 +- go.sum | 18 +- vendor/github.com/creack/goselect/.gitignore | 29 - vendor/github.com/creack/goselect/Dockerfile | 5 - vendor/github.com/creack/goselect/LICENSE | 22 - vendor/github.com/creack/goselect/README.md | 133 - vendor/github.com/creack/goselect/fdset.go | 33 - vendor/github.com/creack/goselect/fdset_32.go | 10 - vendor/github.com/creack/goselect/fdset_64.go | 11 - .../github.com/creack/goselect/fdset_doc.go | 93 - .../creack/goselect/fdset_freebsd.go | 33 - .../creack/goselect/fdset_unsupported.go | 20 - .../creack/goselect/fdset_windows.go | 57 - vendor/github.com/creack/goselect/select.go | 28 - .../creack/goselect/select_linux.go | 10 - .../creack/goselect/select_other.go | 9 - .../creack/goselect/select_unsupported.go | 16 - .../creack/goselect/select_windows.go | 13 - .../creack/goselect/test_crosscompile.sh | 112 - .../creack/goselect/zselect_windows.go | 39 - vendor/github.com/docker/docker/AUTHORS | 1952 ------- vendor/github.com/docker/docker/NOTICE | 19 - .../github.com/dustin/go-humanize/.travis.yml | 21 - vendor/github.com/dustin/go-humanize/LICENSE | 21 - .../dustin/go-humanize/README.markdown | 124 - vendor/github.com/dustin/go-humanize/big.go | 31 - .../github.com/dustin/go-humanize/bigbytes.go | 173 - vendor/github.com/dustin/go-humanize/bytes.go | 143 - vendor/github.com/dustin/go-humanize/comma.go | 116 - .../github.com/dustin/go-humanize/commaf.go | 40 - vendor/github.com/dustin/go-humanize/ftoa.go | 46 - .../github.com/dustin/go-humanize/humanize.go | 8 - .../github.com/dustin/go-humanize/number.go | 192 - .../github.com/dustin/go-humanize/ordinals.go | 25 - vendor/github.com/dustin/go-humanize/si.go | 123 - vendor/github.com/dustin/go-humanize/times.go | 117 - .../github.com/gorilla/websocket/.gitignore | 25 - .../github.com/gorilla/websocket/.travis.yml | 19 - vendor/github.com/gorilla/websocket/AUTHORS | 8 - vendor/github.com/gorilla/websocket/LICENSE | 22 - vendor/github.com/gorilla/websocket/README.md | 64 - vendor/github.com/gorilla/websocket/client.go | 392 -- .../gorilla/websocket/client_clone.go | 16 - .../gorilla/websocket/client_clone_legacy.go | 38 - .../gorilla/websocket/compression.go | 148 - vendor/github.com/gorilla/websocket/conn.go | 1149 ---- .../github.com/gorilla/websocket/conn_read.go | 18 - .../gorilla/websocket/conn_read_legacy.go | 21 - vendor/github.com/gorilla/websocket/doc.go | 180 - vendor/github.com/gorilla/websocket/json.go | 55 - vendor/github.com/gorilla/websocket/mask.go | 55 - .../github.com/gorilla/websocket/mask_safe.go | 15 - .../github.com/gorilla/websocket/prepared.go | 103 - vendor/github.com/gorilla/websocket/server.go | 291 - vendor/github.com/gorilla/websocket/util.go | 214 - vendor/github.com/moul/anonuuid/.gitignore | 28 - vendor/github.com/moul/anonuuid/.godir | 1 - vendor/github.com/moul/anonuuid/.goxc.json | 12 - vendor/github.com/moul/anonuuid/.travis.yml | 30 - vendor/github.com/moul/anonuuid/LICENSE | 22 - vendor/github.com/moul/anonuuid/Makefile | 73 - vendor/github.com/moul/anonuuid/README.md | 170 - vendor/github.com/moul/anonuuid/anonuuid.go | 229 - .../github.com/moul/gotty-client/.gitignore | 27 - .../github.com/moul/gotty-client/.goxc.json | 13 - .../github.com/moul/gotty-client/.travis.yml | 14 - .../github.com/moul/gotty-client/Dockerfile | 11 - vendor/github.com/moul/gotty-client/LICENSE | 22 - .../moul/gotty-client/LICENSE.apache | 192 - vendor/github.com/moul/gotty-client/Makefile | 73 - vendor/github.com/moul/gotty-client/README.md | 205 - vendor/github.com/moul/gotty-client/arch.go | 32 - .../moul/gotty-client/arch_windows.go | 16 - .../moul/gotty-client/gotty-client.go | 473 -- vendor/github.com/moul/gotty-client/proxy.go | 90 - .../github.com/renstrom/fuzzysearch/LICENSE | 21 - .../renstrom/fuzzysearch/fuzzy/fuzzy.go | 167 - .../renstrom/fuzzysearch/fuzzy/levenshtein.go | 43 - .../scaleway/scaleway-cli/LICENSE.md | 22 - .../scaleway/scaleway-cli/pkg/api/README.md | 3 - .../scaleway/scaleway-cli/pkg/api/api.go | 2844 ---------- .../scaleway/scaleway-cli/pkg/api/cache.go | 816 --- .../scaleway/scaleway-cli/pkg/api/helpers.go | 725 --- .../scaleway/scaleway-cli/pkg/api/logger.go | 77 - .../scaleway-cli/pkg/sshcommand/sshcommand.go | 122 - .../scaleway/scaleway-cli/pkg/utils/quiet.go | 30 - .../scaleway/scaleway-cli/pkg/utils/utils.go | 253 - .../scaleway-sdk-go}/LICENSE | 2 +- .../api/instance/v1/image_utils.go | 56 + .../api/instance/v1/instance_metadata_sdk.go | 273 + .../api/instance/v1/instance_sdk.go | 4707 +++++++++++++++++ .../api/instance/v1/instance_utils.go | 329 ++ .../api/instance/v1/security_group_utils.go | 214 + .../api/instance/v1/server_utils.go | 409 ++ .../api/instance/v1/snapshot_utils.go | 56 + .../api/instance/v1/volume_utils.go | 56 + .../api/marketplace/v1/marketplace_sdk.go | 304 ++ .../api/marketplace/v1/marketplace_utils.go | 98 + .../scaleway-sdk-go/internal/async/wait.go | 90 + .../scaleway-sdk-go/internal/auth/auth.go | 14 + .../scaleway-sdk-go/internal/auth/no_auth.go | 19 + .../scaleway-sdk-go/internal/auth/token.go | 45 + .../scaleway-sdk-go/internal/errors/error.go | 41 + .../internal/marshaler/duration.go | 160 + .../internal/parameter/query.go | 33 + .../scaleway-sdk-go/logger/default_logger.go | 111 + .../scaleway/scaleway-sdk-go/logger/logger.go | 50 + .../namegenerator/name_generator.go} | 264 +- .../scaleway/scaleway-sdk-go/scw/README.md | 55 + .../scaleway/scaleway-sdk-go/scw/client.go | 376 ++ .../scaleway-sdk-go/scw/client_option.go | 265 + .../scaleway/scaleway-sdk-go/scw/config.go | 343 ++ .../scaleway-sdk-go/scw/config_legacy.go | 92 + .../scaleway/scaleway-sdk-go/scw/convert.go | 177 + .../scaleway-sdk-go/scw/custom_types.go | 339 ++ .../scaleway/scaleway-sdk-go/scw/env.go | 143 + .../scaleway/scaleway-sdk-go/scw/errors.go | 484 ++ .../scaleway/scaleway-sdk-go/scw/locality.go | 198 + .../scaleway/scaleway-sdk-go/scw/path.go | 98 + .../scaleway/scaleway-sdk-go/scw/request.go | 104 + .../scaleway-sdk-go/scw/request_option.go | 32 + .../scaleway/scaleway-sdk-go/scw/version.go | 11 + .../scaleway/scaleway-sdk-go/validation/is.go | 61 + vendor/modules.txt | 30 +- .../builder/scaleway/Config-not-required.mdx | 22 + .../builder/scaleway/Config-required.mdx | 22 +- 138 files changed, 10385 insertions(+), 13704 deletions(-) delete mode 100644 vendor/github.com/creack/goselect/.gitignore delete mode 100644 vendor/github.com/creack/goselect/Dockerfile delete mode 100644 vendor/github.com/creack/goselect/LICENSE delete mode 100644 vendor/github.com/creack/goselect/README.md delete mode 100644 vendor/github.com/creack/goselect/fdset.go delete mode 100644 vendor/github.com/creack/goselect/fdset_32.go delete mode 100644 vendor/github.com/creack/goselect/fdset_64.go delete mode 100644 vendor/github.com/creack/goselect/fdset_doc.go delete mode 100644 vendor/github.com/creack/goselect/fdset_freebsd.go delete mode 100644 vendor/github.com/creack/goselect/fdset_unsupported.go delete mode 100644 vendor/github.com/creack/goselect/fdset_windows.go delete mode 100644 vendor/github.com/creack/goselect/select.go delete mode 100644 vendor/github.com/creack/goselect/select_linux.go delete mode 100644 vendor/github.com/creack/goselect/select_other.go delete mode 100644 vendor/github.com/creack/goselect/select_unsupported.go delete mode 100644 vendor/github.com/creack/goselect/select_windows.go delete mode 100644 vendor/github.com/creack/goselect/test_crosscompile.sh delete mode 100644 vendor/github.com/creack/goselect/zselect_windows.go delete mode 100644 vendor/github.com/docker/docker/AUTHORS delete mode 100644 vendor/github.com/docker/docker/NOTICE delete mode 100644 vendor/github.com/dustin/go-humanize/.travis.yml delete mode 100644 vendor/github.com/dustin/go-humanize/LICENSE delete mode 100644 vendor/github.com/dustin/go-humanize/README.markdown delete mode 100644 vendor/github.com/dustin/go-humanize/big.go delete mode 100644 vendor/github.com/dustin/go-humanize/bigbytes.go delete mode 100644 vendor/github.com/dustin/go-humanize/bytes.go delete mode 100644 vendor/github.com/dustin/go-humanize/comma.go delete mode 100644 vendor/github.com/dustin/go-humanize/commaf.go delete mode 100644 vendor/github.com/dustin/go-humanize/ftoa.go delete mode 100644 vendor/github.com/dustin/go-humanize/humanize.go delete mode 100644 vendor/github.com/dustin/go-humanize/number.go delete mode 100644 vendor/github.com/dustin/go-humanize/ordinals.go delete mode 100644 vendor/github.com/dustin/go-humanize/si.go delete mode 100644 vendor/github.com/dustin/go-humanize/times.go delete mode 100644 vendor/github.com/gorilla/websocket/.gitignore delete mode 100644 vendor/github.com/gorilla/websocket/.travis.yml delete mode 100644 vendor/github.com/gorilla/websocket/AUTHORS delete mode 100644 vendor/github.com/gorilla/websocket/LICENSE delete mode 100644 vendor/github.com/gorilla/websocket/README.md delete mode 100644 vendor/github.com/gorilla/websocket/client.go delete mode 100644 vendor/github.com/gorilla/websocket/client_clone.go delete mode 100644 vendor/github.com/gorilla/websocket/client_clone_legacy.go delete mode 100644 vendor/github.com/gorilla/websocket/compression.go delete mode 100644 vendor/github.com/gorilla/websocket/conn.go delete mode 100644 vendor/github.com/gorilla/websocket/conn_read.go delete mode 100644 vendor/github.com/gorilla/websocket/conn_read_legacy.go delete mode 100644 vendor/github.com/gorilla/websocket/doc.go delete mode 100644 vendor/github.com/gorilla/websocket/json.go delete mode 100644 vendor/github.com/gorilla/websocket/mask.go delete mode 100644 vendor/github.com/gorilla/websocket/mask_safe.go delete mode 100644 vendor/github.com/gorilla/websocket/prepared.go delete mode 100644 vendor/github.com/gorilla/websocket/server.go delete mode 100644 vendor/github.com/gorilla/websocket/util.go delete mode 100644 vendor/github.com/moul/anonuuid/.gitignore delete mode 100644 vendor/github.com/moul/anonuuid/.godir delete mode 100644 vendor/github.com/moul/anonuuid/.goxc.json delete mode 100644 vendor/github.com/moul/anonuuid/.travis.yml delete mode 100644 vendor/github.com/moul/anonuuid/LICENSE delete mode 100644 vendor/github.com/moul/anonuuid/Makefile delete mode 100644 vendor/github.com/moul/anonuuid/README.md delete mode 100644 vendor/github.com/moul/anonuuid/anonuuid.go delete mode 100644 vendor/github.com/moul/gotty-client/.gitignore delete mode 100644 vendor/github.com/moul/gotty-client/.goxc.json delete mode 100644 vendor/github.com/moul/gotty-client/.travis.yml delete mode 100644 vendor/github.com/moul/gotty-client/Dockerfile delete mode 100644 vendor/github.com/moul/gotty-client/LICENSE delete mode 100644 vendor/github.com/moul/gotty-client/LICENSE.apache delete mode 100644 vendor/github.com/moul/gotty-client/Makefile delete mode 100644 vendor/github.com/moul/gotty-client/README.md delete mode 100644 vendor/github.com/moul/gotty-client/arch.go delete mode 100644 vendor/github.com/moul/gotty-client/arch_windows.go delete mode 100644 vendor/github.com/moul/gotty-client/gotty-client.go delete mode 100644 vendor/github.com/moul/gotty-client/proxy.go delete mode 100644 vendor/github.com/renstrom/fuzzysearch/LICENSE delete mode 100644 vendor/github.com/renstrom/fuzzysearch/fuzzy/fuzzy.go delete mode 100644 vendor/github.com/renstrom/fuzzysearch/fuzzy/levenshtein.go delete mode 100644 vendor/github.com/scaleway/scaleway-cli/LICENSE.md delete mode 100644 vendor/github.com/scaleway/scaleway-cli/pkg/api/README.md delete mode 100644 vendor/github.com/scaleway/scaleway-cli/pkg/api/api.go delete mode 100644 vendor/github.com/scaleway/scaleway-cli/pkg/api/cache.go delete mode 100644 vendor/github.com/scaleway/scaleway-cli/pkg/api/helpers.go delete mode 100644 vendor/github.com/scaleway/scaleway-cli/pkg/api/logger.go delete mode 100644 vendor/github.com/scaleway/scaleway-cli/pkg/sshcommand/sshcommand.go delete mode 100644 vendor/github.com/scaleway/scaleway-cli/pkg/utils/quiet.go delete mode 100644 vendor/github.com/scaleway/scaleway-cli/pkg/utils/utils.go rename vendor/github.com/{docker/docker => scaleway/scaleway-sdk-go}/LICENSE (99%) 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/security_group_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/auth.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/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 rename vendor/github.com/{docker/docker/pkg/namesgenerator/names-generator.go => scaleway/scaleway-sdk-go/namegenerator/name_generator.go} (66%) 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/config_legacy.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_option.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 diff --git a/builder/scaleway/artifact.go b/builder/scaleway/artifact.go index 0e103cc42..6fe31ee7d 100644 --- a/builder/scaleway/artifact.go +++ b/builder/scaleway/artifact.go @@ -4,7 +4,8 @@ import ( "fmt" "log" - "github.com/scaleway/scaleway-cli/pkg/api" + "github.com/scaleway/scaleway-sdk-go/api/instance/v1" + "github.com/scaleway/scaleway-sdk-go/scw" ) type Artifact struct { @@ -20,11 +21,11 @@ type Artifact struct { // The ID of the snapshot snapshotID string - // The name of the region - regionName string + // The name of the zone + zoneName string // The client for making API calls - client *api.ScalewayAPI + client *scw.Client // StateData should store data such as GeneratedData // to be shared with post-processors @@ -41,12 +42,12 @@ func (*Artifact) Files() []string { } func (a *Artifact) Id() string { - return fmt.Sprintf("%s:%s", a.regionName, a.imageID) + return fmt.Sprintf("%s:%s", a.zoneName, a.imageID) } func (a *Artifact) String() string { - return fmt.Sprintf("An image was created: '%v' (ID: %v) in region '%v' based on snapshot '%v' (ID: %v)", - a.imageName, a.imageID, a.regionName, a.snapshotName, a.snapshotID) + return fmt.Sprintf("An image was created: '%v' (ID: %v) in zone '%v' based on snapshot '%v' (ID: %v)", + a.imageName, a.imageID, a.zoneName, a.snapshotName, a.snapshotID) } func (a *Artifact) State(name string) interface{} { @@ -55,11 +56,19 @@ func (a *Artifact) State(name string) interface{} { func (a *Artifact) Destroy() error { log.Printf("Destroying image: %s (%s)", a.imageID, a.imageName) - if err := a.client.DeleteImage(a.imageID); err != nil { + instanceAPI := instance.NewAPI(a.client) + + err := instanceAPI.DeleteImage(&instance.DeleteImageRequest{ + ImageID: a.imageID, + }) + if err != nil { return err } log.Printf("Destroying snapshot: %s (%s)", a.snapshotID, a.snapshotName) - if err := a.client.DeleteSnapshot(a.snapshotID); err != nil { + err = instanceAPI.DeleteSnapshot(&instance.DeleteSnapshotRequest{ + SnapshotID: a.snapshotID, + }) + if err != nil { return err } return nil diff --git a/builder/scaleway/artifact_test.go b/builder/scaleway/artifact_test.go index 3583fd4e3..619e2ebc2 100644 --- a/builder/scaleway/artifact_test.go +++ b/builder/scaleway/artifact_test.go @@ -27,7 +27,7 @@ func TestArtifactId(t *testing.T) { func TestArtifactString(t *testing.T) { generatedData := make(map[string]interface{}) a := &Artifact{"packer-foobar-image", "cc586e45-5156-4f71-b223-cf406b10dd1d", "packer-foobar-snapshot", "cc586e45-5156-4f71-b223-cf406b10dd1c", "ams1", nil, generatedData} - expected := "An image was created: 'packer-foobar-image' (ID: cc586e45-5156-4f71-b223-cf406b10dd1d) in region 'ams1' based on snapshot 'packer-foobar-snapshot' (ID: cc586e45-5156-4f71-b223-cf406b10dd1c)" + expected := "An image was created: 'packer-foobar-image' (ID: cc586e45-5156-4f71-b223-cf406b10dd1d) in zone 'ams1' based on snapshot 'packer-foobar-snapshot' (ID: cc586e45-5156-4f71-b223-cf406b10dd1c)" if a.String() != expected { t.Fatalf("artifact string should match: %v", expected) diff --git a/builder/scaleway/builder.go b/builder/scaleway/builder.go index 1f25a0509..018ff060d 100644 --- a/builder/scaleway/builder.go +++ b/builder/scaleway/builder.go @@ -13,7 +13,7 @@ import ( "github.com/hashicorp/packer/helper/communicator" "github.com/hashicorp/packer/helper/multistep" "github.com/hashicorp/packer/packer" - "github.com/scaleway/scaleway-cli/pkg/api" + "github.com/scaleway/scaleway-sdk-go/scw" ) // The unique id for the builder @@ -32,12 +32,27 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, []string, error) { return nil, warnings, errs } - return nil, nil, nil + return nil, warnings, nil } func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (packer.Artifact, error) { - client, err := api.NewScalewayAPI(b.config.Organization, b.config.Token, b.config.UserAgent, b.config.Region) + scwZone, err := scw.ParseZone(b.config.Zone) + if err != nil { + ui.Error(err.Error()) + return nil, err + } + clientOpts := []scw.ClientOption{ + scw.WithDefaultProjectID(b.config.ProjectID), + scw.WithAuth(b.config.AccessKey, b.config.SecretKey), + scw.WithDefaultZone(scwZone), + } + + if b.config.APIURL != "" { + clientOpts = append(clientOpts, scw.WithAPIURL(b.config.APIURL)) + } + + client, err := scw.NewClient(clientOpts...) if err != nil { ui.Error(err.Error()) return nil, err @@ -96,7 +111,7 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack imageID: state.Get("image_id").(string), snapshotName: state.Get("snapshot_name").(string), snapshotID: state.Get("snapshot_id").(string), - regionName: state.Get("region").(string), + zoneName: b.config.Zone, client: client, StateData: map[string]interface{}{"generated_data": state.Get("generated_data")}, } diff --git a/builder/scaleway/builder_test.go b/builder/scaleway/builder_test.go index be0b2bc7e..e223d6e42 100644 --- a/builder/scaleway/builder_test.go +++ b/builder/scaleway/builder_test.go @@ -9,9 +9,10 @@ import ( func testConfig() map[string]interface{} { return map[string]interface{}{ - "organization_id": "foo", - "api_token": "bar", - "region": "ams1", + "project_id": "00000000-1111-2222-3333-444444444444", + "access_key": "SCWABCXXXXXXXXXXXXXX", + "secret_key": "00000000-1111-2222-3333-444444444444", + "zone": "fr-par-1", "commercial_type": "START1-S", "ssh_username": "root", "image": "image-uuid", @@ -32,10 +33,7 @@ func TestBuilder_Prepare_BadType(t *testing.T) { "api_token": []string{}, } - _, warnings, err := b.Prepare(c) - if len(warnings) > 0 { - t.Fatalf("bad: %#v", warnings) - } + _, _, err := b.Prepare(c) if err == nil { t.Fatalf("prepare should fail") } @@ -68,11 +66,11 @@ func TestBuilderPrepare_InvalidKey(t *testing.T) { } } -func TestBuilderPrepare_Region(t *testing.T) { +func TestBuilderPrepare_Zone(t *testing.T) { var b Builder config := testConfig() - delete(config, "region") + delete(config, "zone") _, warnings, err := b.Prepare(config) if len(warnings) > 0 { t.Fatalf("bad: %#v", warnings) @@ -81,9 +79,9 @@ func TestBuilderPrepare_Region(t *testing.T) { t.Fatalf("should error") } - expected := "ams1" + expected := "fr-par-1" - config["region"] = expected + config["zone"] = expected b = Builder{} _, warnings, err = b.Prepare(config) if len(warnings) > 0 { @@ -93,8 +91,8 @@ func TestBuilderPrepare_Region(t *testing.T) { t.Fatalf("should not have error: %s", err) } - if b.config.Region != expected { - t.Errorf("found %s, expected %s", b.config.Region, expected) + if b.config.Zone != expected { + t.Errorf("found %s, expected %s", b.config.Zone, expected) } } diff --git a/builder/scaleway/config.go b/builder/scaleway/config.go index ae4fc502a..2f744986b 100644 --- a/builder/scaleway/config.go +++ b/builder/scaleway/config.go @@ -17,27 +17,29 @@ import ( "github.com/hashicorp/packer/packer" "github.com/hashicorp/packer/template/interpolate" "github.com/mitchellh/mapstructure" + "github.com/scaleway/scaleway-sdk-go/api/instance/v1" + "github.com/scaleway/scaleway-sdk-go/scw" ) type Config struct { common.PackerConfig `mapstructure:",squash"` Comm communicator.Config `mapstructure:",squash"` - // The token to use to authenticate with your account. - // It can also be specified via environment variable SCALEWAY_API_TOKEN. You - // can see and generate tokens in the "Credentials" - // section of the control panel. - Token string `mapstructure:"api_token" required:"true"` - // The organization id to use to identify your - // organization. It can also be specified via environment variable - // SCALEWAY_ORGANIZATION. Your organization id is available in the - // "Account" section of the - // control panel. - // Previously named: api_access_key with environment variable: SCALEWAY_API_ACCESS_KEY - Organization string `mapstructure:"organization_id" required:"true"` - // The name of the region to launch the server in (par1 - // or ams1). Consequently, this is the region where the snapshot will be - // available. - Region string `mapstructure:"region" required:"true"` + // The AccessKey corresponding to the secret key. + // It can also be specified via the environment variable SCW_ACCESS_KEY. + AccessKey string `mapstructure:"access_key" required:"true"` + // The SecretKey to authenticate against the Scaleway API. + // It can also be specified via the environment variable SCW_SECRET_KEY. + SecretKey string `mapstructure:"secret_key" required:"true"` + // The Project ID in which the instances, volumes and snapshots will be created. + // It can also be specified via the environment variable SCW_DEFAULT_PROJECT_ID. + ProjectID string `mapstructure:"project_id" required:"true"` + // The Zone in which the instances, volumes and snapshots will be created. + // It can also be specified via the environment variable SCW_DEFAULT_ZONE + Zone string `mapstructure:"zone" required:"true"` + // The Scaleway API URL to use + // It can also be specified via the environment variable SCW_API_URL + APIURL string `mapstructure:"api_url"` + // The UUID of the base image to use. This is the image // that will be used to launch a new server and provision it. See // the images list @@ -69,6 +71,28 @@ type Config struct { UserAgent string `mapstructure-to-hcl2:",skip"` ctx interpolate.Context + + // Deprecated configs + + // The token to use to authenticate with your account. + // It can also be specified via environment variable SCALEWAY_API_TOKEN. You + // can see and generate tokens in the "Credentials" + // section of the control panel. + // Deprecated, use SecretKey instead + Token string `mapstructure:"api_token" required:"false"` + // The organization id to use to identify your + // organization. It can also be specified via environment variable + // SCALEWAY_ORGANIZATION. Your organization id is available in the + // "Account" section of the + // control panel. + // Previously named: api_access_key with environment variable: SCALEWAY_API_ACCESS_KEY + // Deprecated, use ProjectID instead + Organization string `mapstructure:"organization_id" required:"false"` + // The name of the region to launch the server in (par1 + // or ams1). Consequently, this is the region where the snapshot will be + // available. + // Deprecated, use Zone instead + Region string `mapstructure:"region" required:"false"` } func (c *Config) Prepare(raws ...interface{}) ([]string, error) { @@ -88,8 +112,11 @@ func (c *Config) Prepare(raws ...interface{}) ([]string, error) { return nil, err } + var warnings []string + c.UserAgent = useragent.String() + // Deprecated variables if c.Organization == "" { if os.Getenv("SCALEWAY_ORGANIZATION") != "" { c.Organization = os.Getenv("SCALEWAY_ORGANIZATION") @@ -98,10 +125,43 @@ func (c *Config) Prepare(raws ...interface{}) ([]string, error) { c.Organization = os.Getenv("SCALEWAY_API_ACCESS_KEY") } } + if c.Organization != "" { + warnings = append(warnings, "organization_id is deprecated in favor of project_id") + c.ProjectID = c.Organization + } if c.Token == "" { c.Token = os.Getenv("SCALEWAY_API_TOKEN") } + if c.Token != "" { + warnings = append(warnings, "token is deprecated in favor of secret_key") + c.SecretKey = c.Token + } + + if c.Region != "" { + warnings = append(warnings, "region is deprecated in favor of zone") + c.Zone = c.Region + } + + if c.AccessKey == "" { + c.AccessKey = os.Getenv(scw.ScwAccessKeyEnv) + } + + if c.SecretKey == "" { + c.SecretKey = os.Getenv(scw.ScwSecretKeyEnv) + } + + if c.ProjectID == "" { + c.ProjectID = os.Getenv(scw.ScwDefaultProjectIDEnv) + } + + if c.Zone == "" { + c.Zone = os.Getenv(scw.ScwDefaultZoneEnv) + } + + if c.APIURL == "" { + c.APIURL = os.Getenv(scw.ScwAPIURLEnv) + } if c.SnapshotName == "" { def, err := interpolate.Render("snapshot-packer-{{timestamp}}", nil) @@ -127,26 +187,31 @@ func (c *Config) Prepare(raws ...interface{}) ([]string, error) { } if c.BootType == "" { - c.BootType = "local" + c.BootType = instance.BootTypeLocal.String() } var errs *packer.MultiError if es := c.Comm.Prepare(&c.ctx); len(es) > 0 { errs = packer.MultiErrorAppend(errs, es...) } - if c.Organization == "" { + if c.ProjectID == "" { errs = packer.MultiErrorAppend( - errs, errors.New("Scaleway Organization ID must be specified")) + errs, errors.New("Scaleway Project ID must be specified")) } - if c.Token == "" { + if c.SecretKey == "" { errs = packer.MultiErrorAppend( - errs, errors.New("Scaleway Token must be specified")) + errs, errors.New("Scaleway Secret Key must be specified")) } - if c.Region == "" { + if c.AccessKey == "" { + warnings = append(warnings, "access_key will be required in future versions") + c.AccessKey = "SCWXXXXXXXXXXXXXXXXX" + } + + if c.Zone == "" { errs = packer.MultiErrorAppend( - errs, errors.New("region is required")) + errs, errors.New("Scaleway Zone is required")) } if c.CommercialType == "" { @@ -160,9 +225,9 @@ func (c *Config) Prepare(raws ...interface{}) ([]string, error) { } if errs != nil && len(errs.Errors) > 0 { - return nil, errs + return warnings, errs } packer.LogSecretFilter.Set(c.Token) - return nil, nil + return warnings, nil } diff --git a/builder/scaleway/config.hcl2spec.go b/builder/scaleway/config.hcl2spec.go index 9c4ed27ed..5de81642f 100644 --- a/builder/scaleway/config.hcl2spec.go +++ b/builder/scaleway/config.hcl2spec.go @@ -63,9 +63,11 @@ type FlatConfig struct { WinRMUseSSL *bool `mapstructure:"winrm_use_ssl" cty:"winrm_use_ssl" hcl:"winrm_use_ssl"` WinRMInsecure *bool `mapstructure:"winrm_insecure" cty:"winrm_insecure" hcl:"winrm_insecure"` WinRMUseNTLM *bool `mapstructure:"winrm_use_ntlm" cty:"winrm_use_ntlm" hcl:"winrm_use_ntlm"` - Token *string `mapstructure:"api_token" required:"true" cty:"api_token" hcl:"api_token"` - Organization *string `mapstructure:"organization_id" required:"true" cty:"organization_id" hcl:"organization_id"` - Region *string `mapstructure:"region" required:"true" cty:"region" hcl:"region"` + AccessKey *string `mapstructure:"access_key" required:"true" cty:"access_key" hcl:"access_key"` + SecretKey *string `mapstructure:"secret_key" required:"true" cty:"secret_key" hcl:"secret_key"` + ProjectID *string `mapstructure:"project_id" required:"true" cty:"project_id" hcl:"project_id"` + Zone *string `mapstructure:"zone" required:"true" cty:"zone" hcl:"zone"` + APIURL *string `mapstructure:"api_url" cty:"api_url" hcl:"api_url"` Image *string `mapstructure:"image" required:"true" cty:"image" hcl:"image"` CommercialType *string `mapstructure:"commercial_type" required:"true" cty:"commercial_type" hcl:"commercial_type"` SnapshotName *string `mapstructure:"snapshot_name" required:"false" cty:"snapshot_name" hcl:"snapshot_name"` @@ -74,6 +76,9 @@ type FlatConfig struct { Bootscript *string `mapstructure:"bootscript" required:"false" cty:"bootscript" hcl:"bootscript"` BootType *string `mapstructure:"boottype" required:"false" cty:"boottype" hcl:"boottype"` RemoveVolume *bool `mapstructure:"remove_volume" cty:"remove_volume" hcl:"remove_volume"` + Token *string `mapstructure:"api_token" required:"false" cty:"api_token" hcl:"api_token"` + Organization *string `mapstructure:"organization_id" required:"false" cty:"organization_id" hcl:"organization_id"` + Region *string `mapstructure:"region" required:"false" cty:"region" hcl:"region"` } // FlatMapstructure returns a new FlatConfig. @@ -142,9 +147,11 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { "winrm_use_ssl": &hcldec.AttrSpec{Name: "winrm_use_ssl", Type: cty.Bool, Required: false}, "winrm_insecure": &hcldec.AttrSpec{Name: "winrm_insecure", Type: cty.Bool, Required: false}, "winrm_use_ntlm": &hcldec.AttrSpec{Name: "winrm_use_ntlm", Type: cty.Bool, Required: false}, - "api_token": &hcldec.AttrSpec{Name: "api_token", Type: cty.String, Required: false}, - "organization_id": &hcldec.AttrSpec{Name: "organization_id", Type: cty.String, Required: false}, - "region": &hcldec.AttrSpec{Name: "region", Type: cty.String, Required: false}, + "access_key": &hcldec.AttrSpec{Name: "access_key", Type: cty.String, Required: false}, + "secret_key": &hcldec.AttrSpec{Name: "secret_key", Type: cty.String, Required: false}, + "project_id": &hcldec.AttrSpec{Name: "project_id", Type: cty.String, Required: false}, + "zone": &hcldec.AttrSpec{Name: "zone", Type: cty.String, Required: false}, + "api_url": &hcldec.AttrSpec{Name: "api_url", Type: cty.String, Required: false}, "image": &hcldec.AttrSpec{Name: "image", Type: cty.String, Required: false}, "commercial_type": &hcldec.AttrSpec{Name: "commercial_type", Type: cty.String, Required: false}, "snapshot_name": &hcldec.AttrSpec{Name: "snapshot_name", Type: cty.String, Required: false}, @@ -153,6 +160,9 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { "bootscript": &hcldec.AttrSpec{Name: "bootscript", Type: cty.String, Required: false}, "boottype": &hcldec.AttrSpec{Name: "boottype", Type: cty.String, Required: false}, "remove_volume": &hcldec.AttrSpec{Name: "remove_volume", Type: cty.Bool, Required: false}, + "api_token": &hcldec.AttrSpec{Name: "api_token", Type: cty.String, Required: false}, + "organization_id": &hcldec.AttrSpec{Name: "organization_id", Type: cty.String, Required: false}, + "region": &hcldec.AttrSpec{Name: "region", Type: cty.String, Required: false}, } return s } diff --git a/builder/scaleway/step_create_image.go b/builder/scaleway/step_create_image.go index 41f6eee2d..ef8f586a9 100644 --- a/builder/scaleway/step_create_image.go +++ b/builder/scaleway/step_create_image.go @@ -7,13 +7,14 @@ import ( "github.com/hashicorp/packer/helper/multistep" "github.com/hashicorp/packer/packer" - "github.com/scaleway/scaleway-cli/pkg/api" + "github.com/scaleway/scaleway-sdk-go/api/instance/v1" + "github.com/scaleway/scaleway-sdk-go/scw" ) type stepImage struct{} func (s *stepImage) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { - client := state.Get("client").(*api.ScalewayAPI) + instanceAPI := instance.NewAPI(state.Get("client").(*scw.Client)) ui := state.Get("ui").(packer.Ui) c := state.Get("config").(*Config) snapshotID := state.Get("snapshot_id").(string) @@ -21,7 +22,9 @@ func (s *stepImage) Run(ctx context.Context, state multistep.StateBag) multistep ui.Say(fmt.Sprintf("Creating image: %v", c.ImageName)) - image, err := client.GetImage(c.Image) + imageResp, err := instanceAPI.GetImage(&instance.GetImageRequest{ + ImageID: c.Image, + }) if err != nil { err := fmt.Errorf("Error getting initial image info: %s", err) state.Put("error", err) @@ -29,11 +32,16 @@ func (s *stepImage) Run(ctx context.Context, state multistep.StateBag) multistep return multistep.ActionHalt } - if image.DefaultBootscript != nil { - bootscriptID = image.DefaultBootscript.Identifier + if imageResp.Image.DefaultBootscript != nil { + bootscriptID = imageResp.Image.DefaultBootscript.ID } - imageID, err := client.PostImage(snapshotID, c.ImageName, bootscriptID, image.Arch) + createImageResp, err := instanceAPI.CreateImage(&instance.CreateImageRequest{ + Arch: imageResp.Image.Arch, + DefaultBootscript: bootscriptID, + Name: c.ImageName, + RootVolume: snapshotID, + }) if err != nil { err := fmt.Errorf("Error creating image: %s", err) state.Put("error", err) @@ -41,10 +49,11 @@ func (s *stepImage) Run(ctx context.Context, state multistep.StateBag) multistep return multistep.ActionHalt } - log.Printf("Image ID: %s", imageID) - state.Put("image_id", imageID) + log.Printf("Image ID: %s", createImageResp.Image.ID) + state.Put("image_id", createImageResp.Image.ID) state.Put("image_name", c.ImageName) - state.Put("region", c.Region) + state.Put("region", c.Zone) // Deprecated + state.Put("zone", c.Zone) return multistep.ActionContinue } diff --git a/builder/scaleway/step_create_server.go b/builder/scaleway/step_create_server.go index 06a72d95a..3342305b1 100644 --- a/builder/scaleway/step_create_server.go +++ b/builder/scaleway/step_create_server.go @@ -7,7 +7,8 @@ import ( "github.com/hashicorp/packer/helper/multistep" "github.com/hashicorp/packer/packer" - "github.com/scaleway/scaleway-cli/pkg/api" + "github.com/scaleway/scaleway-sdk-go/api/instance/v1" + "github.com/scaleway/scaleway-sdk-go/scw" ) type stepCreateServer struct { @@ -15,7 +16,7 @@ type stepCreateServer struct { } func (s *stepCreateServer) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { - client := state.Get("client").(*api.ScalewayAPI) + instanceAPI := instance.NewAPI(state.Get("client").(*scw.Client)) ui := state.Get("ui").(packer.Ui) c := state.Get("config").(*Config) tags := []string{} @@ -31,16 +32,16 @@ func (s *stepCreateServer) Run(ctx context.Context, state multistep.StateBag) mu tags = []string{fmt.Sprintf("AUTHORIZED_KEY=%s", strings.Replace(strings.TrimSpace(string(c.Comm.SSHPublicKey)), " ", "_", -1))} } - server, err := client.PostServer(api.ScalewayServerDefinition{ - Name: c.ServerName, - Image: &c.Image, - Organization: c.Organization, - CommercialType: c.CommercialType, - Tags: tags, - Bootscript: bootscript, - BootType: c.BootType, - }) + bootType := instance.BootType(c.BootType) + createServerResp, err := instanceAPI.CreateServer(&instance.CreateServerRequest{ + BootType: &bootType, + Bootscript: bootscript, + CommercialType: c.CommercialType, + Name: c.ServerName, + Image: c.Image, + Tags: tags, + }) if err != nil { err := fmt.Errorf("Error creating server: %s", err) state.Put("error", err) @@ -48,8 +49,10 @@ func (s *stepCreateServer) Run(ctx context.Context, state multistep.StateBag) mu return multistep.ActionHalt } - err = client.PostServerAction(server, "poweron") - + _, err = instanceAPI.ServerAction(&instance.ServerActionRequest{ + Action: instance.ServerActionPoweron, + ServerID: createServerResp.Server.ID, + }) if err != nil { err := fmt.Errorf("Error starting server: %s", err) state.Put("error", err) @@ -57,9 +60,9 @@ func (s *stepCreateServer) Run(ctx context.Context, state multistep.StateBag) mu return multistep.ActionHalt } - s.serverID = server + s.serverID = createServerResp.Server.ID - state.Put("server_id", server) + state.Put("server_id", createServerResp.Server.ID) // instance_id is the generic term used so that users can have access to the // instance id inside of the provisioners, used in step_provision. state.Put("instance_id", s.serverID) @@ -72,16 +75,22 @@ func (s *stepCreateServer) Cleanup(state multistep.StateBag) { return } - client := state.Get("client").(*api.ScalewayAPI) + instanceAPI := instance.NewAPI(state.Get("client").(*scw.Client)) ui := state.Get("ui").(packer.Ui) ui.Say("Destroying server...") - err := client.DeleteServerForce(s.serverID) - + err := instanceAPI.DeleteServer(&instance.DeleteServerRequest{ + ServerID: s.serverID, + }) if err != nil { - ui.Error(fmt.Sprintf( - "Error destroying server. Please destroy it manually: %s", err)) + _, err = instanceAPI.ServerAction(&instance.ServerActionRequest{ + Action: instance.ServerActionTerminate, + ServerID: s.serverID, + }) + if err != nil { + ui.Error(fmt.Sprintf( + "Error destroying server. Please destroy it manually: %s", err)) + } } - } diff --git a/builder/scaleway/step_remove_volume.go b/builder/scaleway/step_remove_volume.go index 9bc6125c1..a9abc2d3b 100644 --- a/builder/scaleway/step_remove_volume.go +++ b/builder/scaleway/step_remove_volume.go @@ -4,10 +4,10 @@ import ( "context" "fmt" - "github.com/scaleway/scaleway-cli/pkg/api" - "github.com/hashicorp/packer/helper/multistep" "github.com/hashicorp/packer/packer" + "github.com/scaleway/scaleway-sdk-go/api/instance/v1" + "github.com/scaleway/scaleway-sdk-go/scw" ) type stepRemoveVolume struct{} @@ -24,7 +24,7 @@ func (s *stepRemoveVolume) Cleanup(state multistep.StateBag) { return } - client := state.Get("client").(*api.ScalewayAPI) + instanceAPI := instance.NewAPI(state.Get("client").(*scw.Client)) ui := state.Get("ui").(packer.Ui) c := state.Get("config").(*Config) volumeID := state.Get("root_volume_id").(string) @@ -35,7 +35,9 @@ func (s *stepRemoveVolume) Cleanup(state multistep.StateBag) { ui.Say("Removing Volume ...") - err := client.DeleteVolume(volumeID) + err := instanceAPI.DeleteVolume(&instance.DeleteVolumeRequest{ + VolumeID: volumeID, + }) if err != nil { err := fmt.Errorf("Error removing volume: %s", err) state.Put("error", err) diff --git a/builder/scaleway/step_server_info.go b/builder/scaleway/step_server_info.go index 34b7db1da..7d5f7d1ba 100644 --- a/builder/scaleway/step_server_info.go +++ b/builder/scaleway/step_server_info.go @@ -6,19 +6,22 @@ import ( "github.com/hashicorp/packer/helper/multistep" "github.com/hashicorp/packer/packer" - "github.com/scaleway/scaleway-cli/pkg/api" + "github.com/scaleway/scaleway-sdk-go/api/instance/v1" + "github.com/scaleway/scaleway-sdk-go/scw" ) type stepServerInfo struct{} func (s *stepServerInfo) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { - client := state.Get("client").(*api.ScalewayAPI) + instanceAPI := instance.NewAPI(state.Get("client").(*scw.Client)) ui := state.Get("ui").(packer.Ui) serverID := state.Get("server_id").(string) ui.Say("Waiting for server to become active...") - _, err := api.WaitForServerState(client, serverID, "running") + instanceResp, err := instanceAPI.WaitForServer(&instance.WaitForServerRequest{ + ServerID: serverID, + }) if err != nil { err := fmt.Errorf("Error waiting for server to become booted: %s", err) state.Put("error", err) @@ -26,16 +29,22 @@ func (s *stepServerInfo) Run(ctx context.Context, state multistep.StateBag) mult return multistep.ActionHalt } - server, err := client.GetServer(serverID) - if err != nil { - err := fmt.Errorf("Error retrieving server: %s", err) + if instanceResp.State != instance.ServerStateRunning { + err := fmt.Errorf("Server is in state %s", instanceResp.State.String()) state.Put("error", err) ui.Error(err.Error()) return multistep.ActionHalt } - state.Put("server_ip", server.PublicAddress.IP) - state.Put("root_volume_id", server.Volumes["0"].Identifier) + if instanceResp.PublicIP == nil { + err := fmt.Errorf("Server does not have a public IP") + state.Put("error", err) + ui.Error(err.Error()) + return multistep.ActionHalt + } + + state.Put("server_ip", instanceResp.PublicIP.Address.String()) + state.Put("root_volume_id", instanceResp.Volumes["0"].ID) return multistep.ActionContinue } diff --git a/builder/scaleway/step_shutdown.go b/builder/scaleway/step_shutdown.go index 69bbf3586..60d76d907 100644 --- a/builder/scaleway/step_shutdown.go +++ b/builder/scaleway/step_shutdown.go @@ -6,20 +6,23 @@ import ( "github.com/hashicorp/packer/helper/multistep" "github.com/hashicorp/packer/packer" - "github.com/scaleway/scaleway-cli/pkg/api" + "github.com/scaleway/scaleway-sdk-go/api/instance/v1" + "github.com/scaleway/scaleway-sdk-go/scw" ) type stepShutdown struct{} func (s *stepShutdown) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { - client := state.Get("client").(*api.ScalewayAPI) + instanceAPI := instance.NewAPI(state.Get("client").(*scw.Client)) ui := state.Get("ui").(packer.Ui) serverID := state.Get("server_id").(string) ui.Say("Shutting down server...") - err := client.PostServerAction(serverID, "poweroff") - + _, err := instanceAPI.ServerAction(&instance.ServerActionRequest{ + Action: instance.ServerActionPoweroff, + ServerID: serverID, + }) if err != nil { err := fmt.Errorf("Error stopping server: %s", err) state.Put("error", err) @@ -27,8 +30,9 @@ func (s *stepShutdown) Run(ctx context.Context, state multistep.StateBag) multis return multistep.ActionHalt } - _, err = api.WaitForServerState(client, serverID, "stopped") - + instanceResp, err := instanceAPI.WaitForServer(&instance.WaitForServerRequest{ + ServerID: serverID, + }) if err != nil { err := fmt.Errorf("Error shutting down server: %s", err) state.Put("error", err) @@ -36,6 +40,13 @@ func (s *stepShutdown) Run(ctx context.Context, state multistep.StateBag) multis return multistep.ActionHalt } + if instanceResp.State != instance.ServerStateStopped { + err := fmt.Errorf("Server is in state %s instead of stopped", instanceResp.State.String()) + state.Put("error", err) + ui.Error(err.Error()) + return multistep.ActionHalt + } + return multistep.ActionContinue } diff --git a/builder/scaleway/step_snapshot.go b/builder/scaleway/step_snapshot.go index 705a95806..8b4fab470 100644 --- a/builder/scaleway/step_snapshot.go +++ b/builder/scaleway/step_snapshot.go @@ -7,19 +7,23 @@ import ( "github.com/hashicorp/packer/helper/multistep" "github.com/hashicorp/packer/packer" - "github.com/scaleway/scaleway-cli/pkg/api" + "github.com/scaleway/scaleway-sdk-go/api/instance/v1" + "github.com/scaleway/scaleway-sdk-go/scw" ) type stepSnapshot struct{} func (s *stepSnapshot) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { - client := state.Get("client").(*api.ScalewayAPI) + instanceAPI := instance.NewAPI(state.Get("client").(*scw.Client)) ui := state.Get("ui").(packer.Ui) c := state.Get("config").(*Config) volumeID := state.Get("root_volume_id").(string) ui.Say(fmt.Sprintf("Creating snapshot: %v", c.SnapshotName)) - snapshot, err := client.PostSnapshot(volumeID, c.SnapshotName) + createSnapshotResp, err := instanceAPI.CreateSnapshot(&instance.CreateSnapshotRequest{ + Name: c.SnapshotName, + VolumeID: volumeID, + }) if err != nil { err := fmt.Errorf("Error creating snapshot: %s", err) state.Put("error", err) @@ -27,10 +31,11 @@ func (s *stepSnapshot) Run(ctx context.Context, state multistep.StateBag) multis return multistep.ActionHalt } - log.Printf("Snapshot ID: %s", snapshot) - state.Put("snapshot_id", snapshot) + log.Printf("Snapshot ID: %s", createSnapshotResp.Snapshot.ID) + state.Put("snapshot_id", createSnapshotResp.Snapshot.ID) state.Put("snapshot_name", c.SnapshotName) - state.Put("region", c.Region) + state.Put("region", c.Zone) // Deprecated + state.Put("zone", c.Zone) return multistep.ActionContinue } diff --git a/go.mod b/go.mod index ccf98e851..608c6fac7 100644 --- a/go.mod +++ b/go.mod @@ -26,13 +26,10 @@ require ( github.com/c2h5oh/datasize v0.0.0-20200112174442-28bbd4740fee github.com/cheggaaa/pb v1.0.27 github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e - github.com/creack/goselect v0.1.0 // indirect github.com/dgrijalva/jwt-go v3.2.0+incompatible github.com/digitalocean/go-libvirt v0.0.0-20190626172931-4d226dd6c437 // indirect github.com/digitalocean/go-qemu v0.0.0-20181112162955-dd7bb9c771b8 github.com/digitalocean/godo v1.11.1 - github.com/docker/docker v0.0.0-20180422163414-57142e89befe // indirect - github.com/dustin/go-humanize v1.0.0 // indirect github.com/dylanmei/iso8601 v0.1.0 // indirect github.com/dylanmei/winrmtest v0.0.0-20170819153634-c2fbb09e6c08 github.com/exoscale/egoscale v0.18.1 @@ -52,7 +49,6 @@ require ( github.com/gophercloud/gophercloud v0.12.0 github.com/gophercloud/utils v0.0.0-20200508015959-b0167b94122c github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e // indirect - github.com/gorilla/websocket v0.0.0-20170319172727-a91eba7f9777 // indirect github.com/grpc-ecosystem/go-grpc-middleware v1.1.0 github.com/hako/durafmt v0.0.0-20200710122514-c0fb7b4da026 github.com/hashicorp/consul/api v1.4.0 @@ -103,8 +99,6 @@ require ( github.com/mitchellh/reflectwalk v1.0.0 github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.1 // indirect - github.com/moul/anonuuid v0.0.0-20160222162117-609b752a95ef // indirect - github.com/moul/gotty-client v0.0.0-20180327180212-b26a57ebc215 // indirect github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d // indirect github.com/olekukonko/tablewriter v0.0.0-20180105111133-96aac992fc8b github.com/oracle/oci-go-sdk v18.0.0+incompatible @@ -115,9 +109,8 @@ require ( github.com/pkg/sftp v0.0.0-20160118190721-e84cc8c755ca github.com/posener/complete v1.2.3 github.com/profitbricks/profitbricks-sdk-go v4.0.2+incompatible - github.com/renstrom/fuzzysearch v0.0.0-20160331204855-2d205ac6ec17 // indirect github.com/satori/go.uuid v1.2.0 // indirect - github.com/scaleway/scaleway-cli v0.0.0-20180921094345-7b12c9699d70 + github.com/scaleway/scaleway-sdk-go v1.0.0-beta.6.0.20200903143645-c0ce17a0443d github.com/shirou/gopsutil v2.18.12+incompatible github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4 // indirect github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d // indirect diff --git a/go.sum b/go.sum index 6c12cf677..862b11d1c 100644 --- a/go.sum +++ b/go.sum @@ -143,8 +143,6 @@ github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f h1:WBZRG4aNOuI15bLRrCgN8fCq8E5Xuty6jGbmSNEvSsU= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/creack/goselect v0.1.0 h1:4QiXIhcpSQF50XGaBsFzesjwX/1qOY5bOveQPmN9CXY= -github.com/creack/goselect v0.1.0/go.mod h1:gHrIcH/9UZDn2qgeTUeW5K9eZsVYCH6/60J/FHysWyE= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -163,10 +161,6 @@ github.com/diskfs/go-diskfs v1.1.1 h1:rMjLpaydtXGVZb7mdkRGK1+//30i76nKAit89zUzea github.com/diskfs/go-diskfs v1.1.1/go.mod h1:afUPxxu+x1snp4aCY2bKR0CoZ/YFJewV3X2UEr2nPZE= github.com/dnaeon/go-vcr v1.0.1 h1:r8L/HqC0Hje5AXMu1ooW8oyQyOFv4GxqpL0nRP7SLLY= github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E= -github.com/docker/docker v0.0.0-20180422163414-57142e89befe h1:VW8TnWi0CZgg7oCv0wH6evNwkzcJg/emnw4HrVIWws4= -github.com/docker/docker v0.0.0-20180422163414-57142e89befe/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= -github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= -github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dylanmei/iso8601 v0.1.0 h1:812NGQDBcqquTfH5Yeo7lwR0nzx/cKdsmf3qMjPURUI= github.com/dylanmei/iso8601 v0.1.0/go.mod h1:w9KhXSgIyROl1DefbMYIE7UVSIvELTbMrCfx+QkYnoQ= github.com/dylanmei/winrmtest v0.0.0-20170819153634-c2fbb09e6c08 h1:0bp6/GrNOrTDtSXe9YYGCwf8jp5Fb/b+4a6MTRm4qzY= @@ -284,8 +278,6 @@ github.com/gophercloud/utils v0.0.0-20200508015959-b0167b94122c h1:iawx2ojEQA7c+ github.com/gophercloud/utils v0.0.0-20200508015959-b0167b94122c/go.mod h1:ehWUbLQJPqS0Ep+CxeD559hsm9pthPXadJNKwZkp43w= github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e h1:JKmoR8x90Iww1ks85zJ1lfDGgIiMDuIptTOhJq+zKyg= github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= -github.com/gorilla/websocket v0.0.0-20170319172727-a91eba7f9777 h1:JIM+OacoOJRU30xpjMf8sulYqjr0ViA3WDrTX6j/yDI= -github.com/gorilla/websocket v0.0.0-20170319172727-a91eba7f9777/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/grpc-ecosystem/go-grpc-middleware v1.1.0 h1:THDBEeQ9xZ8JEaCLyLQqXMMdRqNr0QAUJTIkQAUtFjg= github.com/grpc-ecosystem/go-grpc-middleware v1.1.0/go.mod h1:f5nM7jw/oeRSadq3xCzHAvxcr8HZnzsqU6ILg/0NiiE= github.com/hako/durafmt v0.0.0-20200710122514-c0fb7b4da026 h1:BpJ2o0OR5FV7vrkDYfXYVJQeMNWa8RhklZOpW2ITAIQ= @@ -503,10 +495,6 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/moul/anonuuid v0.0.0-20160222162117-609b752a95ef h1:E/seV1Rtsnr2juBw1Dfz4iDPT3/5s1H/BATx+ePmSyo= -github.com/moul/anonuuid v0.0.0-20160222162117-609b752a95ef/go.mod h1:LgKrp0Iss/BVwquptq4eIe6HPr0s3t1WHT5x0qOh14U= -github.com/moul/gotty-client v0.0.0-20180327180212-b26a57ebc215 h1:y6FZWUBBt1iPmJyGbGza3ncvVBMKzgd32oFChRZR7Do= -github.com/moul/gotty-client v0.0.0-20180327180212-b26a57ebc215/go.mod h1:CxM/JGtpRrEPve5H04IhxJrGhxgwxMc6jSP2T4YD60w= github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d h1:VhgPp6v9qf9Agr/56bj7Y/xa04UccTW04VP0Qed4vnQ= github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d/go.mod h1:YUTz3bUH2ZwIWBy3CJBeOBEugqcmXREj14T+iG/4k4U= github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= @@ -550,8 +538,6 @@ github.com/prometheus/common v0.0.0-20181126121408-4724e9255275 h1:PnBWHBf+6L0jO github.com/prometheus/common v0.0.0-20181126121408-4724e9255275/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a h1:9a8MnZMP0X2nLJdBg+pBmGgkJlSaKC2KaQmTCk1XDtE= github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/renstrom/fuzzysearch v0.0.0-20160331204855-2d205ac6ec17 h1:4qPms2txLWMLXKzqlnYSulKRS4cS9aYgPtAEpUelQok= -github.com/renstrom/fuzzysearch v0.0.0-20160331204855-2d205ac6ec17/go.mod h1:SAEjPB4voP88qmWJXI7mA5m15uNlEnuHLx4Eu2mPGpQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/ryanuber/columnize v2.1.0+incompatible h1:j1Wcmh8OrK4Q7GXY+V7SVSY8nUWQxHW5TkBe7YUl+2s= @@ -560,8 +546,8 @@ github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkB github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc= github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= -github.com/scaleway/scaleway-cli v0.0.0-20180921094345-7b12c9699d70 h1:DaqC32ZwOuO4ctgg9qAdKnlQxwFPkKmCOEqwSNwYy7c= -github.com/scaleway/scaleway-cli v0.0.0-20180921094345-7b12c9699d70/go.mod h1:XjlXWPd6VONhsRSEuzGkV8mzRpH7ou1cdLV7IKJk96s= +github.com/scaleway/scaleway-sdk-go v1.0.0-beta.6.0.20200903143645-c0ce17a0443d h1:pK33AoOAlzj6gJs/V1vi2Ouj2u1Ww84pREwxFi1oxkM= +github.com/scaleway/scaleway-sdk-go v1.0.0-beta.6.0.20200903143645-c0ce17a0443d/go.mod h1:CJJ5VAbozOl0yEw7nHB9+7BXTJbIn6h7W+f6Gau5IP8= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ= diff --git a/vendor/github.com/creack/goselect/.gitignore b/vendor/github.com/creack/goselect/.gitignore deleted file mode 100644 index 6f4dacea8..000000000 --- a/vendor/github.com/creack/goselect/.gitignore +++ /dev/null @@ -1,29 +0,0 @@ -# Compiled Object files, Static and Dynamic libs (Shared Objects) -*.o -*.a -*.so - -# Folders -_obj -_test - -# Architecture specific extensions/prefixes -*.[568vq] -[568vq].out - -*.cgo1.go -*.cgo2.c -_cgo_defun.c -_cgo_gotypes.go -_cgo_export.* - -_testmain.go - -*.exe -*.test -*.prof - -go-select* -goselect* -example-* -example/example diff --git a/vendor/github.com/creack/goselect/Dockerfile b/vendor/github.com/creack/goselect/Dockerfile deleted file mode 100644 index d03b5a9d9..000000000 --- a/vendor/github.com/creack/goselect/Dockerfile +++ /dev/null @@ -1,5 +0,0 @@ -FROM google/golang:stable -MAINTAINER Guillaume J. Charmes -CMD /tmp/a.out -ADD . /src -RUN cd /src && go build -o /tmp/a.out diff --git a/vendor/github.com/creack/goselect/LICENSE b/vendor/github.com/creack/goselect/LICENSE deleted file mode 100644 index 95c600af1..000000000 --- a/vendor/github.com/creack/goselect/LICENSE +++ /dev/null @@ -1,22 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2014 Guillaume J. Charmes - -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/github.com/creack/goselect/README.md b/vendor/github.com/creack/goselect/README.md deleted file mode 100644 index 807ab85e8..000000000 --- a/vendor/github.com/creack/goselect/README.md +++ /dev/null @@ -1,133 +0,0 @@ -# go-select - -select(2) implementation in Go - -## Supported platforms - -| | 386 | amd64 | arm | arm64 | mips | mipsle | mips64 | mips64le | ppc64le | s390x | -|---------------|-----|-------|-----|-------|------|--------|--------|----------|---------|-------| -| **linux** | yes | yes | yes | yes | yes | yes | yes | yes | yes | yes | -| **darwin** | yes | yes | ?? | ?? | n/a | n/a | n/a | n/a | n/a | n/a | -| **freebsd** | yes | yes | yes | n/a | n/a | n/a | n/a | n/a | n/a | n/a | -| **openbsd** | yes | yes | yes | n/a | n/a | n/a | n/a | n/a | n/a | n/a | -| **netbsd** | yes | yes | yes | n/a | n/a | n/a | n/a | n/a | n/a | n/a | -| **dragonfly** | n/a | yes | n/a | n/a | n/a | n/a | n/a | n/a | n/a | n/a | -| **solaris** | n/a | no | n/a | n/a | n/a | n/a | n/a | n/a | n/a | n/a | -| **plan9** | no | no | no | n/a | n/a | n/a | n/a | n/a | n/a | n/a | -| **windows** | yes | yes | n/a | n/a | n/a | n/a | n/a | n/a | n/a | n/a | -| **android** | ?? | ?? | ?? | ?? | n/a | n/a | n/a | n/a | n/a | n/a | - -*n/a: platform not supported by Go -*??: not tested - -Go on `plan9` and `solaris` do not implement `syscall.Select` nor `syscall.SYS_SELECT`. - -## Cross compile test - -Note that this only tests the compilation, not the functionality. - -```sh -$> ./test_crosscompile.sh > /dev/null | sort -[OK] android/386 -[OK] android/amd64 -[OK] android/arm -[OK] android/arm64 -[OK] darwin/386 -[OK] darwin/amd64 -[OK] darwin/arm -[OK] darwin/arm64 -[OK] dragonfly/amd64 -[OK] freebsd/386 -[OK] freebsd/amd64 -[OK] freebsd/arm -[OK] linux/386 -[OK] linux/amd64 -[OK] linux/arm -[OK] linux/arm64 -[OK] linux/mips -[OK] linux/mips64 -[OK] linux/mips64le -[OK] linux/mipsle -[OK] linux/ppc64le -[OK] linux/s390x -[OK] netbsd/386 -[OK] netbsd/amd64 -[OK] netbsd/arm -[OK] openbsd/386 -[OK] openbsd/amd64 -[OK] openbsd/arm -[OK] plan9/386 -[OK] plan9/amd64 -[OK] plan9/arm -[OK] solaris/amd64 -[OK] windows/386 -[OK] windows/amd64 -[OK] windows/arm - -# Expected failures. -[KO] android/mips -[KO] android/mips64 -[KO] android/mips64le -[KO] android/mipsle -[KO] android/ppc64le -[KO] android/s390x -[KO] darwin/mips -[KO] darwin/mips64 -[KO] darwin/mips64le -[KO] darwin/mipsle -[KO] darwin/ppc64le -[KO] darwin/s390x -[KO] dragonfly/386 -[KO] dragonfly/arm -[KO] dragonfly/arm64 -[KO] dragonfly/mips -[KO] dragonfly/mips64 -[KO] dragonfly/mips64le -[KO] dragonfly/mipsle -[KO] dragonfly/ppc64le -[KO] dragonfly/s390x -[KO] freebsd/arm64 -[KO] freebsd/mips -[KO] freebsd/mips64 -[KO] freebsd/mips64le -[KO] freebsd/mipsle -[KO] freebsd/ppc64le -[KO] freebsd/s390x -[KO] netbsd/arm64 -[KO] netbsd/mips -[KO] netbsd/mips64 -[KO] netbsd/mips64le -[KO] netbsd/mipsle -[KO] netbsd/ppc64le -[KO] netbsd/s390x -[KO] openbsd/arm64 -[KO] openbsd/mips -[KO] openbsd/mips64 -[KO] openbsd/mips64le -[KO] openbsd/mipsle -[KO] openbsd/ppc64le -[KO] openbsd/s390x -[KO] plan9/arm64 -[KO] plan9/mips -[KO] plan9/mips64 -[KO] plan9/mips64le -[KO] plan9/mipsle -[KO] plan9/ppc64le -[KO] plan9/s390x -[KO] solaris/386 -[KO] solaris/arm -[KO] solaris/arm64 -[KO] solaris/mips -[KO] solaris/mips64 -[KO] solaris/mips64le -[KO] solaris/mipsle -[KO] solaris/ppc64le -[KO] solaris/s390x -[KO] windows/arm64 -[KO] windows/mips -[KO] windows/mips64 -[KO] windows/mips64le -[KO] windows/mipsle -[KO] windows/ppc64le -[KO] windows/s390x -``` diff --git a/vendor/github.com/creack/goselect/fdset.go b/vendor/github.com/creack/goselect/fdset.go deleted file mode 100644 index 2ff3f6c7a..000000000 --- a/vendor/github.com/creack/goselect/fdset.go +++ /dev/null @@ -1,33 +0,0 @@ -// +build !freebsd,!windows,!plan9 - -package goselect - -import "syscall" - -const FD_SETSIZE = syscall.FD_SETSIZE - -// FDSet wraps syscall.FdSet with convenience methods -type FDSet syscall.FdSet - -// Set adds the fd to the set -func (fds *FDSet) Set(fd uintptr) { - fds.Bits[fd/NFDBITS] |= (1 << (fd % NFDBITS)) -} - -// Clear remove the fd from the set -func (fds *FDSet) Clear(fd uintptr) { - fds.Bits[fd/NFDBITS] &^= (1 << (fd % NFDBITS)) -} - -// IsSet check if the given fd is set -func (fds *FDSet) IsSet(fd uintptr) bool { - return fds.Bits[fd/NFDBITS]&(1<<(fd%NFDBITS)) != 0 -} - -// Keep a null set to avoid reinstatiation -var nullFdSet = &FDSet{} - -// Zero empties the Set -func (fds *FDSet) Zero() { - copy(fds.Bits[:], (nullFdSet).Bits[:]) -} diff --git a/vendor/github.com/creack/goselect/fdset_32.go b/vendor/github.com/creack/goselect/fdset_32.go deleted file mode 100644 index a346d3f1c..000000000 --- a/vendor/github.com/creack/goselect/fdset_32.go +++ /dev/null @@ -1,10 +0,0 @@ -// +build darwin openbsd netbsd 386 arm mips mipsle - -package goselect - -// darwin, netbsd and openbsd uses uint32 on both amd64 and 386 - -const ( - // NFDBITS is the amount of bits per mask - NFDBITS = 4 * 8 -) diff --git a/vendor/github.com/creack/goselect/fdset_64.go b/vendor/github.com/creack/goselect/fdset_64.go deleted file mode 100644 index 0e70213d8..000000000 --- a/vendor/github.com/creack/goselect/fdset_64.go +++ /dev/null @@ -1,11 +0,0 @@ -// +build !darwin,!netbsd,!openbsd -// +build amd64 arm64 ppc64le mips64 mips64le s390x - -package goselect - -// darwin, netbsd and openbsd uses uint32 on both amd64 and 386 - -const ( - // NFDBITS is the amount of bits per mask - NFDBITS = 8 * 8 -) diff --git a/vendor/github.com/creack/goselect/fdset_doc.go b/vendor/github.com/creack/goselect/fdset_doc.go deleted file mode 100644 index d9f15a1ce..000000000 --- a/vendor/github.com/creack/goselect/fdset_doc.go +++ /dev/null @@ -1,93 +0,0 @@ -package goselect - -/** -From: XCode's MacOSX10.10.sdk/System/Library/Frameworks/Kernel.framework/Versions/A/Headers/sys/select.h --- -// darwin/amd64 / 386 -sizeof(__int32_t) == 4 --- - -typedef __int32_t __fd_mask; - -#define FD_SETSIZE 1024 -#define __NFDBITS (sizeof(__fd_mask) * 8) -#define __howmany(x, y) ((((x) % (y)) == 0) ? ((x) / (y)) : (((x) / (y)) + 1)) - -typedef struct fd_set { - __fd_mask fds_bits[__howmany(__FD_SETSIZE, __NFDBITS)]; -} fd_set; - -#define __FD_MASK(n) ((__fd_mask)1 << ((n) % __NFDBITS)) -#define FD_SET(n, p) ((p)->fds_bits[(n)/__NFDBITS] |= __FD_MASK(n)) -#define FD_CLR(n, p) ((p)->fds_bits[(n)/__NFDBITS] &= ~__FD_MASK(n)) -#define FD_ISSET(n, p) (((p)->fds_bits[(n)/__NFDBITS] & __FD_MASK(n)) != 0) -*/ - -/** -From: /usr/include/i386-linux-gnu/sys/select.h --- -// linux/i686 -sizeof(long int) == 4 --- - -typedef long int __fd_mask; - -#define FD_SETSIZE 1024 -#define __NFDBITS (sizeof(__fd_mask) * 8) - - -typedef struct fd_set { - __fd_mask fds_bits[__FD_SETSIZE / __NFDBITS]; -} fd_set; - -#define __FD_MASK(n) ((__fd_mask)1 << ((n) % __NFDBITS)) -#define FD_SET(n, p) ((p)->fds_bits[(n)/__NFDBITS] |= __FD_MASK(n)) -#define FD_CLR(n, p) ((p)->fds_bits[(n)/__NFDBITS] &= ~__FD_MASK(n)) -#define FD_ISSET(n, p) (((p)->fds_bits[(n)/__NFDBITS] & __FD_MASK(n)) != 0) -*/ - -/** -From: /usr/include/x86_64-linux-gnu/sys/select.h --- -// linux/amd64 -sizeof(long int) == 8 --- - -typedef long int __fd_mask; - -#define FD_SETSIZE 1024 -#define __NFDBITS (sizeof(__fd_mask) * 8) - - -typedef struct fd_set { - __fd_mask fds_bits[__FD_SETSIZE / __NFDBITS]; -} fd_set; - -#define __FD_MASK(n) ((__fd_mask)1 << ((n) % __NFDBITS)) -#define FD_SET(n, p) ((p)->fds_bits[(n)/__NFDBITS] |= __FD_MASK(n)) -#define FD_CLR(n, p) ((p)->fds_bits[(n)/__NFDBITS] &= ~__FD_MASK(n)) -#define FD_ISSET(n, p) (((p)->fds_bits[(n)/__NFDBITS] & __FD_MASK(n)) != 0) -*/ - -/** -From: /usr/include/sys/select.h --- -// freebsd/amd64 -sizeof(unsigned long) == 8 --- - -typedef unsigned long __fd_mask; - -#define FD_SETSIZE 1024U -#define __NFDBITS (sizeof(__fd_mask) * 8) -#define _howmany(x, y) (((x) + ((y) - 1)) / (y)) - -typedef struct fd_set { - __fd_mask fds_bits[_howmany(FD_SETSIZE, __NFDBITS)]; -} fd_set; - -#define __FD_MASK(n) ((__fd_mask)1 << ((n) % __NFDBITS)) -#define FD_SET(n, p) ((p)->fds_bits[(n)/__NFDBITS] |= __FD_MASK(n)) -#define FD_CLR(n, p) ((p)->fds_bits[(n)/__NFDBITS] &= ~__FD_MASK(n)) -#define FD_ISSET(n, p) (((p)->fds_bits[(n)/__NFDBITS] & __FD_MASK(n)) != 0) -*/ diff --git a/vendor/github.com/creack/goselect/fdset_freebsd.go b/vendor/github.com/creack/goselect/fdset_freebsd.go deleted file mode 100644 index 03f7b9127..000000000 --- a/vendor/github.com/creack/goselect/fdset_freebsd.go +++ /dev/null @@ -1,33 +0,0 @@ -// +build freebsd - -package goselect - -import "syscall" - -const FD_SETSIZE = syscall.FD_SETSIZE - -// FDSet wraps syscall.FdSet with convenience methods -type FDSet syscall.FdSet - -// Set adds the fd to the set -func (fds *FDSet) Set(fd uintptr) { - fds.X__fds_bits[fd/NFDBITS] |= (1 << (fd % NFDBITS)) -} - -// Clear remove the fd from the set -func (fds *FDSet) Clear(fd uintptr) { - fds.X__fds_bits[fd/NFDBITS] &^= (1 << (fd % NFDBITS)) -} - -// IsSet check if the given fd is set -func (fds *FDSet) IsSet(fd uintptr) bool { - return fds.X__fds_bits[fd/NFDBITS]&(1<<(fd%NFDBITS)) != 0 -} - -// Keep a null set to avoid reinstatiation -var nullFdSet = &FDSet{} - -// Zero empties the Set -func (fds *FDSet) Zero() { - copy(fds.X__fds_bits[:], (nullFdSet).X__fds_bits[:]) -} diff --git a/vendor/github.com/creack/goselect/fdset_unsupported.go b/vendor/github.com/creack/goselect/fdset_unsupported.go deleted file mode 100644 index cbd8d5880..000000000 --- a/vendor/github.com/creack/goselect/fdset_unsupported.go +++ /dev/null @@ -1,20 +0,0 @@ -// +build plan9 - -package goselect - -const FD_SETSIZE = 0 - -// FDSet wraps syscall.FdSet with convenience methods -type FDSet struct{} - -// Set adds the fd to the set -func (fds *FDSet) Set(fd uintptr) {} - -// Clear remove the fd from the set -func (fds *FDSet) Clear(fd uintptr) {} - -// IsSet check if the given fd is set -func (fds *FDSet) IsSet(fd uintptr) bool { return false } - -// Zero empties the Set -func (fds *FDSet) Zero() {} diff --git a/vendor/github.com/creack/goselect/fdset_windows.go b/vendor/github.com/creack/goselect/fdset_windows.go deleted file mode 100644 index ee3491975..000000000 --- a/vendor/github.com/creack/goselect/fdset_windows.go +++ /dev/null @@ -1,57 +0,0 @@ -// +build windows - -package goselect - -import "syscall" - -const FD_SETSIZE = 64 - -// FDSet extracted from mingw libs source code -type FDSet struct { - fd_count uint - fd_array [FD_SETSIZE]uintptr -} - -// Set adds the fd to the set -func (fds *FDSet) Set(fd uintptr) { - var i uint - for i = 0; i < fds.fd_count; i++ { - if fds.fd_array[i] == fd { - break - } - } - if i == fds.fd_count { - if fds.fd_count < FD_SETSIZE { - fds.fd_array[i] = fd - fds.fd_count++ - } - } -} - -// Clear remove the fd from the set -func (fds *FDSet) Clear(fd uintptr) { - var i uint - for i = 0; i < fds.fd_count; i++ { - if fds.fd_array[i] == fd { - for i < fds.fd_count-1 { - fds.fd_array[i] = fds.fd_array[i+1] - i++ - } - fds.fd_count-- - break - } - } -} - -// IsSet check if the given fd is set -func (fds *FDSet) IsSet(fd uintptr) bool { - if isset, err := __WSAFDIsSet(syscall.Handle(fd), fds); err == nil && isset != 0 { - return true - } - return false -} - -// Zero empties the Set -func (fds *FDSet) Zero() { - fds.fd_count = 0 -} diff --git a/vendor/github.com/creack/goselect/select.go b/vendor/github.com/creack/goselect/select.go deleted file mode 100644 index 7f2875e73..000000000 --- a/vendor/github.com/creack/goselect/select.go +++ /dev/null @@ -1,28 +0,0 @@ -package goselect - -import ( - "syscall" - "time" -) - -// Select wraps syscall.Select with Go types -func Select(n int, r, w, e *FDSet, timeout time.Duration) error { - var timeval *syscall.Timeval - if timeout >= 0 { - t := syscall.NsecToTimeval(timeout.Nanoseconds()) - timeval = &t - } - - return sysSelect(n, r, w, e, timeval) -} - -// RetrySelect wraps syscall.Select with Go types, and retries a number of times, with a given retryDelay. -func RetrySelect(n int, r, w, e *FDSet, timeout time.Duration, retries int, retryDelay time.Duration) (err error) { - for i := 0; i < retries; i++ { - if err = Select(n, r, w, e, timeout); err != syscall.EINTR { - return err - } - time.Sleep(retryDelay) - } - return err -} diff --git a/vendor/github.com/creack/goselect/select_linux.go b/vendor/github.com/creack/goselect/select_linux.go deleted file mode 100644 index acd569e9d..000000000 --- a/vendor/github.com/creack/goselect/select_linux.go +++ /dev/null @@ -1,10 +0,0 @@ -// +build linux - -package goselect - -import "syscall" - -func sysSelect(n int, r, w, e *FDSet, timeout *syscall.Timeval) error { - _, err := syscall.Select(n, (*syscall.FdSet)(r), (*syscall.FdSet)(w), (*syscall.FdSet)(e), timeout) - return err -} diff --git a/vendor/github.com/creack/goselect/select_other.go b/vendor/github.com/creack/goselect/select_other.go deleted file mode 100644 index 6c8208147..000000000 --- a/vendor/github.com/creack/goselect/select_other.go +++ /dev/null @@ -1,9 +0,0 @@ -// +build !linux,!windows,!plan9,!solaris - -package goselect - -import "syscall" - -func sysSelect(n int, r, w, e *FDSet, timeout *syscall.Timeval) error { - return syscall.Select(n, (*syscall.FdSet)(r), (*syscall.FdSet)(w), (*syscall.FdSet)(e), timeout) -} diff --git a/vendor/github.com/creack/goselect/select_unsupported.go b/vendor/github.com/creack/goselect/select_unsupported.go deleted file mode 100644 index bea0ad94a..000000000 --- a/vendor/github.com/creack/goselect/select_unsupported.go +++ /dev/null @@ -1,16 +0,0 @@ -// +build plan9 solaris - -package goselect - -import ( - "fmt" - "runtime" - "syscall" -) - -// ErrUnsupported . -var ErrUnsupported = fmt.Errorf("Platofrm %s/%s unsupported", runtime.GOOS, runtime.GOARCH) - -func sysSelect(n int, r, w, e *FDSet, timeout *syscall.Timeval) error { - return ErrUnsupported -} diff --git a/vendor/github.com/creack/goselect/select_windows.go b/vendor/github.com/creack/goselect/select_windows.go deleted file mode 100644 index 0fb70d5d2..000000000 --- a/vendor/github.com/creack/goselect/select_windows.go +++ /dev/null @@ -1,13 +0,0 @@ -// +build windows - -package goselect - -import "syscall" - -//sys _select(nfds int, readfds *FDSet, writefds *FDSet, exceptfds *FDSet, timeout *syscall.Timeval) (total int, err error) = ws2_32.select -//sys __WSAFDIsSet(handle syscall.Handle, fdset *FDSet) (isset int, err error) = ws2_32.__WSAFDIsSet - -func sysSelect(n int, r, w, e *FDSet, timeout *syscall.Timeval) error { - _, err := _select(n, r, w, e, timeout) - return err -} diff --git a/vendor/github.com/creack/goselect/test_crosscompile.sh b/vendor/github.com/creack/goselect/test_crosscompile.sh deleted file mode 100644 index f8e1d292f..000000000 --- a/vendor/github.com/creack/goselect/test_crosscompile.sh +++ /dev/null @@ -1,112 +0,0 @@ -rm -rf crosstest -mkdir -p crosstest - -export GOOS=linux; export GOARCH=arm; echo "$(go build -o crosstest/${GOOS}_${GOARCH} && echo '[OK]' || echo '[KO]') $GOOS/$GOARCH"; -export GOOS=linux; export GOARCH=arm64; echo "$(go build -o crosstest/${GOOS}_${GOARCH} && echo '[OK]' || echo '[KO]') $GOOS/$GOARCH"; -export GOOS=linux; export GOARCH=amd64; echo "$(go build -o crosstest/${GOOS}_${GOARCH} && echo '[OK]' || echo '[KO]') $GOOS/$GOARCH"; -export GOOS=linux; export GOARCH=386; echo "$(go build -o crosstest/${GOOS}_${GOARCH} && echo '[OK]' || echo '[KO]') $GOOS/$GOARCH"; -export GOOS=linux; export GOARCH=mips; echo "$(go build -o crosstest/${GOOS}_${GOARCH} && echo '[OK]' || echo '[KO]') $GOOS/$GOARCH"; -export GOOS=linux; export GOARCH=mipsle; echo "$(go build -o crosstest/${GOOS}_${GOARCH} && echo '[OK]' || echo '[KO]') $GOOS/$GOARCH"; -export GOOS=linux; export GOARCH=mips64; echo "$(go build -o crosstest/${GOOS}_${GOARCH} && echo '[OK]' || echo '[KO]') $GOOS/$GOARCH"; -export GOOS=linux; export GOARCH=mips64le; echo "$(go build -o crosstest/${GOOS}_${GOARCH} && echo '[OK]' || echo '[KO]') $GOOS/$GOARCH"; -export GOOS=linux; export GOARCH=ppc64le; echo "$(go build -o crosstest/${GOOS}_${GOARCH} && echo '[OK]' || echo '[KO]') $GOOS/$GOARCH"; -export GOOS=linux; export GOARCH=s390x; echo "$(go build -o crosstest/${GOOS}_${GOARCH} && echo '[OK]' || echo '[KO]') $GOOS/$GOARCH"; - -export GOOS=darwin; export GOARCH=arm; echo "$(go build -o crosstest/${GOOS}_${GOARCH} && echo '[OK]' || echo '[KO]') $GOOS/$GOARCH"; -export GOOS=darwin; export GOARCH=arm64; echo "$(go build -o crosstest/${GOOS}_${GOARCH} && echo '[OK]' || echo '[KO]') $GOOS/$GOARCH"; -export GOOS=darwin; export GOARCH=amd64; echo "$(go build -o crosstest/${GOOS}_${GOARCH} && echo '[OK]' || echo '[KO]') $GOOS/$GOARCH"; -export GOOS=darwin; export GOARCH=386; echo "$(go build -o crosstest/${GOOS}_${GOARCH} && echo '[OK]' || echo '[KO]') $GOOS/$GOARCH"; -export GOOS=darwin; export GOARCH=mips; echo "$(go build -o crosstest/${GOOS}_${GOARCH} && echo '[OK]' || echo '[KO]') $GOOS/$GOARCH"; -export GOOS=darwin; export GOARCH=mipsle; echo "$(go build -o crosstest/${GOOS}_${GOARCH} && echo '[OK]' || echo '[KO]') $GOOS/$GOARCH"; -export GOOS=darwin; export GOARCH=mips64; echo "$(go build -o crosstest/${GOOS}_${GOARCH} && echo '[OK]' || echo '[KO]') $GOOS/$GOARCH"; -export GOOS=darwin; export GOARCH=mips64le; echo "$(go build -o crosstest/${GOOS}_${GOARCH} && echo '[OK]' || echo '[KO]') $GOOS/$GOARCH"; -export GOOS=darwin; export GOARCH=ppc64le; echo "$(go build -o crosstest/${GOOS}_${GOARCH} && echo '[OK]' || echo '[KO]') $GOOS/$GOARCH"; -export GOOS=darwin; export GOARCH=s390x; echo "$(go build -o crosstest/${GOOS}_${GOARCH} && echo '[OK]' || echo '[KO]') $GOOS/$GOARCH"; - -export GOOS=freebsd; export GOARCH=arm; echo "$(go build -o crosstest/${GOOS}_${GOARCH} && echo '[OK]' || echo '[KO]') $GOOS/$GOARCH"; -export GOOS=freebsd; export GOARCH=arm64; echo "$(go build -o crosstest/${GOOS}_${GOARCH} && echo '[OK]' || echo '[KO]') $GOOS/$GOARCH"; -export GOOS=freebsd; export GOARCH=amd64; echo "$(go build -o crosstest/${GOOS}_${GOARCH} && echo '[OK]' || echo '[KO]') $GOOS/$GOARCH"; -export GOOS=freebsd; export GOARCH=386; echo "$(go build -o crosstest/${GOOS}_${GOARCH} && echo '[OK]' || echo '[KO]') $GOOS/$GOARCH"; -export GOOS=freebsd; export GOARCH=mips; echo "$(go build -o crosstest/${GOOS}_${GOARCH} && echo '[OK]' || echo '[KO]') $GOOS/$GOARCH"; -export GOOS=freebsd; export GOARCH=mipsle; echo "$(go build -o crosstest/${GOOS}_${GOARCH} && echo '[OK]' || echo '[KO]') $GOOS/$GOARCH"; -export GOOS=freebsd; export GOARCH=mips64; echo "$(go build -o crosstest/${GOOS}_${GOARCH} && echo '[OK]' || echo '[KO]') $GOOS/$GOARCH"; -export GOOS=freebsd; export GOARCH=mips64le; echo "$(go build -o crosstest/${GOOS}_${GOARCH} && echo '[OK]' || echo '[KO]') $GOOS/$GOARCH"; -export GOOS=freebsd; export GOARCH=ppc64le; echo "$(go build -o crosstest/${GOOS}_${GOARCH} && echo '[OK]' || echo '[KO]') $GOOS/$GOARCH"; -export GOOS=freebsd; export GOARCH=s390x; echo "$(go build -o crosstest/${GOOS}_${GOARCH} && echo '[OK]' || echo '[KO]') $GOOS/$GOARCH"; - -export GOOS=openbsd; export GOARCH=arm; echo "$(go build -o crosstest/${GOOS}_${GOARCH} && echo '[OK]' || echo '[KO]') $GOOS/$GOARCH"; -export GOOS=openbsd; export GOARCH=arm64; echo "$(go build -o crosstest/${GOOS}_${GOARCH} && echo '[OK]' || echo '[KO]') $GOOS/$GOARCH"; -export GOOS=openbsd; export GOARCH=amd64; echo "$(go build -o crosstest/${GOOS}_${GOARCH} && echo '[OK]' || echo '[KO]') $GOOS/$GOARCH"; -export GOOS=openbsd; export GOARCH=386; echo "$(go build -o crosstest/${GOOS}_${GOARCH} && echo '[OK]' || echo '[KO]') $GOOS/$GOARCH"; -export GOOS=openbsd; export GOARCH=mips; echo "$(go build -o crosstest/${GOOS}_${GOARCH} && echo '[OK]' || echo '[KO]') $GOOS/$GOARCH"; -export GOOS=openbsd; export GOARCH=mipsle; echo "$(go build -o crosstest/${GOOS}_${GOARCH} && echo '[OK]' || echo '[KO]') $GOOS/$GOARCH"; -export GOOS=openbsd; export GOARCH=mips64; echo "$(go build -o crosstest/${GOOS}_${GOARCH} && echo '[OK]' || echo '[KO]') $GOOS/$GOARCH"; -export GOOS=openbsd; export GOARCH=mips64le; echo "$(go build -o crosstest/${GOOS}_${GOARCH} && echo '[OK]' || echo '[KO]') $GOOS/$GOARCH"; -export GOOS=openbsd; export GOARCH=ppc64le; echo "$(go build -o crosstest/${GOOS}_${GOARCH} && echo '[OK]' || echo '[KO]') $GOOS/$GOARCH"; -export GOOS=openbsd; export GOARCH=s390x; echo "$(go build -o crosstest/${GOOS}_${GOARCH} && echo '[OK]' || echo '[KO]') $GOOS/$GOARCH"; - -export GOOS=netbsd; export GOARCH=arm; echo "$(go build -o crosstest/${GOOS}_${GOARCH} && echo '[OK]' || echo '[KO]') $GOOS/$GOARCH"; -export GOOS=netbsd; export GOARCH=arm64; echo "$(go build -o crosstest/${GOOS}_${GOARCH} && echo '[OK]' || echo '[KO]') $GOOS/$GOARCH"; -export GOOS=netbsd; export GOARCH=amd64; echo "$(go build -o crosstest/${GOOS}_${GOARCH} && echo '[OK]' || echo '[KO]') $GOOS/$GOARCH"; -export GOOS=netbsd; export GOARCH=386; echo "$(go build -o crosstest/${GOOS}_${GOARCH} && echo '[OK]' || echo '[KO]') $GOOS/$GOARCH"; -export GOOS=netbsd; export GOARCH=mips; echo "$(go build -o crosstest/${GOOS}_${GOARCH} && echo '[OK]' || echo '[KO]') $GOOS/$GOARCH"; -export GOOS=netbsd; export GOARCH=mipsle; echo "$(go build -o crosstest/${GOOS}_${GOARCH} && echo '[OK]' || echo '[KO]') $GOOS/$GOARCH"; -export GOOS=netbsd; export GOARCH=mips64; echo "$(go build -o crosstest/${GOOS}_${GOARCH} && echo '[OK]' || echo '[KO]') $GOOS/$GOARCH"; -export GOOS=netbsd; export GOARCH=mips64le; echo "$(go build -o crosstest/${GOOS}_${GOARCH} && echo '[OK]' || echo '[KO]') $GOOS/$GOARCH"; -export GOOS=netbsd; export GOARCH=ppc64le; echo "$(go build -o crosstest/${GOOS}_${GOARCH} && echo '[OK]' || echo '[KO]') $GOOS/$GOARCH"; -export GOOS=netbsd; export GOARCH=s390x; echo "$(go build -o crosstest/${GOOS}_${GOARCH} && echo '[OK]' || echo '[KO]') $GOOS/$GOARCH"; - -export GOOS=dragonfly; export GOARCH=arm; echo "$(go build -o crosstest/${GOOS}_${GOARCH} && echo '[OK]' || echo '[KO]') $GOOS/$GOARCH"; -export GOOS=dragonfly; export GOARCH=arm64; echo "$(go build -o crosstest/${GOOS}_${GOARCH} && echo '[OK]' || echo '[KO]') $GOOS/$GOARCH"; -export GOOS=dragonfly; export GOARCH=amd64; echo "$(go build -o crosstest/${GOOS}_${GOARCH} && echo '[OK]' || echo '[KO]') $GOOS/$GOARCH"; -export GOOS=dragonfly; export GOARCH=386; echo "$(go build -o crosstest/${GOOS}_${GOARCH} && echo '[OK]' || echo '[KO]') $GOOS/$GOARCH"; -export GOOS=dragonfly; export GOARCH=mips; echo "$(go build -o crosstest/${GOOS}_${GOARCH} && echo '[OK]' || echo '[KO]') $GOOS/$GOARCH"; -export GOOS=dragonfly; export GOARCH=mipsle; echo "$(go build -o crosstest/${GOOS}_${GOARCH} && echo '[OK]' || echo '[KO]') $GOOS/$GOARCH"; -export GOOS=dragonfly; export GOARCH=mips64; echo "$(go build -o crosstest/${GOOS}_${GOARCH} && echo '[OK]' || echo '[KO]') $GOOS/$GOARCH"; -export GOOS=dragonfly; export GOARCH=mips64le; echo "$(go build -o crosstest/${GOOS}_${GOARCH} && echo '[OK]' || echo '[KO]') $GOOS/$GOARCH"; -export GOOS=dragonfly; export GOARCH=ppc64le; echo "$(go build -o crosstest/${GOOS}_${GOARCH} && echo '[OK]' || echo '[KO]') $GOOS/$GOARCH"; -export GOOS=dragonfly; export GOARCH=s390x; echo "$(go build -o crosstest/${GOOS}_${GOARCH} && echo '[OK]' || echo '[KO]') $GOOS/$GOARCH"; - -export GOOS=solaris; export GOARCH=arm; echo "$(go build -o crosstest/${GOOS}_${GOARCH} && echo '[OK]' || echo '[KO]') $GOOS/$GOARCH"; -export GOOS=solaris; export GOARCH=arm64; echo "$(go build -o crosstest/${GOOS}_${GOARCH} && echo '[OK]' || echo '[KO]') $GOOS/$GOARCH"; -export GOOS=solaris; export GOARCH=amd64; echo "$(go build -o crosstest/${GOOS}_${GOARCH} && echo '[OK]' || echo '[KO]') $GOOS/$GOARCH"; -export GOOS=solaris; export GOARCH=386; echo "$(go build -o crosstest/${GOOS}_${GOARCH} && echo '[OK]' || echo '[KO]') $GOOS/$GOARCH"; -export GOOS=solaris; export GOARCH=mips; echo "$(go build -o crosstest/${GOOS}_${GOARCH} && echo '[OK]' || echo '[KO]') $GOOS/$GOARCH"; -export GOOS=solaris; export GOARCH=mipsle; echo "$(go build -o crosstest/${GOOS}_${GOARCH} && echo '[OK]' || echo '[KO]') $GOOS/$GOARCH"; -export GOOS=solaris; export GOARCH=mips64; echo "$(go build -o crosstest/${GOOS}_${GOARCH} && echo '[OK]' || echo '[KO]') $GOOS/$GOARCH"; -export GOOS=solaris; export GOARCH=mips64le; echo "$(go build -o crosstest/${GOOS}_${GOARCH} && echo '[OK]' || echo '[KO]') $GOOS/$GOARCH"; -export GOOS=solaris; export GOARCH=ppc64le; echo "$(go build -o crosstest/${GOOS}_${GOARCH} && echo '[OK]' || echo '[KO]') $GOOS/$GOARCH"; -export GOOS=solaris; export GOARCH=s390x; echo "$(go build -o crosstest/${GOOS}_${GOARCH} && echo '[OK]' || echo '[KO]') $GOOS/$GOARCH"; - -export GOOS=plan9; export GOARCH=arm; echo "$(go build -o crosstest/${GOOS}_${GOARCH} && echo '[OK]' || echo '[KO]') $GOOS/$GOARCH"; -export GOOS=plan9; export GOARCH=arm64; echo "$(go build -o crosstest/${GOOS}_${GOARCH} && echo '[OK]' || echo '[KO]') $GOOS/$GOARCH"; -export GOOS=plan9; export GOARCH=amd64; echo "$(go build -o crosstest/${GOOS}_${GOARCH} && echo '[OK]' || echo '[KO]') $GOOS/$GOARCH"; -export GOOS=plan9; export GOARCH=386; echo "$(go build -o crosstest/${GOOS}_${GOARCH} && echo '[OK]' || echo '[KO]') $GOOS/$GOARCH"; -export GOOS=plan9; export GOARCH=mips; echo "$(go build -o crosstest/${GOOS}_${GOARCH} && echo '[OK]' || echo '[KO]') $GOOS/$GOARCH"; -export GOOS=plan9; export GOARCH=mipsle; echo "$(go build -o crosstest/${GOOS}_${GOARCH} && echo '[OK]' || echo '[KO]') $GOOS/$GOARCH"; -export GOOS=plan9; export GOARCH=mips64; echo "$(go build -o crosstest/${GOOS}_${GOARCH} && echo '[OK]' || echo '[KO]') $GOOS/$GOARCH"; -export GOOS=plan9; export GOARCH=mips64le; echo "$(go build -o crosstest/${GOOS}_${GOARCH} && echo '[OK]' || echo '[KO]') $GOOS/$GOARCH"; -export GOOS=plan9; export GOARCH=ppc64le; echo "$(go build -o crosstest/${GOOS}_${GOARCH} && echo '[OK]' || echo '[KO]') $GOOS/$GOARCH"; -export GOOS=plan9; export GOARCH=s390x; echo "$(go build -o crosstest/${GOOS}_${GOARCH} && echo '[OK]' || echo '[KO]') $GOOS/$GOARCH"; - -export GOOS=windows; export GOARCH=arm; echo "$(go build -o crosstest/${GOOS}_${GOARCH} && echo '[OK]' || echo '[KO]') $GOOS/$GOARCH"; -export GOOS=windows; export GOARCH=arm64; echo "$(go build -o crosstest/${GOOS}_${GOARCH} && echo '[OK]' || echo '[KO]') $GOOS/$GOARCH"; -export GOOS=windows; export GOARCH=amd64; echo "$(go build -o crosstest/${GOOS}_${GOARCH} && echo '[OK]' || echo '[KO]') $GOOS/$GOARCH"; -export GOOS=windows; export GOARCH=386; echo "$(go build -o crosstest/${GOOS}_${GOARCH} && echo '[OK]' || echo '[KO]') $GOOS/$GOARCH"; -export GOOS=windows; export GOARCH=mips; echo "$(go build -o crosstest/${GOOS}_${GOARCH} && echo '[OK]' || echo '[KO]') $GOOS/$GOARCH"; -export GOOS=windows; export GOARCH=mipsle; echo "$(go build -o crosstest/${GOOS}_${GOARCH} && echo '[OK]' || echo '[KO]') $GOOS/$GOARCH"; -export GOOS=windows; export GOARCH=mips64; echo "$(go build -o crosstest/${GOOS}_${GOARCH} && echo '[OK]' || echo '[KO]') $GOOS/$GOARCH"; -export GOOS=windows; export GOARCH=mips64le; echo "$(go build -o crosstest/${GOOS}_${GOARCH} && echo '[OK]' || echo '[KO]') $GOOS/$GOARCH"; -export GOOS=windows; export GOARCH=ppc64le; echo "$(go build -o crosstest/${GOOS}_${GOARCH} && echo '[OK]' || echo '[KO]') $GOOS/$GOARCH"; -export GOOS=windows; export GOARCH=s390x; echo "$(go build -o crosstest/${GOOS}_${GOARCH} && echo '[OK]' || echo '[KO]') $GOOS/$GOARCH"; - -export GOOS=android; export GOARCH=arm; echo "$(go build -o crosstest/${GOOS}_${GOARCH} && echo '[OK]' || echo '[KO]') $GOOS/$GOARCH"; -export GOOS=android; export GOARCH=arm64; echo "$(go build -o crosstest/${GOOS}_${GOARCH} && echo '[OK]' || echo '[KO]') $GOOS/$GOARCH"; -export GOOS=android; export GOARCH=amd64; echo "$(go build -o crosstest/${GOOS}_${GOARCH} && echo '[OK]' || echo '[KO]') $GOOS/$GOARCH"; -export GOOS=android; export GOARCH=386; echo "$(go build -o crosstest/${GOOS}_${GOARCH} && echo '[OK]' || echo '[KO]') $GOOS/$GOARCH"; -export GOOS=android; export GOARCH=mips; echo "$(go build -o crosstest/${GOOS}_${GOARCH} && echo '[OK]' || echo '[KO]') $GOOS/$GOARCH"; -export GOOS=android; export GOARCH=mipsle; echo "$(go build -o crosstest/${GOOS}_${GOARCH} && echo '[OK]' || echo '[KO]') $GOOS/$GOARCH"; -export GOOS=android; export GOARCH=mips64; echo "$(go build -o crosstest/${GOOS}_${GOARCH} && echo '[OK]' || echo '[KO]') $GOOS/$GOARCH"; -export GOOS=android; export GOARCH=mips64le; echo "$(go build -o crosstest/${GOOS}_${GOARCH} && echo '[OK]' || echo '[KO]') $GOOS/$GOARCH"; -export GOOS=android; export GOARCH=ppc64le; echo "$(go build -o crosstest/${GOOS}_${GOARCH} && echo '[OK]' || echo '[KO]') $GOOS/$GOARCH"; -export GOOS=android; export GOARCH=s390x; echo "$(go build -o crosstest/${GOOS}_${GOARCH} && echo '[OK]' || echo '[KO]') $GOOS/$GOARCH"; diff --git a/vendor/github.com/creack/goselect/zselect_windows.go b/vendor/github.com/creack/goselect/zselect_windows.go deleted file mode 100644 index e01502c07..000000000 --- a/vendor/github.com/creack/goselect/zselect_windows.go +++ /dev/null @@ -1,39 +0,0 @@ -// MACHINE GENERATED BY 'go generate' COMMAND; DO NOT EDIT - -package goselect - -import "unsafe" -import "syscall" - -var _ unsafe.Pointer - -var ( - modws2_32 = syscall.NewLazyDLL("ws2_32.dll") - - procselect = modws2_32.NewProc("select") - proc__WSAFDIsSet = modws2_32.NewProc("__WSAFDIsSet") -) - -func _select(nfds int, readfds *FDSet, writefds *FDSet, exceptfds *FDSet, timeout *syscall.Timeval) (total int, err error) { - r0, _, e1 := syscall.Syscall6(procselect.Addr(), 5, uintptr(nfds), uintptr(unsafe.Pointer(readfds)), uintptr(unsafe.Pointer(writefds)), uintptr(unsafe.Pointer(exceptfds)), uintptr(unsafe.Pointer(timeout)), 0) - total = int(r0) - if total == 0 { - if e1 != 0 { - err = error(e1) - } - } - return -} - -func __WSAFDIsSet(handle syscall.Handle, fdset *FDSet) (isset int, err error) { - r0, _, e1 := syscall.Syscall(proc__WSAFDIsSet.Addr(), 2, uintptr(handle), uintptr(unsafe.Pointer(fdset)), 0) - isset = int(r0) - if isset == 0 { - if e1 != 0 { - err = error(e1) - } else { - err = syscall.EINVAL - } - } - return -} diff --git a/vendor/github.com/docker/docker/AUTHORS b/vendor/github.com/docker/docker/AUTHORS deleted file mode 100644 index c5dafd722..000000000 --- a/vendor/github.com/docker/docker/AUTHORS +++ /dev/null @@ -1,1952 +0,0 @@ -# This file lists all individuals having contributed content to the repository. -# For how it is generated, see `hack/generate-authors.sh`. - -Aanand Prasad -Aaron Davidson -Aaron Feng -Aaron Huslage -Aaron L. Xu -Aaron Lehmann -Aaron Welch -Aaron.L.Xu -Abel Muiño -Abhijeet Kasurde -Abhinandan Prativadi -Abhinav Ajgaonkar -Abhishek Chanda -Abhishek Sharma -Abin Shahab -Adam Avilla -Adam Eijdenberg -Adam Kunk -Adam Miller -Adam Mills -Adam Pointer -Adam Singer -Adam Walz -Addam Hardy -Aditi Rajagopal -Aditya -Adnan Khan -Adolfo Ochagavía -Adria Casas -Adrian Moisey -Adrian Mouat -Adrian Oprea -Adrien Folie -Adrien Gallouët -Ahmed Kamal -Ahmet Alp Balkan -Aidan Feldman -Aidan Hobson Sayers -AJ Bowen -Ajey Charantimath -ajneu -Akash Gupta -Akihiro Matsushima -Akihiro Suda -Akim Demaille -Akira Koyasu -Akshay Karle -Al Tobey -alambike -Alan Scherger -Alan Thompson -Albert Callarisa -Albert Zhang -Aleksa Sarai -Aleksandrs Fadins -Alena Prokharchyk -Alessandro Boch -Alessio Biancalana -Alex Chan -Alex Chen -Alex Coventry -Alex Crawford -Alex Ellis -Alex Gaynor -Alex Olshansky -Alex Samorukov -Alex Warhawk -Alexander Artemenko -Alexander Boyd -Alexander Larsson -Alexander Midlash -Alexander Morozov -Alexander Shopov -Alexandre Beslic -Alexandre Garnier -Alexandre González -Alexandru Sfirlogea -Alexey Guskov -Alexey Kotlyarov -Alexey Shamrin -Alexis THOMAS -Alfred Landrum -Ali Dehghani -Alicia Lauerman -Alihan Demir -Allen Madsen -Allen Sun -almoehi -Alvaro Saurin -Alvin Deng -Alvin Richards -amangoel -Amen Belayneh -Amir Goldstein -Amit Bakshi -Amit Krishnan -Amit Shukla -Amy Lindburg -Anand Patil -AnandkumarPatel -Anatoly Borodin -Anchal Agrawal -Anders Janmyr -Andre Dublin <81dublin@gmail.com> -Andre Granovsky -Andrea Luzzardi -Andrea Turli -Andreas Elvers -Andreas Köhler -Andreas Savvides -Andreas Tiefenthaler -Andrei Gherzan -Andrew C. Bodine -Andrew Clay Shafer -Andrew Duckworth -Andrew France -Andrew Gerrand -Andrew Guenther -Andrew He -Andrew Hsu -Andrew Kuklewicz -Andrew Macgregor -Andrew Macpherson -Andrew Martin -Andrew McDonnell -Andrew Munsell -Andrew Pennebaker -Andrew Po -Andrew Weiss -Andrew Williams -Andrews Medina -Andrey Petrov -Andrey Stolbovsky -André Martins -andy -Andy Chambers -andy diller -Andy Goldstein -Andy Kipp -Andy Rothfusz -Andy Smith -Andy Wilson -Anes Hasicic -Anil Belur -Anil Madhavapeddy -Ankush Agarwal -Anonmily -Anran Qiao -Anshul Pundir -Anthon van der Neut -Anthony Baire -Anthony Bishopric -Anthony Dahanne -Anthony Sottile -Anton Löfgren -Anton Nikitin -Anton Polonskiy -Anton Tiurin -Antonio Murdaca -Antonis Kalipetis -Antony Messerli -Anuj Bahuguna -Anusha Ragunathan -apocas -Arash Deshmeh -ArikaChen -Arnaud Lefebvre -Arnaud Porterie -Arthur Barr -Arthur Gautier -Artur Meyster -Arun Gupta -Asad Saeeduddin -Asbjørn Enge -averagehuman -Avi Das -Avi Miller -Avi Vaid -ayoshitake -Azat Khuyiyakhmetov -Bardia Keyoumarsi -Barnaby Gray -Barry Allard -Bartłomiej Piotrowski -Bastiaan Bakker -bdevloed -Ben Bonnefoy -Ben Firshman -Ben Golub -Ben Hall -Ben Sargent -Ben Severson -Ben Toews -Ben Wiklund -Benjamin Atkin -Benjamin Boudreau -Benoit Chesneau -Bernerd Schaefer -Bernhard M. Wiedemann -Bert Goethals -Bharath Thiruveedula -Bhiraj Butala -Bhumika Bayani -Bilal Amarni -Bill Wang -Bin Liu -Bingshen Wang -Blake Geno -Boaz Shuster -bobby abbott -Boris Pruessmann -Boshi Lian -Bouke Haarsma -Boyd Hemphill -boynux -Bradley Cicenas -Bradley Wright -Brandon Liu -Brandon Philips -Brandon Rhodes -Brendan Dixon -Brent Salisbury -Brett Higgins -Brett Kochendorfer -Brett Randall -Brian (bex) Exelbierd -Brian Bland -Brian DeHamer -Brian Dorsey -Brian Flad -Brian Goff -Brian McCallister -Brian Olsen -Brian Schwind -Brian Shumate -Brian Torres-Gil -Brian Trump -Brice Jaglin -Briehan Lombaard -Bruno Bigras -Bruno Binet -Bruno Gazzera -Bruno Renié -Bruno Tavares -Bryan Bess -Bryan Boreham -Bryan Matsuo -Bryan Murphy -Burke Libbey -Byung Kang -Caleb Spare -Calen Pennington -Cameron Boehmer -Cameron Spear -Campbell Allen -Candid Dauth -Cao Weiwei -Carl Henrik Lunde -Carl Loa Odin -Carl X. Su -Carlo Mion -Carlos Alexandro Becker -Carlos Sanchez -Carol Fager-Higgins -Cary -Casey Bisson -Ce Gao -Cedric Davies -Cezar Sa Espinola -Chad Swenson -Chance Zibolski -Chander Govindarajan -Chanhun Jeong -Chao Wang -Charles Chan -Charles Hooper -Charles Law -Charles Lindsay -Charles Merriam -Charles Sarrazin -Charles Smith -Charlie Drage -Charlie Lewis -Chase Bolt -ChaYoung You -Chen Chao -Chen Chuanliang -Chen Hanxiao -Chen Min -Chen Mingjie -Chen Qiu -Cheng-mean Liu -Chetan Birajdar -Chewey -Chia-liang Kao -chli -Cholerae Hu -Chris Alfonso -Chris Armstrong -Chris Dias -Chris Dituri -Chris Fordham -Chris Gavin -Chris Gibson -Chris Khoo -Chris McKinnel -Chris McKinnel -Chris Seto -Chris Snow -Chris St. Pierre -Chris Stivers -Chris Swan -Chris Wahl -Chris Weyl -Christian Berendt -Christian Brauner -Christian Böhme -Christian Persson -Christian Rotzoll -Christian Simon -Christian Stefanescu -Christophe Mehay -Christophe Troestler -Christophe Vidal -Christopher Biscardi -Christopher Crone -Christopher Currie -Christopher Jones -Christopher Latham -Christopher Rigor -Christy Perez -Chun Chen -Ciro S. Costa -Clayton Coleman -Clinton Kitson -Coenraad Loubser -Colin Dunklau -Colin Hebert -Colin Rice -Colin Walters -Collin Guarino -Colm Hally -companycy -Corbin Coleman -Corey Farrell -Cory Forsyth -cressie176 -CrimsonGlory -Cristian Staretu -cristiano balducci -Cruceru Calin-Cristian -CUI Wei -Cyprian Gracz -Cyril F -Daan van Berkel -Daehyeok Mun -Dafydd Crosby -dalanlan -Damian Smyth -Damien Nadé -Damien Nozay -Damjan Georgievski -Dan Anolik -Dan Buch -Dan Cotora -Dan Feldman -Dan Griffin -Dan Hirsch -Dan Keder -Dan Levy -Dan McPherson -Dan Stine -Dan Williams -Dani Louca -Daniel Antlinger -Daniel Dao -Daniel Exner -Daniel Farrell -Daniel Garcia -Daniel Gasienica -Daniel Grunwell -Daniel Hiltgen -Daniel J Walsh -Daniel Menet -Daniel Mizyrycki -Daniel Nephin -Daniel Norberg -Daniel Nordberg -Daniel Robinson -Daniel S -Daniel Von Fange -Daniel Watkins -Daniel X Moore -Daniel YC Lin -Daniel Zhang -Danny Berger -Danny Yates -Danyal Khaliq -Darren Coxall -Darren Shepherd -Darren Stahl -Dattatraya Kumbhar -Davanum Srinivas -Dave Barboza -Dave Goodchild -Dave Henderson -Dave MacDonald -Dave Tucker -David Anderson -David Calavera -David Corking -David Cramer -David Currie -David Davis -David Dooling -David Gageot -David Gebler -David Glasser -David Lawrence -David Lechner -David M. Karr -David Mackey -David Mat -David Mcanulty -David McKay -David Pelaez -David R. Jenni -David Röthlisberger -David Sheets -David Sissitka -David Trott -David Williamson -David Xia -David Young -Davide Ceretti -Dawn Chen -dbdd -dcylabs -Deborah Gertrude Digges -deed02392 -Deng Guangxing -Deni Bertovic -Denis Defreyne -Denis Gladkikh -Denis Ollier -Dennis Chen -Dennis Chen -Dennis Docter -Derek -Derek -Derek Ch -Derek McGowan -Deric Crago -Deshi Xiao -devmeyster -Devvyn Murphy -Dharmit Shah -Dhawal Yogesh Bhanushali -Diego Romero -Diego Siqueira -Dieter Reuter -Dillon Dixon -Dima Stopel -Dimitri John Ledkov -Dimitris Rozakis -Dimitry Andric -Dinesh Subhraveti -Ding Fei -Diogo Monica -DiuDiugirl -Djibril Koné -dkumor -Dmitri Logvinenko -Dmitri Shuralyov -Dmitry Demeshchuk -Dmitry Gusev -Dmitry Kononenko -Dmitry Shyshkin -Dmitry Smirnov -Dmitry V. Krivenok -Dmitry Vorobev -Dolph Mathews -Dominik Dingel -Dominik Finkbeiner -Dominik Honnef -Don Kirkby -Don Kjer -Don Spaulding -Donald Huang -Dong Chen -Donovan Jones -Doron Podoleanu -Doug Davis -Doug MacEachern -Doug Tangren -Douglas Curtis -Dr Nic Williams -dragon788 -Dražen Lučanin -Drew Erny -Drew Hubl -Dustin Sallings -Ed Costello -Edmund Wagner -Eiichi Tsukata -Eike Herzbach -Eivin Giske Skaaren -Eivind Uggedal -Elan Ruusamäe -Elena Morozova -Eli Uriegas -Elias Faxö -Elias Probst -Elijah Zupancic -eluck -Elvir Kuric -Emil Davtyan -Emil Hernvall -Emily Maier -Emily Rose -Emir Ozer -Enguerran -Eohyung Lee -epeterso -Eric Barch -Eric Curtin -Eric G. Noriega -Eric Hanchrow -Eric Lee -Eric Myhre -Eric Paris -Eric Rafaloff -Eric Rosenberg -Eric Sage -Eric Soderstrom -Eric Yang -Eric-Olivier Lamey -Erica Windisch -Erik Bray -Erik Dubbelboer -Erik Hollensbe -Erik Inge Bolsø -Erik Kristensen -Erik St. Martin -Erik Weathers -Erno Hopearuoho -Erwin van der Koogh -Euan Kemp -Eugen Krizo -Eugene Yakubovich -Evan Allrich -Evan Carmi -Evan Hazlett -Evan Krall -Evan Phoenix -Evan Wies -Evelyn Xu -Everett Toews -Evgeny Shmarnev -Evgeny Vereshchagin -Ewa Czechowska -Eystein Måløy Stenberg -ezbercih -Ezra Silvera -Fabian Lauer -Fabiano Rosas -Fabio Falci -Fabio Kung -Fabio Rapposelli -Fabio Rehm -Fabrizio Regini -Fabrizio Soppelsa -Faiz Khan -falmp -Fangyuan Gao <21551127@zju.edu.cn> -Fareed Dudhia -Fathi Boudra -Federico Gimenez -Felipe Oliveira -Felix Abecassis -Felix Geisendörfer -Felix Hupfeld -Felix Rabe -Felix Ruess -Felix Schindler -Feng Yan -Fengtu Wang -Ferenc Szabo -Fernando -Fero Volar -Ferran Rodenas -Filipe Brandenburger -Filipe Oliveira -Flavio Castelli -Flavio Crisciani -Florian -Florian Klein -Florian Maier -Florian Noeding -Florian Weingarten -Florin Asavoaie -Florin Patan -fonglh -Foysal Iqbal -Francesc Campoy -Francis Chuang -Francisco Carriedo -Francisco Souza -Frank Groeneveld -Frank Herrmann -Frank Macreery -Frank Rosquin -Fred Lifton -Frederick F. Kautz IV -Frederik Loeffert -Frederik Nordahl Jul Sabroe -Freek Kalter -Frieder Bluemle -Félix Baylac-Jacqué -Félix Cantournet -Gabe Rosenhouse -Gabor Nagy -Gabriel Linder -Gabriel Monroy -Gabriel Nicolas Avellaneda -Gaetan de Villele -Galen Sampson -Gang Qiao -Gareth Rushgrove -Garrett Barboza -Gary Schaetz -Gaurav -gautam, prasanna -Gaël PORTAY -Genki Takiuchi -GennadySpb -Geoffrey Bachelet -George Kontridze -George MacRorie -George Xie -Georgi Hristozov -Gereon Frey -German DZ -Gert van Valkenhoef -Gerwim Feiken -Ghislain Bourgeois -Giampaolo Mancini -Gianluca Borello -Gildas Cuisinier -gissehel -Giuseppe Mazzotta -Gleb Fotengauer-Malinovskiy -Gleb M Borisov -Glyn Normington -GoBella -Goffert van Gool -Gopikannan Venugopalsamy -Gosuke Miyashita -Gou Rao -Govinda Fichtner -Grant Reaber -Graydon Hoare -Greg Fausak -Greg Pflaum -Greg Stephens -Greg Thornton -Grzegorz Jaśkiewicz -Guilhem Lettron -Guilherme Salgado -Guillaume Dufour -Guillaume J. Charmes -guoxiuyan -Gurjeet Singh -Guruprasad -Gustav Sinder -gwx296173 -Günter Zöchbauer -Hakan Özler -Hans Kristian Flaatten -Hans Rødtang -Hao Shu Wei -Hao Zhang <21521210@zju.edu.cn> -Harald Albers -Harley Laue -Harold Cooper -Harry Zhang -Harshal Patil -Harshal Patil -He Simei -He Xiaoxi -He Xin -heartlock <21521209@zju.edu.cn> -Hector Castro -Helen Xie -Henning Sprang -Hobofan -Hollie Teal -Hong Xu -Hongbin Lu -hsinko <21551195@zju.edu.cn> -Hu Keping -Hu Tao -Huanzhong Zhang -Huayi Zhang -Hugo Duncan -Hugo Marisco <0x6875676f@gmail.com> -Hunter Blanks -huqun -Huu Nguyen -hyeongkyu.lee -Hyzhou Zhy -Iago López Galeiras -Ian Babrou -Ian Bishop -Ian Bull -Ian Calvert -Ian Campbell -Ian Lee -Ian Main -Ian Philpot -Ian Truslove -Iavael -Icaro Seara -Ignacio Capurro -Igor Dolzhikov -Igor Karpovich -Iliana Weller -Ilkka Laukkanen -Ilya Dmitrichenko -Ilya Gusev -Ilya Khlopotov -imre Fitos -inglesp -Ingo Gottwald -Isaac Dupree -Isabel Jimenez -Isao Jonas -Ivan Babrou -Ivan Fraixedes -Ivan Grcic -Ivan Markin -J Bruni -J. Nunn -Jack Danger Canty -Jack Laxson -Jacob Atzen -Jacob Edelman -Jacob Tomlinson -Jacob Vallejo -Jacob Wen -Jaivish Kothari -Jake Champlin -Jake Moshenko -Jake Sanders -jakedt -James Allen -James Carey -James Carr -James DeFelice -James Harrison Fisher -James Kyburz -James Kyle -James Lal -James Mills -James Nesbitt -James Nugent -James Turnbull -Jamie Hannaford -Jamshid Afshar -Jan Keromnes -Jan Koprowski -Jan Pazdziora -Jan Toebes -Jan-Gerd Tenberge -Jan-Jaap Driessen -Jana Radhakrishnan -Jannick Fahlbusch -Januar Wayong -Jared Biel -Jared Hocutt -Jaroslaw Zabiello -jaseg -Jasmine Hegman -Jason Divock -Jason Giedymin -Jason Green -Jason Hall -Jason Heiss -Jason Livesay -Jason McVetta -Jason Plum -Jason Shepherd -Jason Smith -Jason Sommer -Jason Stangroome -jaxgeller -Jay -Jay -Jay Kamat -Jean-Baptiste Barth -Jean-Baptiste Dalido -Jean-Christophe Berthon -Jean-Paul Calderone -Jean-Pierre Huynh -Jean-Tiare Le Bigot -Jeeva S. Chelladhurai -Jeff Anderson -Jeff Johnston -Jeff Lindsay -Jeff Mickey -Jeff Minard -Jeff Nickoloff -Jeff Silberman -Jeff Welch -Jeffrey Bolle -Jeffrey Morgan -Jeffrey van Gogh -Jenny Gebske -Jeremy Chambers -Jeremy Grosser -Jeremy Price -Jeremy Qian -Jeremy Unruh -Jeremy Yallop -Jeroen Franse -Jeroen Jacobs -Jesse Dearing -Jesse Dubay -Jessica Frazelle -Jezeniel Zapanta -Jhon Honce -Ji.Zhilong -Jian Zhang -Jie Luo -Jihyun Hwang -Jilles Oldenbeuving -Jim Alateras -Jim Galasyn -Jim Minter -Jim Perrin -Jimmy Cuadra -Jimmy Puckett -Jimmy Song -jimmyxian -Jinsoo Park -Jiri Popelka -Jiuyue Ma -Jiří Župka -jjy -jmzwcn -Joao Fernandes -Joe Beda -Joe Doliner -Joe Ferguson -Joe Gordon -Joe Shaw -Joe Van Dyk -Joel Friedly -Joel Handwell -Joel Hansson -Joel Wurtz -Joey Geiger -Joey Geiger -Joey Gibson -Joffrey F -Johan Euphrosine -Johan Rydberg -Johanan Lieberman -Johannes 'fish' Ziemke -John Costa -John Feminella -John Gardiner Myers -John Gossman -John Harris -John Howard (VM) -John Laswell -John Maguire -John Mulhausen -John OBrien III -John Starks -John Stephens -John Tims -John V. Martinez -John Warwick -John Willis -Jon Johnson -Jon Surrell -Jon Wedaman -Jonas Pfenniger -Jonathan A. Sternberg -Jonathan Boulle -Jonathan Camp -Jonathan Dowland -Jonathan Lebon -Jonathan Lomas -Jonathan McCrohan -Jonathan Mueller -Jonathan Pares -Jonathan Rudenberg -Jonathan Stoppani -Jonh Wendell -Joni Sar -Joost Cassee -Jordan Arentsen -Jordan Jennings -Jordan Sissel -Jorge Marin -Jorit Kleine-Möllhoff -Jose Diaz-Gonzalez -Joseph Anthony Pasquale Holsten -Joseph Hager -Joseph Kern -Joseph Rothrock -Josh -Josh Bodah -Josh Bonczkowski -Josh Chorlton -Josh Eveleth -Josh Hawn -Josh Horwitz -Josh Poimboeuf -Josh Soref -Josh Wilson -Josiah Kiehl -José Tomás Albornoz -Joyce Jang -JP -Julian Taylor -Julien Barbier -Julien Bisconti -Julien Bordellier -Julien Dubois -Julien Kassar -Julien Maitrehenry -Julien Pervillé -Julio Montes -Jun-Ru Chang -Jussi Nummelin -Justas Brazauskas -Justin Cormack -Justin Force -Justin Menga -Justin Plock -Justin Simonelis -Justin Terry -Justyn Temme -Jyrki Puttonen -Jérôme Petazzoni -Jörg Thalheim -K. Heller -Kai Blin -Kai Qiang Wu (Kennan) -Kamil Domański -Kamjar Gerami -Kanstantsin Shautsou -Kara Alexandra -Karan Lyons -Kareem Khazem -kargakis -Karl Grzeszczak -Karol Duleba -Karthik Nayak -Kate Heddleston -Katie McLaughlin -Kato Kazuyoshi -Katrina Owen -Kawsar Saiyeed -Kay Yan -kayrus -Ke Li -Ke Xu -Kei Ohmura -Keith Hudgins -Keli Hu -Ken Cochrane -Ken Herner -Ken ICHIKAWA -Kenfe-Mickaël Laventure -Kenjiro Nakayama -Kent Johnson -Kevin "qwazerty" Houdebert -Kevin Burke -Kevin Clark -Kevin Feyrer -Kevin J. Lynagh -Kevin Jing Qiu -Kevin Kern -Kevin Menard -Kevin Meredith -Kevin P. Kucharczyk -Kevin Richardson -Kevin Shi -Kevin Wallace -Kevin Yap -Keyvan Fatehi -kies -Kim BKC Carlbacker -Kim Eik -Kimbro Staken -Kir Kolyshkin -Kiran Gangadharan -Kirill SIbirev -knappe -Kohei Tsuruta -Koichi Shiraishi -Konrad Kleine -Konstantin Gribov -Konstantin L -Konstantin Pelykh -Krasi Georgiev -Krasimir Georgiev -Kris-Mikael Krister -Kristian Haugene -Kristina Zabunova -krrg -Kun Zhang -Kunal Kushwaha -Kyle Conroy -Kyle Linden -kyu -Lachlan Coote -Lai Jiangshan -Lajos Papp -Lakshan Perera -Lalatendu Mohanty -Lance Chen -Lance Kinley -Lars Butler -Lars Kellogg-Stedman -Lars R. Damerow -Lars-Magnus Skog -Laszlo Meszaros -Laura Frank -Laurent Erignoux -Laurie Voss -Leandro Siqueira -Lee Chao <932819864@qq.com> -Lee, Meng-Han -leeplay -Lei Jitang -Len Weincier -Lennie -Leo Gallucci -Leszek Kowalski -Levi Blackstone -Levi Gross -Lewis Daly -Lewis Marshall -Lewis Peckover -Li Yi -Liam Macgillavry -Liana Lo -Liang Mingqiang -Liang-Chi Hsieh -Liao Qingwei -Lily Guo -limsy -Lin Lu -LingFaKe -Linus Heckemann -Liran Tal -Liron Levin -Liu Bo -Liu Hua -liwenqi -lixiaobing10051267 -Liz Zhang -LIZAO LI -Lizzie Dixon <_@lizzie.io> -Lloyd Dewolf -Lokesh Mandvekar -longliqiang88 <394564827@qq.com> -Lorenz Leutgeb -Lorenzo Fontana -Louis Opter -Luca Favatella -Luca Marturana -Luca Orlandi -Luca-Bogdan Grigorescu -Lucas Chan -Lucas Chi -Lucas Molas -Luciano Mores -Luis Martínez de Bartolomé Izquierdo -Luiz Svoboda -Lukas Waslowski -lukaspustina -Lukasz Zajaczkowski -Luke Marsden -Lyn -Lynda O'Leary -Lénaïc Huard -Ma Müller -Ma Shimiao -Mabin -Madhan Raj Mookkandy -Madhav Puri -Madhu Venugopal -Mageee -Mahesh Tiyyagura -malnick -Malte Janduda -Manfred Touron -Manfred Zabarauskas -Manjunath A Kumatagi -Mansi Nahar -Manuel Meurer -Manuel Woelker -mapk0y -Marc Abramowitz -Marc Kuo -Marc Tamsky -Marcel Edmund Franke -Marcelo Horacio Fortino -Marcelo Salazar -Marco Hennings -Marcus Cobden -Marcus Farkas -Marcus Linke -Marcus Martins -Marcus Ramberg -Marek Goldmann -Marian Marinov -Marianna Tessel -Mario Loriedo -Marius Gundersen -Marius Sturm -Marius Voila -Mark Allen -Mark McGranaghan -Mark McKinstry -Mark Milstein -Mark Oates -Mark Parker -Mark West -Markan Patel -Marko Mikulicic -Marko Tibold -Markus Fix -Markus Kortlang -Martijn Dwars -Martijn van Oosterhout -Martin Honermeyer -Martin Kelly -Martin Mosegaard Amdisen -Martin Redmond -Mary Anthony -Masahito Zembutsu -Masayuki Morita -Mason Malone -Mateusz Sulima -Mathias Monnerville -Mathieu Le Marec - Pasquet -Mathieu Parent -Matt Apperson -Matt Bachmann -Matt Bentley -Matt Haggard -Matt Hoyle -Matt McCormick -Matt Moore -Matt Richardson -Matt Rickard -Matt Robenolt -Matt Schurenko -Matt Williams -Matthew Heon -Matthew Lapworth -Matthew Mayer -Matthew Mosesohn -Matthew Mueller -Matthew Riley -Matthias Klumpp -Matthias Kühnle -Matthias Rampke -Matthieu Hauglustaine -Mauricio Garavaglia -mauriyouth -Max Shytikov -Maxim Fedchyshyn -Maxim Ivanov -Maxim Kulkin -Maxim Treskin -Maxime Petazzoni -Meaglith Ma -meejah -Megan Kostick -Mehul Kar -Mei ChunTao -Mengdi Gao -Mert Yazıcıoğlu -mgniu -Micah Zoltu -Michael A. Smith -Michael Bridgen -Michael Brown -Michael Chiang -Michael Crosby -Michael Currie -Michael Friis -Michael Gorsuch -Michael Grauer -Michael Holzheu -Michael Hudson-Doyle -Michael Huettermann -Michael Irwin -Michael Käufl -Michael Neale -Michael Prokop -Michael Scharf -Michael Spetsiotis -Michael Stapelberg -Michael Steinert -Michael Thies -Michael West -Michal Fojtik -Michal Gebauer -Michal Jemala -Michal Minář -Michal Wieczorek -Michaël Pailloncy -Michał Czeraszkiewicz -Michiel@unhosted -Mickaël FORTUNATO -Miguel Angel Fernández -Miguel Morales -Mihai Borobocea -Mihuleacc Sergiu -Mike Brown -Mike Casas -Mike Chelen -Mike Danese -Mike Dillon -Mike Dougherty -Mike Estes -Mike Gaffney -Mike Goelzer -Mike Leone -Mike MacCana -Mike Naberezny -Mike Snitzer -mikelinjie <294893458@qq.com> -Mikhail Sobolev -Miklos Szegedi -Milind Chawre -Miloslav Trmač -mingqing -Mingzhen Feng -Misty Stanley-Jones -Mitch Capper -Mizuki Urushida -mlarcher -Mohammad Banikazemi -Mohammed Aaqib Ansari -Mohit Soni -Moorthy RS -Morgan Bauer -Morgante Pell -Morgy93 -Morten Siebuhr -Morton Fox -Moysés Borges -mrfly -Mrunal Patel -Muayyad Alsadi -Mustafa Akın -Muthukumar R -Máximo Cuadros -Médi-Rémi Hashim -Nace Oroz -Nahum Shalman -Nakul Pathak -Nalin Dahyabhai -Nan Monnand Deng -Naoki Orii -Natalie Parker -Natanael Copa -Nate Brennand -Nate Eagleson -Nate Jones -Nathan Hsieh -Nathan Kleyn -Nathan LeClaire -Nathan McCauley -Nathan Williams -Naveed Jamil -Neal McBurnett -Neil Horman -Neil Peterson -Nelson Chen -Neyazul Haque -Nghia Tran -Niall O'Higgins -Nicholas E. Rabenau -Nick DeCoursin -Nick Irvine -Nick Parker -Nick Payne -Nick Russo -Nick Stenning -Nick Stinemates -NickrenREN -Nicola Kabar -Nicolas Borboën -Nicolas De Loof -Nicolas Dudebout -Nicolas Goy -Nicolas Kaiser -Nicolas Sterchele -Nicolás Hock Isaza -Nigel Poulton -Nik Nyby -Nikhil Chawla -NikolaMandic -Nikolas Garofil -Nikolay Milovanov -Nirmal Mehta -Nishant Totla -NIWA Hideyuki -Noah Meyerhans -Noah Treuhaft -noducks -Nolan Darilek -nponeccop -Nuutti Kotivuori -nzwsch -O.S. Tezer -objectified -odk- -Oguz Bilgic -Oh Jinkyun -Ohad Schneider -ohmystack -Ole Reifschneider -Oliver Neal -Olivier Gambier -Olle Jonsson -Oriol Francès -Oskar Niburski -Otto Kekäläinen -Ouyang Liduo -Ovidio Mallo -Panagiotis Moustafellos -Paolo G. Giarrusso -Pascal -Pascal Borreli -Pascal Hartig -Patrick Böänziger -Patrick Devine -Patrick Hemmer -Patrick Stapleton -pattichen -Paul -paul -Paul Annesley -Paul Bellamy -Paul Bowsher -Paul Furtado -Paul Hammond -Paul Jimenez -Paul Kehrer -Paul Lietar -Paul Liljenberg -Paul Morie -Paul Nasrat -Paul Weaver -Paulo Ribeiro -Pavel Lobashov -Pavel Pletenev -Pavel Pospisil -Pavel Sutyrin -Pavel Tikhomirov -Pavlos Ratis -Pavol Vargovcik -Pawel Konczalski -Peeyush Gupta -Peggy Li -Pei Su -Peng Tao -Penghan Wang -Per Weijnitz -perhapszzy@sina.com -Peter Bourgon -Peter Braden -Peter Bücker -Peter Choi -Peter Dave Hello -Peter Edge -Peter Ericson -Peter Esbensen -Peter Jaffe -Peter Malmgren -Peter Salvatore -Peter Volpe -Peter Waller -Petr Švihlík -Phil -Phil Estes -Phil Spitler -Philip Alexander Etling -Philip Monroe -Philipp Gillé -Philipp Wahala -Philipp Weissensteiner -Phillip Alexander -phineas -pidster -Piergiuliano Bossi -Pierre -Pierre Carrier -Pierre Dal-Pra -Pierre Wacrenier -Pierre-Alain RIVIERE -Piotr Bogdan -pixelistik -Porjo -Poul Kjeldager Sørensen -Pradeep Chhetri -Pradip Dhara -Prasanna Gautam -Pratik Karki -Prayag Verma -Przemek Hejman -Pure White -pysqz -Qiang Huang -Qinglan Peng -qudongfang -Quentin Brossard -Quentin Perez -Quentin Tayssier -r0n22 -Rafal Jeczalik -Rafe Colton -Raghavendra K T -Raghuram Devarakonda -Raja Sami -Rajat Pandit -Rajdeep Dua -Ralf Sippl -Ralle -Ralph Bean -Ramkumar Ramachandra -Ramon Brooker -Ramon van Alteren -Ray Tsang -ReadmeCritic -Recursive Madman -Reficul -Regan McCooey -Remi Rampin -Remy Suen -Renato Riccieri Santos Zannon -Renaud Gaubert -Rhys Hiltner -Ri Xu -Ricardo N Feliciano -Rich Moyse -Rich Seymour -Richard -Richard Burnison -Richard Harvey -Richard Mathie -Richard Metzler -Richard Scothern -Richo Healey -Rick Bradley -Rick van de Loo -Rick Wieman -Rik Nijessen -Riku Voipio -Riley Guerin -Ritesh H Shukla -Riyaz Faizullabhoy -Rob Vesse -Robert Bachmann -Robert Bittle -Robert Obryk -Robert Schneider -Robert Stern -Robert Terhaar -Robert Wallis -Roberto G. Hashioka -Roberto Muñoz Fernández -Robin Naundorf -Robin Schneider -Robin Speekenbrink -robpc -Rodolfo Carvalho -Rodrigo Vaz -Roel Van Nyen -Roger Peppe -Rohit Jnagal -Rohit Kadam -Rojin George -Roland Huß -Roland Kammerer -Roland Moriz -Roma Sokolov -Roman Dudin -Roman Strashkin -Ron Smits -Ron Williams -root -root -root -root -Rory Hunter -Rory McCune -Ross Boucher -Rovanion Luckey -Royce Remer -Rozhnov Alexandr -Rudolph Gottesheim -Rui Lopes -Runshen Zhu -Ryan Abrams -Ryan Anderson -Ryan Aslett -Ryan Belgrave -Ryan Detzel -Ryan Fowler -Ryan Liu -Ryan McLaughlin -Ryan O'Donnell -Ryan Seto -Ryan Simmen -Ryan Stelly -Ryan Thomas -Ryan Trauntvein -Ryan Wallner -Ryan Zhang -ryancooper7 -RyanDeng -Rémy Greinhofer -s. rannou -s00318865 -Sabin Basyal -Sachin Joshi -Sagar Hani -Sainath Grandhi -Sakeven Jiang -Sally O'Malley -Sam Abed -Sam Alba -Sam Bailey -Sam J Sharpe -Sam Neirinck -Sam Reis -Sam Rijs -Sambuddha Basu -Sami Wagiaalla -Samuel Andaya -Samuel Dion-Girardeau -Samuel Karp -Samuel PHAN -Sandeep Bansal -Sankar சங்கர் -Sanket Saurav -Santhosh Manohar -sapphiredev -Sargun Dhillon -Sascha Andres -Satnam Singh -Satoshi Amemiya -Satoshi Tagomori -Scott Bessler -Scott Collier -Scott Johnston -Scott Stamp -Scott Walls -sdreyesg -Sean Christopherson -Sean Cronin -Sean Lee -Sean McIntyre -Sean OMeara -Sean P. Kane -Sean Rodman -Sebastiaan van Steenis -Sebastiaan van Stijn -Senthil Kumar Selvaraj -Senthil Kumaran -SeongJae Park -Seongyeol Lim -Serge Hallyn -Sergey Alekseev -Sergey Evstifeev -Sergii Kabashniuk -Serhat Gülçiçek -Sevki Hasirci -Shane Canon -Shane da Silva -Shaun Kaasten -shaunol -Shawn Landden -Shawn Siefkas -shawnhe -Shayne Wang -Shekhar Gulati -Sheng Yang -Shengbo Song -Shev Yan -Shih-Yuan Lee -Shijiang Wei -Shijun Qin -Shishir Mahajan -Shoubhik Bose -Shourya Sarcar -shuai-z -Shukui Yang -Shuwei Hao -Sian Lerk Lau -Sidhartha Mani -sidharthamani -Silas Sewell -Silvan Jegen -Simei He -Simon Eskildsen -Simon Ferquel -Simon Leinen -Simon Menke -Simon Taranto -Simon Vikstrom -Sindhu S -Sjoerd Langkemper -Solganik Alexander -Solomon Hykes -Song Gao -Soshi Katsuta -Soulou -Spencer Brown -Spencer Smith -Sridatta Thatipamala -Sridhar Ratnakumar -Srini Brahmaroutu -Srinivasan Srivatsan -Stanislav Bondarenko -Steeve Morin -Stefan Berger -Stefan J. Wernli -Stefan Praszalowicz -Stefan S. -Stefan Scherer -Stefan Staudenmeyer -Stefan Weil -Stephan Spindler -Stephen Crosby -Stephen Day -Stephen Drake -Stephen Rust -Steve Desmond -Steve Dougherty -Steve Durrheimer -Steve Francia -Steve Koch -Steven Burgess -Steven Erenst -Steven Hartland -Steven Iveson -Steven Merrill -Steven Richards -Steven Taylor -Subhajit Ghosh -Sujith Haridasan -Sun Gengze <690388648@qq.com> -Sun Jianbo -Sunny Gogoi -Suryakumar Sudar -Sven Dowideit -Swapnil Daingade -Sylvain Baubeau -Sylvain Bellemare -Sébastien -Sébastien HOUZÉ -Sébastien Luttringer -Sébastien Stormacq -Tabakhase -Tadej Janež -TAGOMORI Satoshi -tang0th -Tangi Colin -Tatsuki Sugiura -Tatsushi Inagaki -Taylor Jones -tbonza -Ted M. Young -Tehmasp Chaudhri -Tejesh Mehta -terryding77 <550147740@qq.com> -tgic -Thatcher Peskens -theadactyl -Thell 'Bo' Fowler -Thermionix -Thijs Terlouw -Thomas Bikeev -Thomas Frössman -Thomas Gazagnaire -Thomas Grainger -Thomas Hansen -Thomas Leonard -Thomas Léveil -Thomas Orozco -Thomas Riccardi -Thomas Schroeter -Thomas Sjögren -Thomas Swift -Thomas Tanaka -Thomas Texier -Ti Zhou -Tianon Gravi -Tianyi Wang -Tibor Vass -Tiffany Jernigan -Tiffany Low -Tim Bart -Tim Bosse -Tim Dettrick -Tim Düsterhus -Tim Hockin -Tim Potter -Tim Ruffles -Tim Smith -Tim Terhorst -Tim Wang -Tim Waugh -Tim Wraight -Tim Zju <21651152@zju.edu.cn> -timfeirg -Timothy Hobbs -tjwebb123 -tobe -Tobias Bieniek -Tobias Bradtke -Tobias Gesellchen -Tobias Klauser -Tobias Munk -Tobias Schmidt -Tobias Schwab -Todd Crane -Todd Lunter -Todd Whiteman -Toli Kuznets -Tom Barlow -Tom Booth -Tom Denham -Tom Fotherby -Tom Howe -Tom Hulihan -Tom Maaswinkel -Tom Sweeney -Tom Wilkie -Tom X. Tobin -Tomas Tomecek -Tomasz Kopczynski -Tomasz Lipinski -Tomasz Nurkiewicz -Tommaso Visconti -Tomáš Hrčka -Tonny Xu -Tony Abboud -Tony Daws -Tony Miller -toogley -Torstein Husebø -Tõnis Tiigi -tpng -tracylihui <793912329@qq.com> -Trapier Marshall -Travis Cline -Travis Thieman -Trent Ogren -Trevor -Trevor Pounds -Trevor Sullivan -Trishna Guha -Tristan Carel -Troy Denton -Tycho Andersen -Tyler Brock -Tzu-Jung Lee -uhayate -Ulysse Carion -Umesh Yadav -Utz Bacher -vagrant -Vaidas Jablonskis -vanderliang -Veres Lajos -Victor Algaze -Victor Coisne -Victor Costan -Victor I. Wood -Victor Lyuboslavsky -Victor Marmol -Victor Palma -Victor Vieux -Victoria Bialas -Vijaya Kumar K -Viktor Stanchev -Viktor Vojnovski -VinayRaghavanKS -Vincent Batts -Vincent Bernat -Vincent Demeester -Vincent Giersch -Vincent Mayers -Vincent Woo -Vinod Kulkarni -Vishal Doshi -Vishnu Kannan -Vitaly Ostrosablin -Vitor Monteiro -Vivek Agarwal -Vivek Dasgupta -Vivek Goyal -Vladimir Bulyga -Vladimir Kirillov -Vladimir Pouzanov -Vladimir Rutsky -Vladimir Varankin -VladimirAus -Vlastimil Zeman -Vojtech Vitek (V-Teq) -waitingkuo -Walter Leibbrandt -Walter Stanish -Wang Chao -Wang Guoliang -Wang Jie -Wang Long -Wang Ping -Wang Xing -Wang Yuexiao -Ward Vandewege -WarheadsSE -Wayne Chang -Wayne Song -Weerasak Chongnguluam -Wei Wu -Wei-Ting Kuo -weiyan -Weiyang Zhu -Wen Cheng Ma -Wendel Fleming -Wenjun Tang -Wenkai Yin -Wentao Zhang -Wenxuan Zhao -Wenyu You <21551128@zju.edu.cn> -Wenzhi Liang -Wes Morgan -Wewang Xiaorenfine -Will Dietz -Will Rouesnel -Will Weaver -willhf -William Delanoue -William Henry -William Hubbs -William Martin -William Riancho -William Thurston -WiseTrem -Wolfgang Powisch -Wonjun Kim -xamyzhao -Xianglin Gao -Xianlu Bird -XiaoBing Jiang -Xiaoxu Chen -Xiaoyu Zhang -xiekeyang -Xinbo Weng -Xinzi Zhou -Xiuming Chen -Xuecong Liao -xuzhaokui -Yahya -YAMADA Tsuyoshi -Yamasaki Masahide -Yan Feng -Yang Bai -Yang Pengfei -yangchenliang -Yanqiang Miao -Yao Zaiyong -Yassine Tijani -Yasunori Mahata -Yazhong Liu -Yestin Sun -Yi EungJun -Yibai Zhang -Yihang Ho -Ying Li -Yohei Ueda -Yong Tang -Yongzhi Pan -Yosef Fertel -You-Sheng Yang (楊有勝) -Youcef YEKHLEF -Yu Changchun -Yu Chengxia -Yu Peng -Yu-Ju Hong -Yuan Sun -Yuanhong Peng -Yuhao Fang -Yunxiang Huang -Yurii Rashkovskii -Yves Junqueira -Zac Dover -Zach Borboa -Zachary Jaffee -Zain Memon -Zaiste! -Zane DeGraffenried -Zefan Li -Zen Lin(Zhinan Lin) -Zhang Kun -Zhang Wei -Zhang Wentao -ZhangHang -zhangxianwei -Zhenan Ye <21551168@zju.edu.cn> -zhenghenghuo -Zhenkun Bi -Zhou Hao -Zhu Guihua -Zhu Kunjia -Zhuoyun Wei -Zilin Du -zimbatm -Ziming Dong -ZJUshuaizhou <21551191@zju.edu.cn> -zmarouf -Zoltan Tombol -Zou Yu -zqh -Zuhayr Elahi -Zunayed Ali -Álex González -Álvaro Lázaro -Átila Camurça Alves -尹吉峰 -徐俊杰 -搏通 -黄艳红00139573 diff --git a/vendor/github.com/docker/docker/NOTICE b/vendor/github.com/docker/docker/NOTICE deleted file mode 100644 index 0c74e15b0..000000000 --- a/vendor/github.com/docker/docker/NOTICE +++ /dev/null @@ -1,19 +0,0 @@ -Docker -Copyright 2012-2017 Docker, Inc. - -This product includes software developed at Docker, Inc. (https://www.docker.com). - -This product contains software (https://github.com/kr/pty) developed -by Keith Rarick, licensed under the MIT License. - -The following is courtesy of our legal counsel: - - -Use and transfer of Docker may be subject to certain restrictions by the -United States and other governments. -It is your responsibility to ensure that your use and/or transfer does not -violate applicable laws. - -For more information, please see https://www.bis.doc.gov - -See also https://www.apache.org/dev/crypto.html and/or seek legal counsel. diff --git a/vendor/github.com/dustin/go-humanize/.travis.yml b/vendor/github.com/dustin/go-humanize/.travis.yml deleted file mode 100644 index ba95cdd15..000000000 --- a/vendor/github.com/dustin/go-humanize/.travis.yml +++ /dev/null @@ -1,21 +0,0 @@ -sudo: false -language: go -go: - - 1.3.x - - 1.5.x - - 1.6.x - - 1.7.x - - 1.8.x - - 1.9.x - - master -matrix: - allow_failures: - - go: master - fast_finish: true -install: - - # Do nothing. This is needed to prevent default install action "go get -t -v ./..." from happening here (we want it to happen inside script step). -script: - - go get -t -v ./... - - diff -u <(echo -n) <(gofmt -d -s .) - - go tool vet . - - go test -v -race ./... diff --git a/vendor/github.com/dustin/go-humanize/LICENSE b/vendor/github.com/dustin/go-humanize/LICENSE deleted file mode 100644 index 8d9a94a90..000000000 --- a/vendor/github.com/dustin/go-humanize/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -Copyright (c) 2005-2008 Dustin Sallings - -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/github.com/dustin/go-humanize/README.markdown b/vendor/github.com/dustin/go-humanize/README.markdown deleted file mode 100644 index 91b4ae564..000000000 --- a/vendor/github.com/dustin/go-humanize/README.markdown +++ /dev/null @@ -1,124 +0,0 @@ -# Humane Units [![Build Status](https://travis-ci.org/dustin/go-humanize.svg?branch=master)](https://travis-ci.org/dustin/go-humanize) [![GoDoc](https://godoc.org/github.com/dustin/go-humanize?status.svg)](https://godoc.org/github.com/dustin/go-humanize) - -Just a few functions for helping humanize times and sizes. - -`go get` it as `github.com/dustin/go-humanize`, import it as -`"github.com/dustin/go-humanize"`, use it as `humanize`. - -See [godoc](https://godoc.org/github.com/dustin/go-humanize) for -complete documentation. - -## Sizes - -This lets you take numbers like `82854982` and convert them to useful -strings like, `83 MB` or `79 MiB` (whichever you prefer). - -Example: - -```go -fmt.Printf("That file is %s.", humanize.Bytes(82854982)) // That file is 83 MB. -``` - -## Times - -This lets you take a `time.Time` and spit it out in relative terms. -For example, `12 seconds ago` or `3 days from now`. - -Example: - -```go -fmt.Printf("This was touched %s.", humanize.Time(someTimeInstance)) // This was touched 7 hours ago. -``` - -Thanks to Kyle Lemons for the time implementation from an IRC -conversation one day. It's pretty neat. - -## Ordinals - -From a [mailing list discussion][odisc] where a user wanted to be able -to label ordinals. - - 0 -> 0th - 1 -> 1st - 2 -> 2nd - 3 -> 3rd - 4 -> 4th - [...] - -Example: - -```go -fmt.Printf("You're my %s best friend.", humanize.Ordinal(193)) // You are my 193rd best friend. -``` - -## Commas - -Want to shove commas into numbers? Be my guest. - - 0 -> 0 - 100 -> 100 - 1000 -> 1,000 - 1000000000 -> 1,000,000,000 - -100000 -> -100,000 - -Example: - -```go -fmt.Printf("You owe $%s.\n", humanize.Comma(6582491)) // You owe $6,582,491. -``` - -## Ftoa - -Nicer float64 formatter that removes trailing zeros. - -```go -fmt.Printf("%f", 2.24) // 2.240000 -fmt.Printf("%s", humanize.Ftoa(2.24)) // 2.24 -fmt.Printf("%f", 2.0) // 2.000000 -fmt.Printf("%s", humanize.Ftoa(2.0)) // 2 -``` - -## SI notation - -Format numbers with [SI notation][sinotation]. - -Example: - -```go -humanize.SI(0.00000000223, "M") // 2.23 nM -``` - -## English-specific functions - -The following functions are in the `humanize/english` subpackage. - -### Plurals - -Simple English pluralization - -```go -english.PluralWord(1, "object", "") // object -english.PluralWord(42, "object", "") // objects -english.PluralWord(2, "bus", "") // buses -english.PluralWord(99, "locus", "loci") // loci - -english.Plural(1, "object", "") // 1 object -english.Plural(42, "object", "") // 42 objects -english.Plural(2, "bus", "") // 2 buses -english.Plural(99, "locus", "loci") // 99 loci -``` - -### Word series - -Format comma-separated words lists with conjuctions: - -```go -english.WordSeries([]string{"foo"}, "and") // foo -english.WordSeries([]string{"foo", "bar"}, "and") // foo and bar -english.WordSeries([]string{"foo", "bar", "baz"}, "and") // foo, bar and baz - -english.OxfordWordSeries([]string{"foo", "bar", "baz"}, "and") // foo, bar, and baz -``` - -[odisc]: https://groups.google.com/d/topic/golang-nuts/l8NhI74jl-4/discussion -[sinotation]: http://en.wikipedia.org/wiki/Metric_prefix diff --git a/vendor/github.com/dustin/go-humanize/big.go b/vendor/github.com/dustin/go-humanize/big.go deleted file mode 100644 index f49dc337d..000000000 --- a/vendor/github.com/dustin/go-humanize/big.go +++ /dev/null @@ -1,31 +0,0 @@ -package humanize - -import ( - "math/big" -) - -// order of magnitude (to a max order) -func oomm(n, b *big.Int, maxmag int) (float64, int) { - mag := 0 - m := &big.Int{} - for n.Cmp(b) >= 0 { - n.DivMod(n, b, m) - mag++ - if mag == maxmag && maxmag >= 0 { - break - } - } - return float64(n.Int64()) + (float64(m.Int64()) / float64(b.Int64())), mag -} - -// total order of magnitude -// (same as above, but with no upper limit) -func oom(n, b *big.Int) (float64, int) { - mag := 0 - m := &big.Int{} - for n.Cmp(b) >= 0 { - n.DivMod(n, b, m) - mag++ - } - return float64(n.Int64()) + (float64(m.Int64()) / float64(b.Int64())), mag -} diff --git a/vendor/github.com/dustin/go-humanize/bigbytes.go b/vendor/github.com/dustin/go-humanize/bigbytes.go deleted file mode 100644 index 1a2bf6172..000000000 --- a/vendor/github.com/dustin/go-humanize/bigbytes.go +++ /dev/null @@ -1,173 +0,0 @@ -package humanize - -import ( - "fmt" - "math/big" - "strings" - "unicode" -) - -var ( - bigIECExp = big.NewInt(1024) - - // BigByte is one byte in bit.Ints - BigByte = big.NewInt(1) - // BigKiByte is 1,024 bytes in bit.Ints - BigKiByte = (&big.Int{}).Mul(BigByte, bigIECExp) - // BigMiByte is 1,024 k bytes in bit.Ints - BigMiByte = (&big.Int{}).Mul(BigKiByte, bigIECExp) - // BigGiByte is 1,024 m bytes in bit.Ints - BigGiByte = (&big.Int{}).Mul(BigMiByte, bigIECExp) - // BigTiByte is 1,024 g bytes in bit.Ints - BigTiByte = (&big.Int{}).Mul(BigGiByte, bigIECExp) - // BigPiByte is 1,024 t bytes in bit.Ints - BigPiByte = (&big.Int{}).Mul(BigTiByte, bigIECExp) - // BigEiByte is 1,024 p bytes in bit.Ints - BigEiByte = (&big.Int{}).Mul(BigPiByte, bigIECExp) - // BigZiByte is 1,024 e bytes in bit.Ints - BigZiByte = (&big.Int{}).Mul(BigEiByte, bigIECExp) - // BigYiByte is 1,024 z bytes in bit.Ints - BigYiByte = (&big.Int{}).Mul(BigZiByte, bigIECExp) -) - -var ( - bigSIExp = big.NewInt(1000) - - // BigSIByte is one SI byte in big.Ints - BigSIByte = big.NewInt(1) - // BigKByte is 1,000 SI bytes in big.Ints - BigKByte = (&big.Int{}).Mul(BigSIByte, bigSIExp) - // BigMByte is 1,000 SI k bytes in big.Ints - BigMByte = (&big.Int{}).Mul(BigKByte, bigSIExp) - // BigGByte is 1,000 SI m bytes in big.Ints - BigGByte = (&big.Int{}).Mul(BigMByte, bigSIExp) - // BigTByte is 1,000 SI g bytes in big.Ints - BigTByte = (&big.Int{}).Mul(BigGByte, bigSIExp) - // BigPByte is 1,000 SI t bytes in big.Ints - BigPByte = (&big.Int{}).Mul(BigTByte, bigSIExp) - // BigEByte is 1,000 SI p bytes in big.Ints - BigEByte = (&big.Int{}).Mul(BigPByte, bigSIExp) - // BigZByte is 1,000 SI e bytes in big.Ints - BigZByte = (&big.Int{}).Mul(BigEByte, bigSIExp) - // BigYByte is 1,000 SI z bytes in big.Ints - BigYByte = (&big.Int{}).Mul(BigZByte, bigSIExp) -) - -var bigBytesSizeTable = map[string]*big.Int{ - "b": BigByte, - "kib": BigKiByte, - "kb": BigKByte, - "mib": BigMiByte, - "mb": BigMByte, - "gib": BigGiByte, - "gb": BigGByte, - "tib": BigTiByte, - "tb": BigTByte, - "pib": BigPiByte, - "pb": BigPByte, - "eib": BigEiByte, - "eb": BigEByte, - "zib": BigZiByte, - "zb": BigZByte, - "yib": BigYiByte, - "yb": BigYByte, - // Without suffix - "": BigByte, - "ki": BigKiByte, - "k": BigKByte, - "mi": BigMiByte, - "m": BigMByte, - "gi": BigGiByte, - "g": BigGByte, - "ti": BigTiByte, - "t": BigTByte, - "pi": BigPiByte, - "p": BigPByte, - "ei": BigEiByte, - "e": BigEByte, - "z": BigZByte, - "zi": BigZiByte, - "y": BigYByte, - "yi": BigYiByte, -} - -var ten = big.NewInt(10) - -func humanateBigBytes(s, base *big.Int, sizes []string) string { - if s.Cmp(ten) < 0 { - return fmt.Sprintf("%d B", s) - } - c := (&big.Int{}).Set(s) - val, mag := oomm(c, base, len(sizes)-1) - suffix := sizes[mag] - f := "%.0f %s" - if val < 10 { - f = "%.1f %s" - } - - return fmt.Sprintf(f, val, suffix) - -} - -// BigBytes produces a human readable representation of an SI size. -// -// See also: ParseBigBytes. -// -// BigBytes(82854982) -> 83 MB -func BigBytes(s *big.Int) string { - sizes := []string{"B", "kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"} - return humanateBigBytes(s, bigSIExp, sizes) -} - -// BigIBytes produces a human readable representation of an IEC size. -// -// See also: ParseBigBytes. -// -// BigIBytes(82854982) -> 79 MiB -func BigIBytes(s *big.Int) string { - sizes := []string{"B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB"} - return humanateBigBytes(s, bigIECExp, sizes) -} - -// ParseBigBytes parses a string representation of bytes into the number -// of bytes it represents. -// -// See also: BigBytes, BigIBytes. -// -// ParseBigBytes("42 MB") -> 42000000, nil -// ParseBigBytes("42 mib") -> 44040192, nil -func ParseBigBytes(s string) (*big.Int, error) { - lastDigit := 0 - hasComma := false - for _, r := range s { - if !(unicode.IsDigit(r) || r == '.' || r == ',') { - break - } - if r == ',' { - hasComma = true - } - lastDigit++ - } - - num := s[:lastDigit] - if hasComma { - num = strings.Replace(num, ",", "", -1) - } - - val := &big.Rat{} - _, err := fmt.Sscanf(num, "%f", val) - if err != nil { - return nil, err - } - - extra := strings.ToLower(strings.TrimSpace(s[lastDigit:])) - if m, ok := bigBytesSizeTable[extra]; ok { - mv := (&big.Rat{}).SetInt(m) - val.Mul(val, mv) - rv := &big.Int{} - rv.Div(val.Num(), val.Denom()) - return rv, nil - } - - return nil, fmt.Errorf("unhandled size name: %v", extra) -} diff --git a/vendor/github.com/dustin/go-humanize/bytes.go b/vendor/github.com/dustin/go-humanize/bytes.go deleted file mode 100644 index 0b498f488..000000000 --- a/vendor/github.com/dustin/go-humanize/bytes.go +++ /dev/null @@ -1,143 +0,0 @@ -package humanize - -import ( - "fmt" - "math" - "strconv" - "strings" - "unicode" -) - -// IEC Sizes. -// kibis of bits -const ( - Byte = 1 << (iota * 10) - KiByte - MiByte - GiByte - TiByte - PiByte - EiByte -) - -// SI Sizes. -const ( - IByte = 1 - KByte = IByte * 1000 - MByte = KByte * 1000 - GByte = MByte * 1000 - TByte = GByte * 1000 - PByte = TByte * 1000 - EByte = PByte * 1000 -) - -var bytesSizeTable = map[string]uint64{ - "b": Byte, - "kib": KiByte, - "kb": KByte, - "mib": MiByte, - "mb": MByte, - "gib": GiByte, - "gb": GByte, - "tib": TiByte, - "tb": TByte, - "pib": PiByte, - "pb": PByte, - "eib": EiByte, - "eb": EByte, - // Without suffix - "": Byte, - "ki": KiByte, - "k": KByte, - "mi": MiByte, - "m": MByte, - "gi": GiByte, - "g": GByte, - "ti": TiByte, - "t": TByte, - "pi": PiByte, - "p": PByte, - "ei": EiByte, - "e": EByte, -} - -func logn(n, b float64) float64 { - return math.Log(n) / math.Log(b) -} - -func humanateBytes(s uint64, base float64, sizes []string) string { - if s < 10 { - return fmt.Sprintf("%d B", s) - } - e := math.Floor(logn(float64(s), base)) - suffix := sizes[int(e)] - val := math.Floor(float64(s)/math.Pow(base, e)*10+0.5) / 10 - f := "%.0f %s" - if val < 10 { - f = "%.1f %s" - } - - return fmt.Sprintf(f, val, suffix) -} - -// Bytes produces a human readable representation of an SI size. -// -// See also: ParseBytes. -// -// Bytes(82854982) -> 83 MB -func Bytes(s uint64) string { - sizes := []string{"B", "kB", "MB", "GB", "TB", "PB", "EB"} - return humanateBytes(s, 1000, sizes) -} - -// IBytes produces a human readable representation of an IEC size. -// -// See also: ParseBytes. -// -// IBytes(82854982) -> 79 MiB -func IBytes(s uint64) string { - sizes := []string{"B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB"} - return humanateBytes(s, 1024, sizes) -} - -// ParseBytes parses a string representation of bytes into the number -// of bytes it represents. -// -// See Also: Bytes, IBytes. -// -// ParseBytes("42 MB") -> 42000000, nil -// ParseBytes("42 mib") -> 44040192, nil -func ParseBytes(s string) (uint64, error) { - lastDigit := 0 - hasComma := false - for _, r := range s { - if !(unicode.IsDigit(r) || r == '.' || r == ',') { - break - } - if r == ',' { - hasComma = true - } - lastDigit++ - } - - num := s[:lastDigit] - if hasComma { - num = strings.Replace(num, ",", "", -1) - } - - f, err := strconv.ParseFloat(num, 64) - if err != nil { - return 0, err - } - - extra := strings.ToLower(strings.TrimSpace(s[lastDigit:])) - if m, ok := bytesSizeTable[extra]; ok { - f *= float64(m) - if f >= math.MaxUint64 { - return 0, fmt.Errorf("too large: %v", s) - } - return uint64(f), nil - } - - return 0, fmt.Errorf("unhandled size name: %v", extra) -} diff --git a/vendor/github.com/dustin/go-humanize/comma.go b/vendor/github.com/dustin/go-humanize/comma.go deleted file mode 100644 index 520ae3e57..000000000 --- a/vendor/github.com/dustin/go-humanize/comma.go +++ /dev/null @@ -1,116 +0,0 @@ -package humanize - -import ( - "bytes" - "math" - "math/big" - "strconv" - "strings" -) - -// Comma produces a string form of the given number in base 10 with -// commas after every three orders of magnitude. -// -// e.g. Comma(834142) -> 834,142 -func Comma(v int64) string { - sign := "" - - // Min int64 can't be negated to a usable value, so it has to be special cased. - if v == math.MinInt64 { - return "-9,223,372,036,854,775,808" - } - - if v < 0 { - sign = "-" - v = 0 - v - } - - parts := []string{"", "", "", "", "", "", ""} - j := len(parts) - 1 - - for v > 999 { - parts[j] = strconv.FormatInt(v%1000, 10) - switch len(parts[j]) { - case 2: - parts[j] = "0" + parts[j] - case 1: - parts[j] = "00" + parts[j] - } - v = v / 1000 - j-- - } - parts[j] = strconv.Itoa(int(v)) - return sign + strings.Join(parts[j:], ",") -} - -// Commaf produces a string form of the given number in base 10 with -// commas after every three orders of magnitude. -// -// e.g. Commaf(834142.32) -> 834,142.32 -func Commaf(v float64) string { - buf := &bytes.Buffer{} - if v < 0 { - buf.Write([]byte{'-'}) - v = 0 - v - } - - comma := []byte{','} - - parts := strings.Split(strconv.FormatFloat(v, 'f', -1, 64), ".") - pos := 0 - if len(parts[0])%3 != 0 { - pos += len(parts[0]) % 3 - buf.WriteString(parts[0][:pos]) - buf.Write(comma) - } - for ; pos < len(parts[0]); pos += 3 { - buf.WriteString(parts[0][pos : pos+3]) - buf.Write(comma) - } - buf.Truncate(buf.Len() - 1) - - if len(parts) > 1 { - buf.Write([]byte{'.'}) - buf.WriteString(parts[1]) - } - return buf.String() -} - -// CommafWithDigits works like the Commaf but limits the resulting -// string to the given number of decimal places. -// -// e.g. CommafWithDigits(834142.32, 1) -> 834,142.3 -func CommafWithDigits(f float64, decimals int) string { - return stripTrailingDigits(Commaf(f), decimals) -} - -// BigComma produces a string form of the given big.Int in base 10 -// with commas after every three orders of magnitude. -func BigComma(b *big.Int) string { - sign := "" - if b.Sign() < 0 { - sign = "-" - b.Abs(b) - } - - athousand := big.NewInt(1000) - c := (&big.Int{}).Set(b) - _, m := oom(c, athousand) - parts := make([]string, m+1) - j := len(parts) - 1 - - mod := &big.Int{} - for b.Cmp(athousand) >= 0 { - b.DivMod(b, athousand, mod) - parts[j] = strconv.FormatInt(mod.Int64(), 10) - switch len(parts[j]) { - case 2: - parts[j] = "0" + parts[j] - case 1: - parts[j] = "00" + parts[j] - } - j-- - } - parts[j] = strconv.Itoa(int(b.Int64())) - return sign + strings.Join(parts[j:], ",") -} diff --git a/vendor/github.com/dustin/go-humanize/commaf.go b/vendor/github.com/dustin/go-humanize/commaf.go deleted file mode 100644 index 620690dec..000000000 --- a/vendor/github.com/dustin/go-humanize/commaf.go +++ /dev/null @@ -1,40 +0,0 @@ -// +build go1.6 - -package humanize - -import ( - "bytes" - "math/big" - "strings" -) - -// BigCommaf produces a string form of the given big.Float in base 10 -// with commas after every three orders of magnitude. -func BigCommaf(v *big.Float) string { - buf := &bytes.Buffer{} - if v.Sign() < 0 { - buf.Write([]byte{'-'}) - v.Abs(v) - } - - comma := []byte{','} - - parts := strings.Split(v.Text('f', -1), ".") - pos := 0 - if len(parts[0])%3 != 0 { - pos += len(parts[0]) % 3 - buf.WriteString(parts[0][:pos]) - buf.Write(comma) - } - for ; pos < len(parts[0]); pos += 3 { - buf.WriteString(parts[0][pos : pos+3]) - buf.Write(comma) - } - buf.Truncate(buf.Len() - 1) - - if len(parts) > 1 { - buf.Write([]byte{'.'}) - buf.WriteString(parts[1]) - } - return buf.String() -} diff --git a/vendor/github.com/dustin/go-humanize/ftoa.go b/vendor/github.com/dustin/go-humanize/ftoa.go deleted file mode 100644 index 1c62b640d..000000000 --- a/vendor/github.com/dustin/go-humanize/ftoa.go +++ /dev/null @@ -1,46 +0,0 @@ -package humanize - -import ( - "strconv" - "strings" -) - -func stripTrailingZeros(s string) string { - offset := len(s) - 1 - for offset > 0 { - if s[offset] == '.' { - offset-- - break - } - if s[offset] != '0' { - break - } - offset-- - } - return s[:offset+1] -} - -func stripTrailingDigits(s string, digits int) string { - if i := strings.Index(s, "."); i >= 0 { - if digits <= 0 { - return s[:i] - } - i++ - if i+digits >= len(s) { - return s - } - return s[:i+digits] - } - return s -} - -// Ftoa converts a float to a string with no trailing zeros. -func Ftoa(num float64) string { - return stripTrailingZeros(strconv.FormatFloat(num, 'f', 6, 64)) -} - -// FtoaWithDigits converts a float to a string but limits the resulting string -// to the given number of decimal places, and no trailing zeros. -func FtoaWithDigits(num float64, digits int) string { - return stripTrailingZeros(stripTrailingDigits(strconv.FormatFloat(num, 'f', 6, 64), digits)) -} diff --git a/vendor/github.com/dustin/go-humanize/humanize.go b/vendor/github.com/dustin/go-humanize/humanize.go deleted file mode 100644 index a2c2da31e..000000000 --- a/vendor/github.com/dustin/go-humanize/humanize.go +++ /dev/null @@ -1,8 +0,0 @@ -/* -Package humanize converts boring ugly numbers to human-friendly strings and back. - -Durations can be turned into strings such as "3 days ago", numbers -representing sizes like 82854982 into useful strings like, "83 MB" or -"79 MiB" (whichever you prefer). -*/ -package humanize diff --git a/vendor/github.com/dustin/go-humanize/number.go b/vendor/github.com/dustin/go-humanize/number.go deleted file mode 100644 index dec618659..000000000 --- a/vendor/github.com/dustin/go-humanize/number.go +++ /dev/null @@ -1,192 +0,0 @@ -package humanize - -/* -Slightly adapted from the source to fit go-humanize. - -Author: https://github.com/gorhill -Source: https://gist.github.com/gorhill/5285193 - -*/ - -import ( - "math" - "strconv" -) - -var ( - renderFloatPrecisionMultipliers = [...]float64{ - 1, - 10, - 100, - 1000, - 10000, - 100000, - 1000000, - 10000000, - 100000000, - 1000000000, - } - - renderFloatPrecisionRounders = [...]float64{ - 0.5, - 0.05, - 0.005, - 0.0005, - 0.00005, - 0.000005, - 0.0000005, - 0.00000005, - 0.000000005, - 0.0000000005, - } -) - -// FormatFloat produces a formatted number as string based on the following user-specified criteria: -// * thousands separator -// * decimal separator -// * decimal precision -// -// Usage: s := RenderFloat(format, n) -// The format parameter tells how to render the number n. -// -// See examples: http://play.golang.org/p/LXc1Ddm1lJ -// -// Examples of format strings, given n = 12345.6789: -// "#,###.##" => "12,345.67" -// "#,###." => "12,345" -// "#,###" => "12345,678" -// "#\u202F###,##" => "12 345,68" -// "#.###,###### => 12.345,678900 -// "" (aka default format) => 12,345.67 -// -// The highest precision allowed is 9 digits after the decimal symbol. -// There is also a version for integer number, FormatInteger(), -// which is convenient for calls within template. -func FormatFloat(format string, n float64) string { - // Special cases: - // NaN = "NaN" - // +Inf = "+Infinity" - // -Inf = "-Infinity" - if math.IsNaN(n) { - return "NaN" - } - if n > math.MaxFloat64 { - return "Infinity" - } - if n < -math.MaxFloat64 { - return "-Infinity" - } - - // default format - precision := 2 - decimalStr := "." - thousandStr := "," - positiveStr := "" - negativeStr := "-" - - if len(format) > 0 { - format := []rune(format) - - // If there is an explicit format directive, - // then default values are these: - precision = 9 - thousandStr = "" - - // collect indices of meaningful formatting directives - formatIndx := []int{} - for i, char := range format { - if char != '#' && char != '0' { - formatIndx = append(formatIndx, i) - } - } - - if len(formatIndx) > 0 { - // Directive at index 0: - // Must be a '+' - // Raise an error if not the case - // index: 0123456789 - // +0.000,000 - // +000,000.0 - // +0000.00 - // +0000 - if formatIndx[0] == 0 { - if format[formatIndx[0]] != '+' { - panic("RenderFloat(): invalid positive sign directive") - } - positiveStr = "+" - formatIndx = formatIndx[1:] - } - - // Two directives: - // First is thousands separator - // Raise an error if not followed by 3-digit - // 0123456789 - // 0.000,000 - // 000,000.00 - if len(formatIndx) == 2 { - if (formatIndx[1] - formatIndx[0]) != 4 { - panic("RenderFloat(): thousands separator directive must be followed by 3 digit-specifiers") - } - thousandStr = string(format[formatIndx[0]]) - formatIndx = formatIndx[1:] - } - - // One directive: - // Directive is decimal separator - // The number of digit-specifier following the separator indicates wanted precision - // 0123456789 - // 0.00 - // 000,0000 - if len(formatIndx) == 1 { - decimalStr = string(format[formatIndx[0]]) - precision = len(format) - formatIndx[0] - 1 - } - } - } - - // generate sign part - var signStr string - if n >= 0.000000001 { - signStr = positiveStr - } else if n <= -0.000000001 { - signStr = negativeStr - n = -n - } else { - signStr = "" - n = 0.0 - } - - // split number into integer and fractional parts - intf, fracf := math.Modf(n + renderFloatPrecisionRounders[precision]) - - // generate integer part string - intStr := strconv.FormatInt(int64(intf), 10) - - // add thousand separator if required - if len(thousandStr) > 0 { - for i := len(intStr); i > 3; { - i -= 3 - intStr = intStr[:i] + thousandStr + intStr[i:] - } - } - - // no fractional part, we can leave now - if precision == 0 { - return signStr + intStr - } - - // generate fractional part - fracStr := strconv.Itoa(int(fracf * renderFloatPrecisionMultipliers[precision])) - // may need padding - if len(fracStr) < precision { - fracStr = "000000000000000"[:precision-len(fracStr)] + fracStr - } - - return signStr + intStr + decimalStr + fracStr -} - -// FormatInteger produces a formatted number as string. -// See FormatFloat. -func FormatInteger(format string, n int) string { - return FormatFloat(format, float64(n)) -} diff --git a/vendor/github.com/dustin/go-humanize/ordinals.go b/vendor/github.com/dustin/go-humanize/ordinals.go deleted file mode 100644 index 43d88a861..000000000 --- a/vendor/github.com/dustin/go-humanize/ordinals.go +++ /dev/null @@ -1,25 +0,0 @@ -package humanize - -import "strconv" - -// Ordinal gives you the input number in a rank/ordinal format. -// -// Ordinal(3) -> 3rd -func Ordinal(x int) string { - suffix := "th" - switch x % 10 { - case 1: - if x%100 != 11 { - suffix = "st" - } - case 2: - if x%100 != 12 { - suffix = "nd" - } - case 3: - if x%100 != 13 { - suffix = "rd" - } - } - return strconv.Itoa(x) + suffix -} diff --git a/vendor/github.com/dustin/go-humanize/si.go b/vendor/github.com/dustin/go-humanize/si.go deleted file mode 100644 index ae659e0e4..000000000 --- a/vendor/github.com/dustin/go-humanize/si.go +++ /dev/null @@ -1,123 +0,0 @@ -package humanize - -import ( - "errors" - "math" - "regexp" - "strconv" -) - -var siPrefixTable = map[float64]string{ - -24: "y", // yocto - -21: "z", // zepto - -18: "a", // atto - -15: "f", // femto - -12: "p", // pico - -9: "n", // nano - -6: "µ", // micro - -3: "m", // milli - 0: "", - 3: "k", // kilo - 6: "M", // mega - 9: "G", // giga - 12: "T", // tera - 15: "P", // peta - 18: "E", // exa - 21: "Z", // zetta - 24: "Y", // yotta -} - -var revSIPrefixTable = revfmap(siPrefixTable) - -// revfmap reverses the map and precomputes the power multiplier -func revfmap(in map[float64]string) map[string]float64 { - rv := map[string]float64{} - for k, v := range in { - rv[v] = math.Pow(10, k) - } - return rv -} - -var riParseRegex *regexp.Regexp - -func init() { - ri := `^([\-0-9.]+)\s?([` - for _, v := range siPrefixTable { - ri += v - } - ri += `]?)(.*)` - - riParseRegex = regexp.MustCompile(ri) -} - -// ComputeSI finds the most appropriate SI prefix for the given number -// and returns the prefix along with the value adjusted to be within -// that prefix. -// -// See also: SI, ParseSI. -// -// e.g. ComputeSI(2.2345e-12) -> (2.2345, "p") -func ComputeSI(input float64) (float64, string) { - if input == 0 { - return 0, "" - } - mag := math.Abs(input) - exponent := math.Floor(logn(mag, 10)) - exponent = math.Floor(exponent/3) * 3 - - value := mag / math.Pow(10, exponent) - - // Handle special case where value is exactly 1000.0 - // Should return 1 M instead of 1000 k - if value == 1000.0 { - exponent += 3 - value = mag / math.Pow(10, exponent) - } - - value = math.Copysign(value, input) - - prefix := siPrefixTable[exponent] - return value, prefix -} - -// SI returns a string with default formatting. -// -// SI uses Ftoa to format float value, removing trailing zeros. -// -// See also: ComputeSI, ParseSI. -// -// e.g. SI(1000000, "B") -> 1 MB -// e.g. SI(2.2345e-12, "F") -> 2.2345 pF -func SI(input float64, unit string) string { - value, prefix := ComputeSI(input) - return Ftoa(value) + " " + prefix + unit -} - -// SIWithDigits works like SI but limits the resulting string to the -// given number of decimal places. -// -// e.g. SIWithDigits(1000000, 0, "B") -> 1 MB -// e.g. SIWithDigits(2.2345e-12, 2, "F") -> 2.23 pF -func SIWithDigits(input float64, decimals int, unit string) string { - value, prefix := ComputeSI(input) - return FtoaWithDigits(value, decimals) + " " + prefix + unit -} - -var errInvalid = errors.New("invalid input") - -// ParseSI parses an SI string back into the number and unit. -// -// See also: SI, ComputeSI. -// -// e.g. ParseSI("2.2345 pF") -> (2.2345e-12, "F", nil) -func ParseSI(input string) (float64, string, error) { - found := riParseRegex.FindStringSubmatch(input) - if len(found) != 4 { - return 0, "", errInvalid - } - mag := revSIPrefixTable[found[2]] - unit := found[3] - - base, err := strconv.ParseFloat(found[1], 64) - return base * mag, unit, err -} diff --git a/vendor/github.com/dustin/go-humanize/times.go b/vendor/github.com/dustin/go-humanize/times.go deleted file mode 100644 index dd3fbf5ef..000000000 --- a/vendor/github.com/dustin/go-humanize/times.go +++ /dev/null @@ -1,117 +0,0 @@ -package humanize - -import ( - "fmt" - "math" - "sort" - "time" -) - -// Seconds-based time units -const ( - Day = 24 * time.Hour - Week = 7 * Day - Month = 30 * Day - Year = 12 * Month - LongTime = 37 * Year -) - -// Time formats a time into a relative string. -// -// Time(someT) -> "3 weeks ago" -func Time(then time.Time) string { - return RelTime(then, time.Now(), "ago", "from now") -} - -// A RelTimeMagnitude struct contains a relative time point at which -// the relative format of time will switch to a new format string. A -// slice of these in ascending order by their "D" field is passed to -// CustomRelTime to format durations. -// -// The Format field is a string that may contain a "%s" which will be -// replaced with the appropriate signed label (e.g. "ago" or "from -// now") and a "%d" that will be replaced by the quantity. -// -// The DivBy field is the amount of time the time difference must be -// divided by in order to display correctly. -// -// e.g. if D is 2*time.Minute and you want to display "%d minutes %s" -// DivBy should be time.Minute so whatever the duration is will be -// expressed in minutes. -type RelTimeMagnitude struct { - D time.Duration - Format string - DivBy time.Duration -} - -var defaultMagnitudes = []RelTimeMagnitude{ - {time.Second, "now", time.Second}, - {2 * time.Second, "1 second %s", 1}, - {time.Minute, "%d seconds %s", time.Second}, - {2 * time.Minute, "1 minute %s", 1}, - {time.Hour, "%d minutes %s", time.Minute}, - {2 * time.Hour, "1 hour %s", 1}, - {Day, "%d hours %s", time.Hour}, - {2 * Day, "1 day %s", 1}, - {Week, "%d days %s", Day}, - {2 * Week, "1 week %s", 1}, - {Month, "%d weeks %s", Week}, - {2 * Month, "1 month %s", 1}, - {Year, "%d months %s", Month}, - {18 * Month, "1 year %s", 1}, - {2 * Year, "2 years %s", 1}, - {LongTime, "%d years %s", Year}, - {math.MaxInt64, "a long while %s", 1}, -} - -// RelTime formats a time into a relative string. -// -// It takes two times and two labels. In addition to the generic time -// delta string (e.g. 5 minutes), the labels are used applied so that -// the label corresponding to the smaller time is applied. -// -// RelTime(timeInPast, timeInFuture, "earlier", "later") -> "3 weeks earlier" -func RelTime(a, b time.Time, albl, blbl string) string { - return CustomRelTime(a, b, albl, blbl, defaultMagnitudes) -} - -// CustomRelTime formats a time into a relative string. -// -// It takes two times two labels and a table of relative time formats. -// In addition to the generic time delta string (e.g. 5 minutes), the -// labels are used applied so that the label corresponding to the -// smaller time is applied. -func CustomRelTime(a, b time.Time, albl, blbl string, magnitudes []RelTimeMagnitude) string { - lbl := albl - diff := b.Sub(a) - - if a.After(b) { - lbl = blbl - diff = a.Sub(b) - } - - n := sort.Search(len(magnitudes), func(i int) bool { - return magnitudes[i].D > diff - }) - - if n >= len(magnitudes) { - n = len(magnitudes) - 1 - } - mag := magnitudes[n] - args := []interface{}{} - escaped := false - for _, ch := range mag.Format { - if escaped { - switch ch { - case 's': - args = append(args, lbl) - case 'd': - args = append(args, diff/mag.DivBy) - } - escaped = false - } else { - escaped = ch == '%' - } - } - return fmt.Sprintf(mag.Format, args...) -} diff --git a/vendor/github.com/gorilla/websocket/.gitignore b/vendor/github.com/gorilla/websocket/.gitignore deleted file mode 100644 index ac710204f..000000000 --- a/vendor/github.com/gorilla/websocket/.gitignore +++ /dev/null @@ -1,25 +0,0 @@ -# Compiled Object files, Static and Dynamic libs (Shared Objects) -*.o -*.a -*.so - -# Folders -_obj -_test - -# Architecture specific extensions/prefixes -*.[568vq] -[568vq].out - -*.cgo1.go -*.cgo2.c -_cgo_defun.c -_cgo_gotypes.go -_cgo_export.* - -_testmain.go - -*.exe - -.idea/ -*.iml \ No newline at end of file diff --git a/vendor/github.com/gorilla/websocket/.travis.yml b/vendor/github.com/gorilla/websocket/.travis.yml deleted file mode 100644 index 3d8d29cf3..000000000 --- a/vendor/github.com/gorilla/websocket/.travis.yml +++ /dev/null @@ -1,19 +0,0 @@ -language: go -sudo: false - -matrix: - include: - - go: 1.4 - - go: 1.5 - - go: 1.6 - - go: 1.7 - - go: 1.8 - - go: tip - allow_failures: - - go: tip - -script: - - go get -t -v ./... - - diff -u <(echo -n) <(gofmt -d .) - - go vet $(go list ./... | grep -v /vendor/) - - go test -v -race ./... diff --git a/vendor/github.com/gorilla/websocket/AUTHORS b/vendor/github.com/gorilla/websocket/AUTHORS deleted file mode 100644 index b003eca0c..000000000 --- a/vendor/github.com/gorilla/websocket/AUTHORS +++ /dev/null @@ -1,8 +0,0 @@ -# This is the official list of Gorilla WebSocket authors for copyright -# purposes. -# -# Please keep the list sorted. - -Gary Burd -Joachim Bauch - diff --git a/vendor/github.com/gorilla/websocket/LICENSE b/vendor/github.com/gorilla/websocket/LICENSE deleted file mode 100644 index 9171c9722..000000000 --- a/vendor/github.com/gorilla/websocket/LICENSE +++ /dev/null @@ -1,22 +0,0 @@ -Copyright (c) 2013 The Gorilla WebSocket Authors. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - - Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - - Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/github.com/gorilla/websocket/README.md b/vendor/github.com/gorilla/websocket/README.md deleted file mode 100644 index 33c3d2be3..000000000 --- a/vendor/github.com/gorilla/websocket/README.md +++ /dev/null @@ -1,64 +0,0 @@ -# Gorilla WebSocket - -Gorilla WebSocket is a [Go](http://golang.org/) implementation of the -[WebSocket](http://www.rfc-editor.org/rfc/rfc6455.txt) protocol. - -[![Build Status](https://travis-ci.org/gorilla/websocket.svg?branch=master)](https://travis-ci.org/gorilla/websocket) -[![GoDoc](https://godoc.org/github.com/gorilla/websocket?status.svg)](https://godoc.org/github.com/gorilla/websocket) - -### Documentation - -* [API Reference](http://godoc.org/github.com/gorilla/websocket) -* [Chat example](https://github.com/gorilla/websocket/tree/master/examples/chat) -* [Command example](https://github.com/gorilla/websocket/tree/master/examples/command) -* [Client and server example](https://github.com/gorilla/websocket/tree/master/examples/echo) -* [File watch example](https://github.com/gorilla/websocket/tree/master/examples/filewatch) - -### Status - -The Gorilla WebSocket package provides a complete and tested implementation of -the [WebSocket](http://www.rfc-editor.org/rfc/rfc6455.txt) protocol. The -package API is stable. - -### Installation - - go get github.com/gorilla/websocket - -### Protocol Compliance - -The Gorilla WebSocket package passes the server tests in the [Autobahn Test -Suite](http://autobahn.ws/testsuite) using the application in the [examples/autobahn -subdirectory](https://github.com/gorilla/websocket/tree/master/examples/autobahn). - -### Gorilla WebSocket compared with other packages - - - - - - - - - - - - - - - - - - -
github.com/gorillagolang.org/x/net
RFC 6455 Features
Passes Autobahn Test SuiteYesNo
Receive fragmented messageYesNo, see note 1
Send close messageYesNo
Send pings and receive pongsYesNo
Get the type of a received data messageYesYes, see note 2
Other Features
Compression ExtensionsExperimentalNo
Read message using io.ReaderYesNo, see note 3
Write message using io.WriteCloserYesNo, see note 3
- -Notes: - -1. Large messages are fragmented in [Chrome's new WebSocket implementation](http://www.ietf.org/mail-archive/web/hybi/current/msg10503.html). -2. The application can get the type of a received data message by implementing - a [Codec marshal](http://godoc.org/golang.org/x/net/websocket#Codec.Marshal) - function. -3. The go.net io.Reader and io.Writer operate across WebSocket frame boundaries. - Read returns when the input buffer is full or a frame boundary is - encountered. Each call to Write sends a single frame message. The Gorilla - io.Reader and io.WriteCloser operate on a single WebSocket message. - diff --git a/vendor/github.com/gorilla/websocket/client.go b/vendor/github.com/gorilla/websocket/client.go deleted file mode 100644 index 43a87c753..000000000 --- a/vendor/github.com/gorilla/websocket/client.go +++ /dev/null @@ -1,392 +0,0 @@ -// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package websocket - -import ( - "bufio" - "bytes" - "crypto/tls" - "encoding/base64" - "errors" - "io" - "io/ioutil" - "net" - "net/http" - "net/url" - "strings" - "time" -) - -// ErrBadHandshake is returned when the server response to opening handshake is -// invalid. -var ErrBadHandshake = errors.New("websocket: bad handshake") - -var errInvalidCompression = errors.New("websocket: invalid compression negotiation") - -// NewClient creates a new client connection using the given net connection. -// The URL u specifies the host and request URI. Use requestHeader to specify -// the origin (Origin), subprotocols (Sec-WebSocket-Protocol) and cookies -// (Cookie). Use the response.Header to get the selected subprotocol -// (Sec-WebSocket-Protocol) and cookies (Set-Cookie). -// -// If the WebSocket handshake fails, ErrBadHandshake is returned along with a -// non-nil *http.Response so that callers can handle redirects, authentication, -// etc. -// -// Deprecated: Use Dialer instead. -func NewClient(netConn net.Conn, u *url.URL, requestHeader http.Header, readBufSize, writeBufSize int) (c *Conn, response *http.Response, err error) { - d := Dialer{ - ReadBufferSize: readBufSize, - WriteBufferSize: writeBufSize, - NetDial: func(net, addr string) (net.Conn, error) { - return netConn, nil - }, - } - return d.Dial(u.String(), requestHeader) -} - -// A Dialer contains options for connecting to WebSocket server. -type Dialer struct { - // NetDial specifies the dial function for creating TCP connections. If - // NetDial is nil, net.Dial is used. - NetDial func(network, addr string) (net.Conn, error) - - // Proxy specifies a function to return a proxy for a given - // Request. If the function returns a non-nil error, the - // request is aborted with the provided error. - // If Proxy is nil or returns a nil *URL, no proxy is used. - Proxy func(*http.Request) (*url.URL, error) - - // TLSClientConfig specifies the TLS configuration to use with tls.Client. - // If nil, the default configuration is used. - TLSClientConfig *tls.Config - - // HandshakeTimeout specifies the duration for the handshake to complete. - HandshakeTimeout time.Duration - - // ReadBufferSize and WriteBufferSize specify I/O buffer sizes. If a buffer - // size is zero, then a useful default size is used. The I/O buffer sizes - // do not limit the size of the messages that can be sent or received. - ReadBufferSize, WriteBufferSize int - - // Subprotocols specifies the client's requested subprotocols. - Subprotocols []string - - // EnableCompression specifies if the client should attempt to negotiate - // per message compression (RFC 7692). Setting this value to true does not - // guarantee that compression will be supported. Currently only "no context - // takeover" modes are supported. - EnableCompression bool - - // Jar specifies the cookie jar. - // If Jar is nil, cookies are not sent in requests and ignored - // in responses. - Jar http.CookieJar -} - -var errMalformedURL = errors.New("malformed ws or wss URL") - -// parseURL parses the URL. -// -// This function is a replacement for the standard library url.Parse function. -// In Go 1.4 and earlier, url.Parse loses information from the path. -func parseURL(s string) (*url.URL, error) { - // From the RFC: - // - // ws-URI = "ws:" "//" host [ ":" port ] path [ "?" query ] - // wss-URI = "wss:" "//" host [ ":" port ] path [ "?" query ] - var u url.URL - switch { - case strings.HasPrefix(s, "ws://"): - u.Scheme = "ws" - s = s[len("ws://"):] - case strings.HasPrefix(s, "wss://"): - u.Scheme = "wss" - s = s[len("wss://"):] - default: - return nil, errMalformedURL - } - - if i := strings.Index(s, "?"); i >= 0 { - u.RawQuery = s[i+1:] - s = s[:i] - } - - if i := strings.Index(s, "/"); i >= 0 { - u.Opaque = s[i:] - s = s[:i] - } else { - u.Opaque = "/" - } - - u.Host = s - - if strings.Contains(u.Host, "@") { - // Don't bother parsing user information because user information is - // not allowed in websocket URIs. - return nil, errMalformedURL - } - - return &u, nil -} - -func hostPortNoPort(u *url.URL) (hostPort, hostNoPort string) { - hostPort = u.Host - hostNoPort = u.Host - if i := strings.LastIndex(u.Host, ":"); i > strings.LastIndex(u.Host, "]") { - hostNoPort = hostNoPort[:i] - } else { - switch u.Scheme { - case "wss": - hostPort += ":443" - case "https": - hostPort += ":443" - default: - hostPort += ":80" - } - } - return hostPort, hostNoPort -} - -// DefaultDialer is a dialer with all fields set to the default zero values. -var DefaultDialer = &Dialer{ - Proxy: http.ProxyFromEnvironment, -} - -// Dial creates a new client connection. Use requestHeader to specify the -// origin (Origin), subprotocols (Sec-WebSocket-Protocol) and cookies (Cookie). -// Use the response.Header to get the selected subprotocol -// (Sec-WebSocket-Protocol) and cookies (Set-Cookie). -// -// If the WebSocket handshake fails, ErrBadHandshake is returned along with a -// non-nil *http.Response so that callers can handle redirects, authentication, -// etcetera. The response body may not contain the entire response and does not -// need to be closed by the application. -func (d *Dialer) Dial(urlStr string, requestHeader http.Header) (*Conn, *http.Response, error) { - - if d == nil { - d = &Dialer{ - Proxy: http.ProxyFromEnvironment, - } - } - - challengeKey, err := generateChallengeKey() - if err != nil { - return nil, nil, err - } - - u, err := parseURL(urlStr) - if err != nil { - return nil, nil, err - } - - switch u.Scheme { - case "ws": - u.Scheme = "http" - case "wss": - u.Scheme = "https" - default: - return nil, nil, errMalformedURL - } - - if u.User != nil { - // User name and password are not allowed in websocket URIs. - return nil, nil, errMalformedURL - } - - req := &http.Request{ - Method: "GET", - URL: u, - Proto: "HTTP/1.1", - ProtoMajor: 1, - ProtoMinor: 1, - Header: make(http.Header), - Host: u.Host, - } - - // Set the cookies present in the cookie jar of the dialer - if d.Jar != nil { - for _, cookie := range d.Jar.Cookies(u) { - req.AddCookie(cookie) - } - } - - // Set the request headers using the capitalization for names and values in - // RFC examples. Although the capitalization shouldn't matter, there are - // servers that depend on it. The Header.Set method is not used because the - // method canonicalizes the header names. - req.Header["Upgrade"] = []string{"websocket"} - req.Header["Connection"] = []string{"Upgrade"} - req.Header["Sec-WebSocket-Key"] = []string{challengeKey} - req.Header["Sec-WebSocket-Version"] = []string{"13"} - if len(d.Subprotocols) > 0 { - req.Header["Sec-WebSocket-Protocol"] = []string{strings.Join(d.Subprotocols, ", ")} - } - for k, vs := range requestHeader { - switch { - case k == "Host": - if len(vs) > 0 { - req.Host = vs[0] - } - case k == "Upgrade" || - k == "Connection" || - k == "Sec-Websocket-Key" || - k == "Sec-Websocket-Version" || - k == "Sec-Websocket-Extensions" || - (k == "Sec-Websocket-Protocol" && len(d.Subprotocols) > 0): - return nil, nil, errors.New("websocket: duplicate header not allowed: " + k) - default: - req.Header[k] = vs - } - } - - if d.EnableCompression { - req.Header.Set("Sec-Websocket-Extensions", "permessage-deflate; server_no_context_takeover; client_no_context_takeover") - } - - hostPort, hostNoPort := hostPortNoPort(u) - - var proxyURL *url.URL - // Check wether the proxy method has been configured - if d.Proxy != nil { - proxyURL, err = d.Proxy(req) - } - if err != nil { - return nil, nil, err - } - - var targetHostPort string - if proxyURL != nil { - targetHostPort, _ = hostPortNoPort(proxyURL) - } else { - targetHostPort = hostPort - } - - var deadline time.Time - if d.HandshakeTimeout != 0 { - deadline = time.Now().Add(d.HandshakeTimeout) - } - - netDial := d.NetDial - if netDial == nil { - netDialer := &net.Dialer{Deadline: deadline} - netDial = netDialer.Dial - } - - netConn, err := netDial("tcp", targetHostPort) - if err != nil { - return nil, nil, err - } - - defer func() { - if netConn != nil { - netConn.Close() - } - }() - - if err := netConn.SetDeadline(deadline); err != nil { - return nil, nil, err - } - - if proxyURL != nil { - connectHeader := make(http.Header) - if user := proxyURL.User; user != nil { - proxyUser := user.Username() - if proxyPassword, passwordSet := user.Password(); passwordSet { - credential := base64.StdEncoding.EncodeToString([]byte(proxyUser + ":" + proxyPassword)) - connectHeader.Set("Proxy-Authorization", "Basic "+credential) - } - } - connectReq := &http.Request{ - Method: "CONNECT", - URL: &url.URL{Opaque: hostPort}, - Host: hostPort, - Header: connectHeader, - } - - connectReq.Write(netConn) - - // Read response. - // Okay to use and discard buffered reader here, because - // TLS server will not speak until spoken to. - br := bufio.NewReader(netConn) - resp, err := http.ReadResponse(br, connectReq) - if err != nil { - return nil, nil, err - } - if resp.StatusCode != 200 { - f := strings.SplitN(resp.Status, " ", 2) - return nil, nil, errors.New(f[1]) - } - } - - if u.Scheme == "https" { - cfg := cloneTLSConfig(d.TLSClientConfig) - if cfg.ServerName == "" { - cfg.ServerName = hostNoPort - } - tlsConn := tls.Client(netConn, cfg) - netConn = tlsConn - if err := tlsConn.Handshake(); err != nil { - return nil, nil, err - } - if !cfg.InsecureSkipVerify { - if err := tlsConn.VerifyHostname(cfg.ServerName); err != nil { - return nil, nil, err - } - } - } - - conn := newConn(netConn, false, d.ReadBufferSize, d.WriteBufferSize) - - if err := req.Write(netConn); err != nil { - return nil, nil, err - } - - resp, err := http.ReadResponse(conn.br, req) - if err != nil { - return nil, nil, err - } - - if d.Jar != nil { - if rc := resp.Cookies(); len(rc) > 0 { - d.Jar.SetCookies(u, rc) - } - } - - if resp.StatusCode != 101 || - !strings.EqualFold(resp.Header.Get("Upgrade"), "websocket") || - !strings.EqualFold(resp.Header.Get("Connection"), "upgrade") || - resp.Header.Get("Sec-Websocket-Accept") != computeAcceptKey(challengeKey) { - // Before closing the network connection on return from this - // function, slurp up some of the response to aid application - // debugging. - buf := make([]byte, 1024) - n, _ := io.ReadFull(resp.Body, buf) - resp.Body = ioutil.NopCloser(bytes.NewReader(buf[:n])) - return nil, resp, ErrBadHandshake - } - - for _, ext := range parseExtensions(resp.Header) { - if ext[""] != "permessage-deflate" { - continue - } - _, snct := ext["server_no_context_takeover"] - _, cnct := ext["client_no_context_takeover"] - if !snct || !cnct { - return nil, resp, errInvalidCompression - } - conn.newCompressionWriter = compressNoContextTakeover - conn.newDecompressionReader = decompressNoContextTakeover - break - } - - resp.Body = ioutil.NopCloser(bytes.NewReader([]byte{})) - conn.subprotocol = resp.Header.Get("Sec-Websocket-Protocol") - - netConn.SetDeadline(time.Time{}) - netConn = nil // to avoid close in defer. - return conn, resp, nil -} diff --git a/vendor/github.com/gorilla/websocket/client_clone.go b/vendor/github.com/gorilla/websocket/client_clone.go deleted file mode 100644 index 4f0d94372..000000000 --- a/vendor/github.com/gorilla/websocket/client_clone.go +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build go1.8 - -package websocket - -import "crypto/tls" - -func cloneTLSConfig(cfg *tls.Config) *tls.Config { - if cfg == nil { - return &tls.Config{} - } - return cfg.Clone() -} diff --git a/vendor/github.com/gorilla/websocket/client_clone_legacy.go b/vendor/github.com/gorilla/websocket/client_clone_legacy.go deleted file mode 100644 index babb007fb..000000000 --- a/vendor/github.com/gorilla/websocket/client_clone_legacy.go +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build !go1.8 - -package websocket - -import "crypto/tls" - -// cloneTLSConfig clones all public fields except the fields -// SessionTicketsDisabled and SessionTicketKey. This avoids copying the -// sync.Mutex in the sync.Once and makes it safe to call cloneTLSConfig on a -// config in active use. -func cloneTLSConfig(cfg *tls.Config) *tls.Config { - if cfg == nil { - return &tls.Config{} - } - return &tls.Config{ - Rand: cfg.Rand, - Time: cfg.Time, - Certificates: cfg.Certificates, - NameToCertificate: cfg.NameToCertificate, - GetCertificate: cfg.GetCertificate, - RootCAs: cfg.RootCAs, - NextProtos: cfg.NextProtos, - ServerName: cfg.ServerName, - ClientAuth: cfg.ClientAuth, - ClientCAs: cfg.ClientCAs, - InsecureSkipVerify: cfg.InsecureSkipVerify, - CipherSuites: cfg.CipherSuites, - PreferServerCipherSuites: cfg.PreferServerCipherSuites, - ClientSessionCache: cfg.ClientSessionCache, - MinVersion: cfg.MinVersion, - MaxVersion: cfg.MaxVersion, - CurvePreferences: cfg.CurvePreferences, - } -} diff --git a/vendor/github.com/gorilla/websocket/compression.go b/vendor/github.com/gorilla/websocket/compression.go deleted file mode 100644 index 813ffb1e8..000000000 --- a/vendor/github.com/gorilla/websocket/compression.go +++ /dev/null @@ -1,148 +0,0 @@ -// Copyright 2017 The Gorilla WebSocket Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package websocket - -import ( - "compress/flate" - "errors" - "io" - "strings" - "sync" -) - -const ( - minCompressionLevel = -2 // flate.HuffmanOnly not defined in Go < 1.6 - maxCompressionLevel = flate.BestCompression - defaultCompressionLevel = 1 -) - -var ( - flateWriterPools [maxCompressionLevel - minCompressionLevel + 1]sync.Pool - flateReaderPool = sync.Pool{New: func() interface{} { - return flate.NewReader(nil) - }} -) - -func decompressNoContextTakeover(r io.Reader) io.ReadCloser { - const tail = - // Add four bytes as specified in RFC - "\x00\x00\xff\xff" + - // Add final block to squelch unexpected EOF error from flate reader. - "\x01\x00\x00\xff\xff" - - fr, _ := flateReaderPool.Get().(io.ReadCloser) - fr.(flate.Resetter).Reset(io.MultiReader(r, strings.NewReader(tail)), nil) - return &flateReadWrapper{fr} -} - -func isValidCompressionLevel(level int) bool { - return minCompressionLevel <= level && level <= maxCompressionLevel -} - -func compressNoContextTakeover(w io.WriteCloser, level int) io.WriteCloser { - p := &flateWriterPools[level-minCompressionLevel] - tw := &truncWriter{w: w} - fw, _ := p.Get().(*flate.Writer) - if fw == nil { - fw, _ = flate.NewWriter(tw, level) - } else { - fw.Reset(tw) - } - return &flateWriteWrapper{fw: fw, tw: tw, p: p} -} - -// truncWriter is an io.Writer that writes all but the last four bytes of the -// stream to another io.Writer. -type truncWriter struct { - w io.WriteCloser - n int - p [4]byte -} - -func (w *truncWriter) Write(p []byte) (int, error) { - n := 0 - - // fill buffer first for simplicity. - if w.n < len(w.p) { - n = copy(w.p[w.n:], p) - p = p[n:] - w.n += n - if len(p) == 0 { - return n, nil - } - } - - m := len(p) - if m > len(w.p) { - m = len(w.p) - } - - if nn, err := w.w.Write(w.p[:m]); err != nil { - return n + nn, err - } - - copy(w.p[:], w.p[m:]) - copy(w.p[len(w.p)-m:], p[len(p)-m:]) - nn, err := w.w.Write(p[:len(p)-m]) - return n + nn, err -} - -type flateWriteWrapper struct { - fw *flate.Writer - tw *truncWriter - p *sync.Pool -} - -func (w *flateWriteWrapper) Write(p []byte) (int, error) { - if w.fw == nil { - return 0, errWriteClosed - } - return w.fw.Write(p) -} - -func (w *flateWriteWrapper) Close() error { - if w.fw == nil { - return errWriteClosed - } - err1 := w.fw.Flush() - w.p.Put(w.fw) - w.fw = nil - if w.tw.p != [4]byte{0, 0, 0xff, 0xff} { - return errors.New("websocket: internal error, unexpected bytes at end of flate stream") - } - err2 := w.tw.w.Close() - if err1 != nil { - return err1 - } - return err2 -} - -type flateReadWrapper struct { - fr io.ReadCloser -} - -func (r *flateReadWrapper) Read(p []byte) (int, error) { - if r.fr == nil { - return 0, io.ErrClosedPipe - } - n, err := r.fr.Read(p) - if err == io.EOF { - // Preemptively place the reader back in the pool. This helps with - // scenarios where the application does not call NextReader() soon after - // this final read. - r.Close() - } - return n, err -} - -func (r *flateReadWrapper) Close() error { - if r.fr == nil { - return io.ErrClosedPipe - } - err := r.fr.Close() - flateReaderPool.Put(r.fr) - r.fr = nil - return err -} diff --git a/vendor/github.com/gorilla/websocket/conn.go b/vendor/github.com/gorilla/websocket/conn.go deleted file mode 100644 index 97e1dbacb..000000000 --- a/vendor/github.com/gorilla/websocket/conn.go +++ /dev/null @@ -1,1149 +0,0 @@ -// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package websocket - -import ( - "bufio" - "encoding/binary" - "errors" - "io" - "io/ioutil" - "math/rand" - "net" - "strconv" - "sync" - "time" - "unicode/utf8" -) - -const ( - // Frame header byte 0 bits from Section 5.2 of RFC 6455 - finalBit = 1 << 7 - rsv1Bit = 1 << 6 - rsv2Bit = 1 << 5 - rsv3Bit = 1 << 4 - - // Frame header byte 1 bits from Section 5.2 of RFC 6455 - maskBit = 1 << 7 - - maxFrameHeaderSize = 2 + 8 + 4 // Fixed header + length + mask - maxControlFramePayloadSize = 125 - - writeWait = time.Second - - defaultReadBufferSize = 4096 - defaultWriteBufferSize = 4096 - - continuationFrame = 0 - noFrame = -1 -) - -// Close codes defined in RFC 6455, section 11.7. -const ( - CloseNormalClosure = 1000 - CloseGoingAway = 1001 - CloseProtocolError = 1002 - CloseUnsupportedData = 1003 - CloseNoStatusReceived = 1005 - CloseAbnormalClosure = 1006 - CloseInvalidFramePayloadData = 1007 - ClosePolicyViolation = 1008 - CloseMessageTooBig = 1009 - CloseMandatoryExtension = 1010 - CloseInternalServerErr = 1011 - CloseServiceRestart = 1012 - CloseTryAgainLater = 1013 - CloseTLSHandshake = 1015 -) - -// The message types are defined in RFC 6455, section 11.8. -const ( - // TextMessage denotes a text data message. The text message payload is - // interpreted as UTF-8 encoded text data. - TextMessage = 1 - - // BinaryMessage denotes a binary data message. - BinaryMessage = 2 - - // CloseMessage denotes a close control message. The optional message - // payload contains a numeric code and text. Use the FormatCloseMessage - // function to format a close message payload. - CloseMessage = 8 - - // PingMessage denotes a ping control message. The optional message payload - // is UTF-8 encoded text. - PingMessage = 9 - - // PongMessage denotes a ping control message. The optional message payload - // is UTF-8 encoded text. - PongMessage = 10 -) - -// ErrCloseSent is returned when the application writes a message to the -// connection after sending a close message. -var ErrCloseSent = errors.New("websocket: close sent") - -// ErrReadLimit is returned when reading a message that is larger than the -// read limit set for the connection. -var ErrReadLimit = errors.New("websocket: read limit exceeded") - -// netError satisfies the net Error interface. -type netError struct { - msg string - temporary bool - timeout bool -} - -func (e *netError) Error() string { return e.msg } -func (e *netError) Temporary() bool { return e.temporary } -func (e *netError) Timeout() bool { return e.timeout } - -// CloseError represents close frame. -type CloseError struct { - - // Code is defined in RFC 6455, section 11.7. - Code int - - // Text is the optional text payload. - Text string -} - -func (e *CloseError) Error() string { - s := []byte("websocket: close ") - s = strconv.AppendInt(s, int64(e.Code), 10) - switch e.Code { - case CloseNormalClosure: - s = append(s, " (normal)"...) - case CloseGoingAway: - s = append(s, " (going away)"...) - case CloseProtocolError: - s = append(s, " (protocol error)"...) - case CloseUnsupportedData: - s = append(s, " (unsupported data)"...) - case CloseNoStatusReceived: - s = append(s, " (no status)"...) - case CloseAbnormalClosure: - s = append(s, " (abnormal closure)"...) - case CloseInvalidFramePayloadData: - s = append(s, " (invalid payload data)"...) - case ClosePolicyViolation: - s = append(s, " (policy violation)"...) - case CloseMessageTooBig: - s = append(s, " (message too big)"...) - case CloseMandatoryExtension: - s = append(s, " (mandatory extension missing)"...) - case CloseInternalServerErr: - s = append(s, " (internal server error)"...) - case CloseTLSHandshake: - s = append(s, " (TLS handshake error)"...) - } - if e.Text != "" { - s = append(s, ": "...) - s = append(s, e.Text...) - } - return string(s) -} - -// IsCloseError returns boolean indicating whether the error is a *CloseError -// with one of the specified codes. -func IsCloseError(err error, codes ...int) bool { - if e, ok := err.(*CloseError); ok { - for _, code := range codes { - if e.Code == code { - return true - } - } - } - return false -} - -// IsUnexpectedCloseError returns boolean indicating whether the error is a -// *CloseError with a code not in the list of expected codes. -func IsUnexpectedCloseError(err error, expectedCodes ...int) bool { - if e, ok := err.(*CloseError); ok { - for _, code := range expectedCodes { - if e.Code == code { - return false - } - } - return true - } - return false -} - -var ( - errWriteTimeout = &netError{msg: "websocket: write timeout", timeout: true, temporary: true} - errUnexpectedEOF = &CloseError{Code: CloseAbnormalClosure, Text: io.ErrUnexpectedEOF.Error()} - errBadWriteOpCode = errors.New("websocket: bad write message type") - errWriteClosed = errors.New("websocket: write closed") - errInvalidControlFrame = errors.New("websocket: invalid control frame") -) - -func newMaskKey() [4]byte { - n := rand.Uint32() - return [4]byte{byte(n), byte(n >> 8), byte(n >> 16), byte(n >> 24)} -} - -func hideTempErr(err error) error { - if e, ok := err.(net.Error); ok && e.Temporary() { - err = &netError{msg: e.Error(), timeout: e.Timeout()} - } - return err -} - -func isControl(frameType int) bool { - return frameType == CloseMessage || frameType == PingMessage || frameType == PongMessage -} - -func isData(frameType int) bool { - return frameType == TextMessage || frameType == BinaryMessage -} - -var validReceivedCloseCodes = map[int]bool{ - // see http://www.iana.org/assignments/websocket/websocket.xhtml#close-code-number - - CloseNormalClosure: true, - CloseGoingAway: true, - CloseProtocolError: true, - CloseUnsupportedData: true, - CloseNoStatusReceived: false, - CloseAbnormalClosure: false, - CloseInvalidFramePayloadData: true, - ClosePolicyViolation: true, - CloseMessageTooBig: true, - CloseMandatoryExtension: true, - CloseInternalServerErr: true, - CloseServiceRestart: true, - CloseTryAgainLater: true, - CloseTLSHandshake: false, -} - -func isValidReceivedCloseCode(code int) bool { - return validReceivedCloseCodes[code] || (code >= 3000 && code <= 4999) -} - -// The Conn type represents a WebSocket connection. -type Conn struct { - conn net.Conn - isServer bool - subprotocol string - - // Write fields - mu chan bool // used as mutex to protect write to conn - writeBuf []byte // frame is constructed in this buffer. - writeDeadline time.Time - writer io.WriteCloser // the current writer returned to the application - isWriting bool // for best-effort concurrent write detection - - writeErrMu sync.Mutex - writeErr error - - enableWriteCompression bool - compressionLevel int - newCompressionWriter func(io.WriteCloser, int) io.WriteCloser - - // Read fields - reader io.ReadCloser // the current reader returned to the application - readErr error - br *bufio.Reader - readRemaining int64 // bytes remaining in current frame. - readFinal bool // true the current message has more frames. - readLength int64 // Message size. - readLimit int64 // Maximum message size. - readMaskPos int - readMaskKey [4]byte - handlePong func(string) error - handlePing func(string) error - handleClose func(int, string) error - readErrCount int - messageReader *messageReader // the current low-level reader - - readDecompress bool // whether last read frame had RSV1 set - newDecompressionReader func(io.Reader) io.ReadCloser -} - -func newConn(conn net.Conn, isServer bool, readBufferSize, writeBufferSize int) *Conn { - return newConnBRW(conn, isServer, readBufferSize, writeBufferSize, nil) -} - -type writeHook struct { - p []byte -} - -func (wh *writeHook) Write(p []byte) (int, error) { - wh.p = p - return len(p), nil -} - -func newConnBRW(conn net.Conn, isServer bool, readBufferSize, writeBufferSize int, brw *bufio.ReadWriter) *Conn { - mu := make(chan bool, 1) - mu <- true - - var br *bufio.Reader - if readBufferSize == 0 && brw != nil && brw.Reader != nil { - // Reuse the supplied bufio.Reader if the buffer has a useful size. - // This code assumes that peek on a reader returns - // bufio.Reader.buf[:0]. - brw.Reader.Reset(conn) - if p, err := brw.Reader.Peek(0); err == nil && cap(p) >= 256 { - br = brw.Reader - } - } - if br == nil { - if readBufferSize == 0 { - readBufferSize = defaultReadBufferSize - } - if readBufferSize < maxControlFramePayloadSize { - readBufferSize = maxControlFramePayloadSize - } - br = bufio.NewReaderSize(conn, readBufferSize) - } - - var writeBuf []byte - if writeBufferSize == 0 && brw != nil && brw.Writer != nil { - // Use the bufio.Writer's buffer if the buffer has a useful size. This - // code assumes that bufio.Writer.buf[:1] is passed to the - // bufio.Writer's underlying writer. - var wh writeHook - brw.Writer.Reset(&wh) - brw.Writer.WriteByte(0) - brw.Flush() - if cap(wh.p) >= maxFrameHeaderSize+256 { - writeBuf = wh.p[:cap(wh.p)] - } - } - - if writeBuf == nil { - if writeBufferSize == 0 { - writeBufferSize = defaultWriteBufferSize - } - writeBuf = make([]byte, writeBufferSize+maxFrameHeaderSize) - } - - c := &Conn{ - isServer: isServer, - br: br, - conn: conn, - mu: mu, - readFinal: true, - writeBuf: writeBuf, - enableWriteCompression: true, - compressionLevel: defaultCompressionLevel, - } - c.SetCloseHandler(nil) - c.SetPingHandler(nil) - c.SetPongHandler(nil) - return c -} - -// Subprotocol returns the negotiated protocol for the connection. -func (c *Conn) Subprotocol() string { - return c.subprotocol -} - -// Close closes the underlying network connection without sending or waiting for a close frame. -func (c *Conn) Close() error { - return c.conn.Close() -} - -// LocalAddr returns the local network address. -func (c *Conn) LocalAddr() net.Addr { - return c.conn.LocalAddr() -} - -// RemoteAddr returns the remote network address. -func (c *Conn) RemoteAddr() net.Addr { - return c.conn.RemoteAddr() -} - -// Write methods - -func (c *Conn) writeFatal(err error) error { - err = hideTempErr(err) - c.writeErrMu.Lock() - if c.writeErr == nil { - c.writeErr = err - } - c.writeErrMu.Unlock() - return err -} - -func (c *Conn) write(frameType int, deadline time.Time, bufs ...[]byte) error { - <-c.mu - defer func() { c.mu <- true }() - - c.writeErrMu.Lock() - err := c.writeErr - c.writeErrMu.Unlock() - if err != nil { - return err - } - - c.conn.SetWriteDeadline(deadline) - for _, buf := range bufs { - if len(buf) > 0 { - _, err := c.conn.Write(buf) - if err != nil { - return c.writeFatal(err) - } - } - } - - if frameType == CloseMessage { - c.writeFatal(ErrCloseSent) - } - return nil -} - -// WriteControl writes a control message with the given deadline. The allowed -// message types are CloseMessage, PingMessage and PongMessage. -func (c *Conn) WriteControl(messageType int, data []byte, deadline time.Time) error { - if !isControl(messageType) { - return errBadWriteOpCode - } - if len(data) > maxControlFramePayloadSize { - return errInvalidControlFrame - } - - b0 := byte(messageType) | finalBit - b1 := byte(len(data)) - if !c.isServer { - b1 |= maskBit - } - - buf := make([]byte, 0, maxFrameHeaderSize+maxControlFramePayloadSize) - buf = append(buf, b0, b1) - - if c.isServer { - buf = append(buf, data...) - } else { - key := newMaskKey() - buf = append(buf, key[:]...) - buf = append(buf, data...) - maskBytes(key, 0, buf[6:]) - } - - d := time.Hour * 1000 - if !deadline.IsZero() { - d = deadline.Sub(time.Now()) - if d < 0 { - return errWriteTimeout - } - } - - timer := time.NewTimer(d) - select { - case <-c.mu: - timer.Stop() - case <-timer.C: - return errWriteTimeout - } - defer func() { c.mu <- true }() - - c.writeErrMu.Lock() - err := c.writeErr - c.writeErrMu.Unlock() - if err != nil { - return err - } - - c.conn.SetWriteDeadline(deadline) - _, err = c.conn.Write(buf) - if err != nil { - return c.writeFatal(err) - } - if messageType == CloseMessage { - c.writeFatal(ErrCloseSent) - } - return err -} - -func (c *Conn) prepWrite(messageType int) error { - // Close previous writer if not already closed by the application. It's - // probably better to return an error in this situation, but we cannot - // change this without breaking existing applications. - if c.writer != nil { - c.writer.Close() - c.writer = nil - } - - if !isControl(messageType) && !isData(messageType) { - return errBadWriteOpCode - } - - c.writeErrMu.Lock() - err := c.writeErr - c.writeErrMu.Unlock() - return err -} - -// NextWriter returns a writer for the next message to send. The writer's Close -// method flushes the complete message to the network. -// -// There can be at most one open writer on a connection. NextWriter closes the -// previous writer if the application has not already done so. -func (c *Conn) NextWriter(messageType int) (io.WriteCloser, error) { - if err := c.prepWrite(messageType); err != nil { - return nil, err - } - - mw := &messageWriter{ - c: c, - frameType: messageType, - pos: maxFrameHeaderSize, - } - c.writer = mw - if c.newCompressionWriter != nil && c.enableWriteCompression && isData(messageType) { - w := c.newCompressionWriter(c.writer, c.compressionLevel) - mw.compress = true - c.writer = w - } - return c.writer, nil -} - -type messageWriter struct { - c *Conn - compress bool // whether next call to flushFrame should set RSV1 - pos int // end of data in writeBuf. - frameType int // type of the current frame. - err error -} - -func (w *messageWriter) fatal(err error) error { - if w.err != nil { - w.err = err - w.c.writer = nil - } - return err -} - -// flushFrame writes buffered data and extra as a frame to the network. The -// final argument indicates that this is the last frame in the message. -func (w *messageWriter) flushFrame(final bool, extra []byte) error { - c := w.c - length := w.pos - maxFrameHeaderSize + len(extra) - - // Check for invalid control frames. - if isControl(w.frameType) && - (!final || length > maxControlFramePayloadSize) { - return w.fatal(errInvalidControlFrame) - } - - b0 := byte(w.frameType) - if final { - b0 |= finalBit - } - if w.compress { - b0 |= rsv1Bit - } - w.compress = false - - b1 := byte(0) - if !c.isServer { - b1 |= maskBit - } - - // Assume that the frame starts at beginning of c.writeBuf. - framePos := 0 - if c.isServer { - // Adjust up if mask not included in the header. - framePos = 4 - } - - switch { - case length >= 65536: - c.writeBuf[framePos] = b0 - c.writeBuf[framePos+1] = b1 | 127 - binary.BigEndian.PutUint64(c.writeBuf[framePos+2:], uint64(length)) - case length > 125: - framePos += 6 - c.writeBuf[framePos] = b0 - c.writeBuf[framePos+1] = b1 | 126 - binary.BigEndian.PutUint16(c.writeBuf[framePos+2:], uint16(length)) - default: - framePos += 8 - c.writeBuf[framePos] = b0 - c.writeBuf[framePos+1] = b1 | byte(length) - } - - if !c.isServer { - key := newMaskKey() - copy(c.writeBuf[maxFrameHeaderSize-4:], key[:]) - maskBytes(key, 0, c.writeBuf[maxFrameHeaderSize:w.pos]) - if len(extra) > 0 { - return c.writeFatal(errors.New("websocket: internal error, extra used in client mode")) - } - } - - // Write the buffers to the connection with best-effort detection of - // concurrent writes. See the concurrency section in the package - // documentation for more info. - - if c.isWriting { - panic("concurrent write to websocket connection") - } - c.isWriting = true - - err := c.write(w.frameType, c.writeDeadline, c.writeBuf[framePos:w.pos], extra) - - if !c.isWriting { - panic("concurrent write to websocket connection") - } - c.isWriting = false - - if err != nil { - return w.fatal(err) - } - - if final { - c.writer = nil - return nil - } - - // Setup for next frame. - w.pos = maxFrameHeaderSize - w.frameType = continuationFrame - return nil -} - -func (w *messageWriter) ncopy(max int) (int, error) { - n := len(w.c.writeBuf) - w.pos - if n <= 0 { - if err := w.flushFrame(false, nil); err != nil { - return 0, err - } - n = len(w.c.writeBuf) - w.pos - } - if n > max { - n = max - } - return n, nil -} - -func (w *messageWriter) Write(p []byte) (int, error) { - if w.err != nil { - return 0, w.err - } - - if len(p) > 2*len(w.c.writeBuf) && w.c.isServer { - // Don't buffer large messages. - err := w.flushFrame(false, p) - if err != nil { - return 0, err - } - return len(p), nil - } - - nn := len(p) - for len(p) > 0 { - n, err := w.ncopy(len(p)) - if err != nil { - return 0, err - } - copy(w.c.writeBuf[w.pos:], p[:n]) - w.pos += n - p = p[n:] - } - return nn, nil -} - -func (w *messageWriter) WriteString(p string) (int, error) { - if w.err != nil { - return 0, w.err - } - - nn := len(p) - for len(p) > 0 { - n, err := w.ncopy(len(p)) - if err != nil { - return 0, err - } - copy(w.c.writeBuf[w.pos:], p[:n]) - w.pos += n - p = p[n:] - } - return nn, nil -} - -func (w *messageWriter) ReadFrom(r io.Reader) (nn int64, err error) { - if w.err != nil { - return 0, w.err - } - for { - if w.pos == len(w.c.writeBuf) { - err = w.flushFrame(false, nil) - if err != nil { - break - } - } - var n int - n, err = r.Read(w.c.writeBuf[w.pos:]) - w.pos += n - nn += int64(n) - if err != nil { - if err == io.EOF { - err = nil - } - break - } - } - return nn, err -} - -func (w *messageWriter) Close() error { - if w.err != nil { - return w.err - } - if err := w.flushFrame(true, nil); err != nil { - return err - } - w.err = errWriteClosed - return nil -} - -// WritePreparedMessage writes prepared message into connection. -func (c *Conn) WritePreparedMessage(pm *PreparedMessage) error { - frameType, frameData, err := pm.frame(prepareKey{ - isServer: c.isServer, - compress: c.newCompressionWriter != nil && c.enableWriteCompression && isData(pm.messageType), - compressionLevel: c.compressionLevel, - }) - if err != nil { - return err - } - if c.isWriting { - panic("concurrent write to websocket connection") - } - c.isWriting = true - err = c.write(frameType, c.writeDeadline, frameData, nil) - if !c.isWriting { - panic("concurrent write to websocket connection") - } - c.isWriting = false - return err -} - -// WriteMessage is a helper method for getting a writer using NextWriter, -// writing the message and closing the writer. -func (c *Conn) WriteMessage(messageType int, data []byte) error { - - if c.isServer && (c.newCompressionWriter == nil || !c.enableWriteCompression) { - // Fast path with no allocations and single frame. - - if err := c.prepWrite(messageType); err != nil { - return err - } - mw := messageWriter{c: c, frameType: messageType, pos: maxFrameHeaderSize} - n := copy(c.writeBuf[mw.pos:], data) - mw.pos += n - data = data[n:] - return mw.flushFrame(true, data) - } - - w, err := c.NextWriter(messageType) - if err != nil { - return err - } - if _, err = w.Write(data); err != nil { - return err - } - return w.Close() -} - -// SetWriteDeadline sets the write deadline on the underlying network -// connection. After a write has timed out, the websocket state is corrupt and -// all future writes will return an error. A zero value for t means writes will -// not time out. -func (c *Conn) SetWriteDeadline(t time.Time) error { - c.writeDeadline = t - return nil -} - -// Read methods - -func (c *Conn) advanceFrame() (int, error) { - - // 1. Skip remainder of previous frame. - - if c.readRemaining > 0 { - if _, err := io.CopyN(ioutil.Discard, c.br, c.readRemaining); err != nil { - return noFrame, err - } - } - - // 2. Read and parse first two bytes of frame header. - - p, err := c.read(2) - if err != nil { - return noFrame, err - } - - final := p[0]&finalBit != 0 - frameType := int(p[0] & 0xf) - mask := p[1]&maskBit != 0 - c.readRemaining = int64(p[1] & 0x7f) - - c.readDecompress = false - if c.newDecompressionReader != nil && (p[0]&rsv1Bit) != 0 { - c.readDecompress = true - p[0] &^= rsv1Bit - } - - if rsv := p[0] & (rsv1Bit | rsv2Bit | rsv3Bit); rsv != 0 { - return noFrame, c.handleProtocolError("unexpected reserved bits 0x" + strconv.FormatInt(int64(rsv), 16)) - } - - switch frameType { - case CloseMessage, PingMessage, PongMessage: - if c.readRemaining > maxControlFramePayloadSize { - return noFrame, c.handleProtocolError("control frame length > 125") - } - if !final { - return noFrame, c.handleProtocolError("control frame not final") - } - case TextMessage, BinaryMessage: - if !c.readFinal { - return noFrame, c.handleProtocolError("message start before final message frame") - } - c.readFinal = final - case continuationFrame: - if c.readFinal { - return noFrame, c.handleProtocolError("continuation after final message frame") - } - c.readFinal = final - default: - return noFrame, c.handleProtocolError("unknown opcode " + strconv.Itoa(frameType)) - } - - // 3. Read and parse frame length. - - switch c.readRemaining { - case 126: - p, err := c.read(2) - if err != nil { - return noFrame, err - } - c.readRemaining = int64(binary.BigEndian.Uint16(p)) - case 127: - p, err := c.read(8) - if err != nil { - return noFrame, err - } - c.readRemaining = int64(binary.BigEndian.Uint64(p)) - } - - // 4. Handle frame masking. - - if mask != c.isServer { - return noFrame, c.handleProtocolError("incorrect mask flag") - } - - if mask { - c.readMaskPos = 0 - p, err := c.read(len(c.readMaskKey)) - if err != nil { - return noFrame, err - } - copy(c.readMaskKey[:], p) - } - - // 5. For text and binary messages, enforce read limit and return. - - if frameType == continuationFrame || frameType == TextMessage || frameType == BinaryMessage { - - c.readLength += c.readRemaining - if c.readLimit > 0 && c.readLength > c.readLimit { - c.WriteControl(CloseMessage, FormatCloseMessage(CloseMessageTooBig, ""), time.Now().Add(writeWait)) - return noFrame, ErrReadLimit - } - - return frameType, nil - } - - // 6. Read control frame payload. - - var payload []byte - if c.readRemaining > 0 { - payload, err = c.read(int(c.readRemaining)) - c.readRemaining = 0 - if err != nil { - return noFrame, err - } - if c.isServer { - maskBytes(c.readMaskKey, 0, payload) - } - } - - // 7. Process control frame payload. - - switch frameType { - case PongMessage: - if err := c.handlePong(string(payload)); err != nil { - return noFrame, err - } - case PingMessage: - if err := c.handlePing(string(payload)); err != nil { - return noFrame, err - } - case CloseMessage: - closeCode := CloseNoStatusReceived - closeText := "" - if len(payload) >= 2 { - closeCode = int(binary.BigEndian.Uint16(payload)) - if !isValidReceivedCloseCode(closeCode) { - return noFrame, c.handleProtocolError("invalid close code") - } - closeText = string(payload[2:]) - if !utf8.ValidString(closeText) { - return noFrame, c.handleProtocolError("invalid utf8 payload in close frame") - } - } - if err := c.handleClose(closeCode, closeText); err != nil { - return noFrame, err - } - return noFrame, &CloseError{Code: closeCode, Text: closeText} - } - - return frameType, nil -} - -func (c *Conn) handleProtocolError(message string) error { - c.WriteControl(CloseMessage, FormatCloseMessage(CloseProtocolError, message), time.Now().Add(writeWait)) - return errors.New("websocket: " + message) -} - -// NextReader returns the next data message received from the peer. The -// returned messageType is either TextMessage or BinaryMessage. -// -// There can be at most one open reader on a connection. NextReader discards -// the previous message if the application has not already consumed it. -// -// Applications must break out of the application's read loop when this method -// returns a non-nil error value. Errors returned from this method are -// permanent. Once this method returns a non-nil error, all subsequent calls to -// this method return the same error. -func (c *Conn) NextReader() (messageType int, r io.Reader, err error) { - // Close previous reader, only relevant for decompression. - if c.reader != nil { - c.reader.Close() - c.reader = nil - } - - c.messageReader = nil - c.readLength = 0 - - for c.readErr == nil { - frameType, err := c.advanceFrame() - if err != nil { - c.readErr = hideTempErr(err) - break - } - if frameType == TextMessage || frameType == BinaryMessage { - c.messageReader = &messageReader{c} - c.reader = c.messageReader - if c.readDecompress { - c.reader = c.newDecompressionReader(c.reader) - } - return frameType, c.reader, nil - } - } - - // Applications that do handle the error returned from this method spin in - // tight loop on connection failure. To help application developers detect - // this error, panic on repeated reads to the failed connection. - c.readErrCount++ - if c.readErrCount >= 1000 { - panic("repeated read on failed websocket connection") - } - - return noFrame, nil, c.readErr -} - -type messageReader struct{ c *Conn } - -func (r *messageReader) Read(b []byte) (int, error) { - c := r.c - if c.messageReader != r { - return 0, io.EOF - } - - for c.readErr == nil { - - if c.readRemaining > 0 { - if int64(len(b)) > c.readRemaining { - b = b[:c.readRemaining] - } - n, err := c.br.Read(b) - c.readErr = hideTempErr(err) - if c.isServer { - c.readMaskPos = maskBytes(c.readMaskKey, c.readMaskPos, b[:n]) - } - c.readRemaining -= int64(n) - if c.readRemaining > 0 && c.readErr == io.EOF { - c.readErr = errUnexpectedEOF - } - return n, c.readErr - } - - if c.readFinal { - c.messageReader = nil - return 0, io.EOF - } - - frameType, err := c.advanceFrame() - switch { - case err != nil: - c.readErr = hideTempErr(err) - case frameType == TextMessage || frameType == BinaryMessage: - c.readErr = errors.New("websocket: internal error, unexpected text or binary in Reader") - } - } - - err := c.readErr - if err == io.EOF && c.messageReader == r { - err = errUnexpectedEOF - } - return 0, err -} - -func (r *messageReader) Close() error { - return nil -} - -// ReadMessage is a helper method for getting a reader using NextReader and -// reading from that reader to a buffer. -func (c *Conn) ReadMessage() (messageType int, p []byte, err error) { - var r io.Reader - messageType, r, err = c.NextReader() - if err != nil { - return messageType, nil, err - } - p, err = ioutil.ReadAll(r) - return messageType, p, err -} - -// SetReadDeadline sets the read deadline on the underlying network connection. -// After a read has timed out, the websocket connection state is corrupt and -// all future reads will return an error. A zero value for t means reads will -// not time out. -func (c *Conn) SetReadDeadline(t time.Time) error { - return c.conn.SetReadDeadline(t) -} - -// SetReadLimit sets the maximum size for a message read from the peer. If a -// message exceeds the limit, the connection sends a close frame to the peer -// and returns ErrReadLimit to the application. -func (c *Conn) SetReadLimit(limit int64) { - c.readLimit = limit -} - -// CloseHandler returns the current close handler -func (c *Conn) CloseHandler() func(code int, text string) error { - return c.handleClose -} - -// SetCloseHandler sets the handler for close messages received from the peer. -// The code argument to h is the received close code or CloseNoStatusReceived -// if the close message is empty. The default close handler sends a close frame -// back to the peer. -// -// The application must read the connection to process close messages as -// described in the section on Control Frames above. -// -// The connection read methods return a CloseError when a close frame is -// received. Most applications should handle close messages as part of their -// normal error handling. Applications should only set a close handler when the -// application must perform some action before sending a close frame back to -// the peer. -func (c *Conn) SetCloseHandler(h func(code int, text string) error) { - if h == nil { - h = func(code int, text string) error { - message := []byte{} - if code != CloseNoStatusReceived { - message = FormatCloseMessage(code, "") - } - c.WriteControl(CloseMessage, message, time.Now().Add(writeWait)) - return nil - } - } - c.handleClose = h -} - -// PingHandler returns the current ping handler -func (c *Conn) PingHandler() func(appData string) error { - return c.handlePing -} - -// SetPingHandler sets the handler for ping messages received from the peer. -// The appData argument to h is the PING frame application data. The default -// ping handler sends a pong to the peer. -// -// The application must read the connection to process ping messages as -// described in the section on Control Frames above. -func (c *Conn) SetPingHandler(h func(appData string) error) { - if h == nil { - h = func(message string) error { - err := c.WriteControl(PongMessage, []byte(message), time.Now().Add(writeWait)) - if err == ErrCloseSent { - return nil - } else if e, ok := err.(net.Error); ok && e.Temporary() { - return nil - } - return err - } - } - c.handlePing = h -} - -// PongHandler returns the current pong handler -func (c *Conn) PongHandler() func(appData string) error { - return c.handlePong -} - -// SetPongHandler sets the handler for pong messages received from the peer. -// The appData argument to h is the PONG frame application data. The default -// pong handler does nothing. -// -// The application must read the connection to process ping messages as -// described in the section on Control Frames above. -func (c *Conn) SetPongHandler(h func(appData string) error) { - if h == nil { - h = func(string) error { return nil } - } - c.handlePong = h -} - -// UnderlyingConn returns the internal net.Conn. This can be used to further -// modifications to connection specific flags. -func (c *Conn) UnderlyingConn() net.Conn { - return c.conn -} - -// EnableWriteCompression enables and disables write compression of -// subsequent text and binary messages. This function is a noop if -// compression was not negotiated with the peer. -func (c *Conn) EnableWriteCompression(enable bool) { - c.enableWriteCompression = enable -} - -// SetCompressionLevel sets the flate compression level for subsequent text and -// binary messages. This function is a noop if compression was not negotiated -// with the peer. See the compress/flate package for a description of -// compression levels. -func (c *Conn) SetCompressionLevel(level int) error { - if !isValidCompressionLevel(level) { - return errors.New("websocket: invalid compression level") - } - c.compressionLevel = level - return nil -} - -// FormatCloseMessage formats closeCode and text as a WebSocket close message. -func FormatCloseMessage(closeCode int, text string) []byte { - buf := make([]byte, 2+len(text)) - binary.BigEndian.PutUint16(buf, uint16(closeCode)) - copy(buf[2:], text) - return buf -} diff --git a/vendor/github.com/gorilla/websocket/conn_read.go b/vendor/github.com/gorilla/websocket/conn_read.go deleted file mode 100644 index 1ea15059e..000000000 --- a/vendor/github.com/gorilla/websocket/conn_read.go +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright 2016 The Gorilla WebSocket Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build go1.5 - -package websocket - -import "io" - -func (c *Conn) read(n int) ([]byte, error) { - p, err := c.br.Peek(n) - if err == io.EOF { - err = errUnexpectedEOF - } - c.br.Discard(len(p)) - return p, err -} diff --git a/vendor/github.com/gorilla/websocket/conn_read_legacy.go b/vendor/github.com/gorilla/websocket/conn_read_legacy.go deleted file mode 100644 index 018541cf6..000000000 --- a/vendor/github.com/gorilla/websocket/conn_read_legacy.go +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright 2016 The Gorilla WebSocket Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build !go1.5 - -package websocket - -import "io" - -func (c *Conn) read(n int) ([]byte, error) { - p, err := c.br.Peek(n) - if err == io.EOF { - err = errUnexpectedEOF - } - if len(p) > 0 { - // advance over the bytes just read - io.ReadFull(c.br, p) - } - return p, err -} diff --git a/vendor/github.com/gorilla/websocket/doc.go b/vendor/github.com/gorilla/websocket/doc.go deleted file mode 100644 index e291a952c..000000000 --- a/vendor/github.com/gorilla/websocket/doc.go +++ /dev/null @@ -1,180 +0,0 @@ -// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Package websocket implements the WebSocket protocol defined in RFC 6455. -// -// Overview -// -// The Conn type represents a WebSocket connection. A server application uses -// the Upgrade function from an Upgrader object with a HTTP request handler -// to get a pointer to a Conn: -// -// var upgrader = websocket.Upgrader{ -// ReadBufferSize: 1024, -// WriteBufferSize: 1024, -// } -// -// func handler(w http.ResponseWriter, r *http.Request) { -// conn, err := upgrader.Upgrade(w, r, nil) -// if err != nil { -// log.Println(err) -// return -// } -// ... Use conn to send and receive messages. -// } -// -// Call the connection's WriteMessage and ReadMessage methods to send and -// receive messages as a slice of bytes. This snippet of code shows how to echo -// messages using these methods: -// -// for { -// messageType, p, err := conn.ReadMessage() -// if err != nil { -// return -// } -// if err = conn.WriteMessage(messageType, p); err != nil { -// return err -// } -// } -// -// In above snippet of code, p is a []byte and messageType is an int with value -// websocket.BinaryMessage or websocket.TextMessage. -// -// An application can also send and receive messages using the io.WriteCloser -// and io.Reader interfaces. To send a message, call the connection NextWriter -// method to get an io.WriteCloser, write the message to the writer and close -// the writer when done. To receive a message, call the connection NextReader -// method to get an io.Reader and read until io.EOF is returned. This snippet -// shows how to echo messages using the NextWriter and NextReader methods: -// -// for { -// messageType, r, err := conn.NextReader() -// if err != nil { -// return -// } -// w, err := conn.NextWriter(messageType) -// if err != nil { -// return err -// } -// if _, err := io.Copy(w, r); err != nil { -// return err -// } -// if err := w.Close(); err != nil { -// return err -// } -// } -// -// Data Messages -// -// The WebSocket protocol distinguishes between text and binary data messages. -// Text messages are interpreted as UTF-8 encoded text. The interpretation of -// binary messages is left to the application. -// -// This package uses the TextMessage and BinaryMessage integer constants to -// identify the two data message types. The ReadMessage and NextReader methods -// return the type of the received message. The messageType argument to the -// WriteMessage and NextWriter methods specifies the type of a sent message. -// -// It is the application's responsibility to ensure that text messages are -// valid UTF-8 encoded text. -// -// Control Messages -// -// The WebSocket protocol defines three types of control messages: close, ping -// and pong. Call the connection WriteControl, WriteMessage or NextWriter -// methods to send a control message to the peer. -// -// Connections handle received close messages by sending a close message to the -// peer and returning a *CloseError from the the NextReader, ReadMessage or the -// message Read method. -// -// Connections handle received ping and pong messages by invoking callback -// functions set with SetPingHandler and SetPongHandler methods. The callback -// functions are called from the NextReader, ReadMessage and the message Read -// methods. -// -// The default ping handler sends a pong to the peer. The application's reading -// goroutine can block for a short time while the handler writes the pong data -// to the connection. -// -// The application must read the connection to process ping, pong and close -// messages sent from the peer. If the application is not otherwise interested -// in messages from the peer, then the application should start a goroutine to -// read and discard messages from the peer. A simple example is: -// -// func readLoop(c *websocket.Conn) { -// for { -// if _, _, err := c.NextReader(); err != nil { -// c.Close() -// break -// } -// } -// } -// -// Concurrency -// -// Connections support one concurrent reader and one concurrent writer. -// -// Applications are responsible for ensuring that no more than one goroutine -// calls the write methods (NextWriter, SetWriteDeadline, WriteMessage, -// WriteJSON, EnableWriteCompression, SetCompressionLevel) concurrently and -// that no more than one goroutine calls the read methods (NextReader, -// SetReadDeadline, ReadMessage, ReadJSON, SetPongHandler, SetPingHandler) -// concurrently. -// -// The Close and WriteControl methods can be called concurrently with all other -// methods. -// -// Origin Considerations -// -// Web browsers allow Javascript applications to open a WebSocket connection to -// any host. It's up to the server to enforce an origin policy using the Origin -// request header sent by the browser. -// -// The Upgrader calls the function specified in the CheckOrigin field to check -// the origin. If the CheckOrigin function returns false, then the Upgrade -// method fails the WebSocket handshake with HTTP status 403. -// -// If the CheckOrigin field is nil, then the Upgrader uses a safe default: fail -// the handshake if the Origin request header is present and not equal to the -// Host request header. -// -// An application can allow connections from any origin by specifying a -// function that always returns true: -// -// var upgrader = websocket.Upgrader{ -// CheckOrigin: func(r *http.Request) bool { return true }, -// } -// -// The deprecated Upgrade function does not enforce an origin policy. It's the -// application's responsibility to check the Origin header before calling -// Upgrade. -// -// Compression EXPERIMENTAL -// -// Per message compression extensions (RFC 7692) are experimentally supported -// by this package in a limited capacity. Setting the EnableCompression option -// to true in Dialer or Upgrader will attempt to negotiate per message deflate -// support. -// -// var upgrader = websocket.Upgrader{ -// EnableCompression: true, -// } -// -// If compression was successfully negotiated with the connection's peer, any -// message received in compressed form will be automatically decompressed. -// All Read methods will return uncompressed bytes. -// -// Per message compression of messages written to a connection can be enabled -// or disabled by calling the corresponding Conn method: -// -// conn.EnableWriteCompression(false) -// -// Currently this package does not support compression with "context takeover". -// This means that messages must be compressed and decompressed in isolation, -// without retaining sliding window or dictionary state across messages. For -// more details refer to RFC 7692. -// -// Use of compression is experimental and may result in decreased performance. -package websocket diff --git a/vendor/github.com/gorilla/websocket/json.go b/vendor/github.com/gorilla/websocket/json.go deleted file mode 100644 index 4f0e36875..000000000 --- a/vendor/github.com/gorilla/websocket/json.go +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package websocket - -import ( - "encoding/json" - "io" -) - -// WriteJSON is deprecated, use c.WriteJSON instead. -func WriteJSON(c *Conn, v interface{}) error { - return c.WriteJSON(v) -} - -// WriteJSON writes the JSON encoding of v to the connection. -// -// See the documentation for encoding/json Marshal for details about the -// conversion of Go values to JSON. -func (c *Conn) WriteJSON(v interface{}) error { - w, err := c.NextWriter(TextMessage) - if err != nil { - return err - } - err1 := json.NewEncoder(w).Encode(v) - err2 := w.Close() - if err1 != nil { - return err1 - } - return err2 -} - -// ReadJSON is deprecated, use c.ReadJSON instead. -func ReadJSON(c *Conn, v interface{}) error { - return c.ReadJSON(v) -} - -// ReadJSON reads the next JSON-encoded message from the connection and stores -// it in the value pointed to by v. -// -// See the documentation for the encoding/json Unmarshal function for details -// about the conversion of JSON to a Go value. -func (c *Conn) ReadJSON(v interface{}) error { - _, r, err := c.NextReader() - if err != nil { - return err - } - err = json.NewDecoder(r).Decode(v) - if err == io.EOF { - // One value is expected in the message. - err = io.ErrUnexpectedEOF - } - return err -} diff --git a/vendor/github.com/gorilla/websocket/mask.go b/vendor/github.com/gorilla/websocket/mask.go deleted file mode 100644 index 6a88bbc74..000000000 --- a/vendor/github.com/gorilla/websocket/mask.go +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright 2016 The Gorilla WebSocket Authors. All rights reserved. Use of -// this source code is governed by a BSD-style license that can be found in the -// LICENSE file. - -// +build !appengine - -package websocket - -import "unsafe" - -const wordSize = int(unsafe.Sizeof(uintptr(0))) - -func maskBytes(key [4]byte, pos int, b []byte) int { - - // Mask one byte at a time for small buffers. - if len(b) < 2*wordSize { - for i := range b { - b[i] ^= key[pos&3] - pos++ - } - return pos & 3 - } - - // Mask one byte at a time to word boundary. - if n := int(uintptr(unsafe.Pointer(&b[0]))) % wordSize; n != 0 { - n = wordSize - n - for i := range b[:n] { - b[i] ^= key[pos&3] - pos++ - } - b = b[n:] - } - - // Create aligned word size key. - var k [wordSize]byte - for i := range k { - k[i] = key[(pos+i)&3] - } - kw := *(*uintptr)(unsafe.Pointer(&k)) - - // Mask one word at a time. - n := (len(b) / wordSize) * wordSize - for i := 0; i < n; i += wordSize { - *(*uintptr)(unsafe.Pointer(uintptr(unsafe.Pointer(&b[0])) + uintptr(i))) ^= kw - } - - // Mask one byte at a time for remaining bytes. - b = b[n:] - for i := range b { - b[i] ^= key[pos&3] - pos++ - } - - return pos & 3 -} diff --git a/vendor/github.com/gorilla/websocket/mask_safe.go b/vendor/github.com/gorilla/websocket/mask_safe.go deleted file mode 100644 index 2aac060e5..000000000 --- a/vendor/github.com/gorilla/websocket/mask_safe.go +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright 2016 The Gorilla WebSocket Authors. All rights reserved. Use of -// this source code is governed by a BSD-style license that can be found in the -// LICENSE file. - -// +build appengine - -package websocket - -func maskBytes(key [4]byte, pos int, b []byte) int { - for i := range b { - b[i] ^= key[pos&3] - pos++ - } - return pos & 3 -} diff --git a/vendor/github.com/gorilla/websocket/prepared.go b/vendor/github.com/gorilla/websocket/prepared.go deleted file mode 100644 index 1efffbd1e..000000000 --- a/vendor/github.com/gorilla/websocket/prepared.go +++ /dev/null @@ -1,103 +0,0 @@ -// Copyright 2017 The Gorilla WebSocket Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package websocket - -import ( - "bytes" - "net" - "sync" - "time" -) - -// PreparedMessage caches on the wire representations of a message payload. -// Use PreparedMessage to efficiently send a message payload to multiple -// connections. PreparedMessage is especially useful when compression is used -// because the CPU and memory expensive compression operation can be executed -// once for a given set of compression options. -type PreparedMessage struct { - messageType int - data []byte - err error - mu sync.Mutex - frames map[prepareKey]*preparedFrame -} - -// prepareKey defines a unique set of options to cache prepared frames in PreparedMessage. -type prepareKey struct { - isServer bool - compress bool - compressionLevel int -} - -// preparedFrame contains data in wire representation. -type preparedFrame struct { - once sync.Once - data []byte -} - -// NewPreparedMessage returns an initialized PreparedMessage. You can then send -// it to connection using WritePreparedMessage method. Valid wire -// representation will be calculated lazily only once for a set of current -// connection options. -func NewPreparedMessage(messageType int, data []byte) (*PreparedMessage, error) { - pm := &PreparedMessage{ - messageType: messageType, - frames: make(map[prepareKey]*preparedFrame), - data: data, - } - - // Prepare a plain server frame. - _, frameData, err := pm.frame(prepareKey{isServer: true, compress: false}) - if err != nil { - return nil, err - } - - // To protect against caller modifying the data argument, remember the data - // copied to the plain server frame. - pm.data = frameData[len(frameData)-len(data):] - return pm, nil -} - -func (pm *PreparedMessage) frame(key prepareKey) (int, []byte, error) { - pm.mu.Lock() - frame, ok := pm.frames[key] - if !ok { - frame = &preparedFrame{} - pm.frames[key] = frame - } - pm.mu.Unlock() - - var err error - frame.once.Do(func() { - // Prepare a frame using a 'fake' connection. - // TODO: Refactor code in conn.go to allow more direct construction of - // the frame. - mu := make(chan bool, 1) - mu <- true - var nc prepareConn - c := &Conn{ - conn: &nc, - mu: mu, - isServer: key.isServer, - compressionLevel: key.compressionLevel, - enableWriteCompression: true, - writeBuf: make([]byte, defaultWriteBufferSize+maxFrameHeaderSize), - } - if key.compress { - c.newCompressionWriter = compressNoContextTakeover - } - err = c.WriteMessage(pm.messageType, pm.data) - frame.data = nc.buf.Bytes() - }) - return pm.messageType, frame.data, err -} - -type prepareConn struct { - buf bytes.Buffer - net.Conn -} - -func (pc *prepareConn) Write(p []byte) (int, error) { return pc.buf.Write(p) } -func (pc *prepareConn) SetWriteDeadline(t time.Time) error { return nil } diff --git a/vendor/github.com/gorilla/websocket/server.go b/vendor/github.com/gorilla/websocket/server.go deleted file mode 100644 index 3495e0f1a..000000000 --- a/vendor/github.com/gorilla/websocket/server.go +++ /dev/null @@ -1,291 +0,0 @@ -// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package websocket - -import ( - "bufio" - "errors" - "net" - "net/http" - "net/url" - "strings" - "time" -) - -// HandshakeError describes an error with the handshake from the peer. -type HandshakeError struct { - message string -} - -func (e HandshakeError) Error() string { return e.message } - -// Upgrader specifies parameters for upgrading an HTTP connection to a -// WebSocket connection. -type Upgrader struct { - // HandshakeTimeout specifies the duration for the handshake to complete. - HandshakeTimeout time.Duration - - // ReadBufferSize and WriteBufferSize specify I/O buffer sizes. If a buffer - // size is zero, then buffers allocated by the HTTP server are used. The - // I/O buffer sizes do not limit the size of the messages that can be sent - // or received. - ReadBufferSize, WriteBufferSize int - - // Subprotocols specifies the server's supported protocols in order of - // preference. If this field is set, then the Upgrade method negotiates a - // subprotocol by selecting the first match in this list with a protocol - // requested by the client. - Subprotocols []string - - // Error specifies the function for generating HTTP error responses. If Error - // is nil, then http.Error is used to generate the HTTP response. - Error func(w http.ResponseWriter, r *http.Request, status int, reason error) - - // CheckOrigin returns true if the request Origin header is acceptable. If - // CheckOrigin is nil, the host in the Origin header must not be set or - // must match the host of the request. - CheckOrigin func(r *http.Request) bool - - // EnableCompression specify if the server should attempt to negotiate per - // message compression (RFC 7692). Setting this value to true does not - // guarantee that compression will be supported. Currently only "no context - // takeover" modes are supported. - EnableCompression bool -} - -func (u *Upgrader) returnError(w http.ResponseWriter, r *http.Request, status int, reason string) (*Conn, error) { - err := HandshakeError{reason} - if u.Error != nil { - u.Error(w, r, status, err) - } else { - w.Header().Set("Sec-Websocket-Version", "13") - http.Error(w, http.StatusText(status), status) - } - return nil, err -} - -// checkSameOrigin returns true if the origin is not set or is equal to the request host. -func checkSameOrigin(r *http.Request) bool { - origin := r.Header["Origin"] - if len(origin) == 0 { - return true - } - u, err := url.Parse(origin[0]) - if err != nil { - return false - } - return u.Host == r.Host -} - -func (u *Upgrader) selectSubprotocol(r *http.Request, responseHeader http.Header) string { - if u.Subprotocols != nil { - clientProtocols := Subprotocols(r) - for _, serverProtocol := range u.Subprotocols { - for _, clientProtocol := range clientProtocols { - if clientProtocol == serverProtocol { - return clientProtocol - } - } - } - } else if responseHeader != nil { - return responseHeader.Get("Sec-Websocket-Protocol") - } - return "" -} - -// Upgrade upgrades the HTTP server connection to the WebSocket protocol. -// -// The responseHeader is included in the response to the client's upgrade -// request. Use the responseHeader to specify cookies (Set-Cookie) and the -// application negotiated subprotocol (Sec-Websocket-Protocol). -// -// If the upgrade fails, then Upgrade replies to the client with an HTTP error -// response. -func (u *Upgrader) Upgrade(w http.ResponseWriter, r *http.Request, responseHeader http.Header) (*Conn, error) { - if r.Method != "GET" { - return u.returnError(w, r, http.StatusMethodNotAllowed, "websocket: not a websocket handshake: request method is not GET") - } - - if _, ok := responseHeader["Sec-Websocket-Extensions"]; ok { - return u.returnError(w, r, http.StatusInternalServerError, "websocket: application specific 'Sec-Websocket-Extensions' headers are unsupported") - } - - if !tokenListContainsValue(r.Header, "Connection", "upgrade") { - return u.returnError(w, r, http.StatusBadRequest, "websocket: not a websocket handshake: 'upgrade' token not found in 'Connection' header") - } - - if !tokenListContainsValue(r.Header, "Upgrade", "websocket") { - return u.returnError(w, r, http.StatusBadRequest, "websocket: not a websocket handshake: 'websocket' token not found in 'Upgrade' header") - } - - if !tokenListContainsValue(r.Header, "Sec-Websocket-Version", "13") { - return u.returnError(w, r, http.StatusBadRequest, "websocket: unsupported version: 13 not found in 'Sec-Websocket-Version' header") - } - - checkOrigin := u.CheckOrigin - if checkOrigin == nil { - checkOrigin = checkSameOrigin - } - if !checkOrigin(r) { - return u.returnError(w, r, http.StatusForbidden, "websocket: 'Origin' header value not allowed") - } - - challengeKey := r.Header.Get("Sec-Websocket-Key") - if challengeKey == "" { - return u.returnError(w, r, http.StatusBadRequest, "websocket: not a websocket handshake: `Sec-Websocket-Key' header is missing or blank") - } - - subprotocol := u.selectSubprotocol(r, responseHeader) - - // Negotiate PMCE - var compress bool - if u.EnableCompression { - for _, ext := range parseExtensions(r.Header) { - if ext[""] != "permessage-deflate" { - continue - } - compress = true - break - } - } - - var ( - netConn net.Conn - err error - ) - - h, ok := w.(http.Hijacker) - if !ok { - return u.returnError(w, r, http.StatusInternalServerError, "websocket: response does not implement http.Hijacker") - } - var brw *bufio.ReadWriter - netConn, brw, err = h.Hijack() - if err != nil { - return u.returnError(w, r, http.StatusInternalServerError, err.Error()) - } - - if brw.Reader.Buffered() > 0 { - netConn.Close() - return nil, errors.New("websocket: client sent data before handshake is complete") - } - - c := newConnBRW(netConn, true, u.ReadBufferSize, u.WriteBufferSize, brw) - c.subprotocol = subprotocol - - if compress { - c.newCompressionWriter = compressNoContextTakeover - c.newDecompressionReader = decompressNoContextTakeover - } - - p := c.writeBuf[:0] - p = append(p, "HTTP/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: "...) - p = append(p, computeAcceptKey(challengeKey)...) - p = append(p, "\r\n"...) - if c.subprotocol != "" { - p = append(p, "Sec-Websocket-Protocol: "...) - p = append(p, c.subprotocol...) - p = append(p, "\r\n"...) - } - if compress { - p = append(p, "Sec-Websocket-Extensions: permessage-deflate; server_no_context_takeover; client_no_context_takeover\r\n"...) - } - for k, vs := range responseHeader { - if k == "Sec-Websocket-Protocol" { - continue - } - for _, v := range vs { - p = append(p, k...) - p = append(p, ": "...) - for i := 0; i < len(v); i++ { - b := v[i] - if b <= 31 { - // prevent response splitting. - b = ' ' - } - p = append(p, b) - } - p = append(p, "\r\n"...) - } - } - p = append(p, "\r\n"...) - - // Clear deadlines set by HTTP server. - netConn.SetDeadline(time.Time{}) - - if u.HandshakeTimeout > 0 { - netConn.SetWriteDeadline(time.Now().Add(u.HandshakeTimeout)) - } - if _, err = netConn.Write(p); err != nil { - netConn.Close() - return nil, err - } - if u.HandshakeTimeout > 0 { - netConn.SetWriteDeadline(time.Time{}) - } - - return c, nil -} - -// Upgrade upgrades the HTTP server connection to the WebSocket protocol. -// -// This function is deprecated, use websocket.Upgrader instead. -// -// The application is responsible for checking the request origin before -// calling Upgrade. An example implementation of the same origin policy is: -// -// if req.Header.Get("Origin") != "http://"+req.Host { -// http.Error(w, "Origin not allowed", 403) -// return -// } -// -// If the endpoint supports subprotocols, then the application is responsible -// for negotiating the protocol used on the connection. Use the Subprotocols() -// function to get the subprotocols requested by the client. Use the -// Sec-Websocket-Protocol response header to specify the subprotocol selected -// by the application. -// -// The responseHeader is included in the response to the client's upgrade -// request. Use the responseHeader to specify cookies (Set-Cookie) and the -// negotiated subprotocol (Sec-Websocket-Protocol). -// -// The connection buffers IO to the underlying network connection. The -// readBufSize and writeBufSize parameters specify the size of the buffers to -// use. Messages can be larger than the buffers. -// -// If the request is not a valid WebSocket handshake, then Upgrade returns an -// error of type HandshakeError. Applications should handle this error by -// replying to the client with an HTTP error response. -func Upgrade(w http.ResponseWriter, r *http.Request, responseHeader http.Header, readBufSize, writeBufSize int) (*Conn, error) { - u := Upgrader{ReadBufferSize: readBufSize, WriteBufferSize: writeBufSize} - u.Error = func(w http.ResponseWriter, r *http.Request, status int, reason error) { - // don't return errors to maintain backwards compatibility - } - u.CheckOrigin = func(r *http.Request) bool { - // allow all connections by default - return true - } - return u.Upgrade(w, r, responseHeader) -} - -// Subprotocols returns the subprotocols requested by the client in the -// Sec-Websocket-Protocol header. -func Subprotocols(r *http.Request) []string { - h := strings.TrimSpace(r.Header.Get("Sec-Websocket-Protocol")) - if h == "" { - return nil - } - protocols := strings.Split(h, ",") - for i := range protocols { - protocols[i] = strings.TrimSpace(protocols[i]) - } - return protocols -} - -// IsWebSocketUpgrade returns true if the client requested upgrade to the -// WebSocket protocol. -func IsWebSocketUpgrade(r *http.Request) bool { - return tokenListContainsValue(r.Header, "Connection", "upgrade") && - tokenListContainsValue(r.Header, "Upgrade", "websocket") -} diff --git a/vendor/github.com/gorilla/websocket/util.go b/vendor/github.com/gorilla/websocket/util.go deleted file mode 100644 index 9a4908df2..000000000 --- a/vendor/github.com/gorilla/websocket/util.go +++ /dev/null @@ -1,214 +0,0 @@ -// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package websocket - -import ( - "crypto/rand" - "crypto/sha1" - "encoding/base64" - "io" - "net/http" - "strings" -) - -var keyGUID = []byte("258EAFA5-E914-47DA-95CA-C5AB0DC85B11") - -func computeAcceptKey(challengeKey string) string { - h := sha1.New() - h.Write([]byte(challengeKey)) - h.Write(keyGUID) - return base64.StdEncoding.EncodeToString(h.Sum(nil)) -} - -func generateChallengeKey() (string, error) { - p := make([]byte, 16) - if _, err := io.ReadFull(rand.Reader, p); err != nil { - return "", err - } - return base64.StdEncoding.EncodeToString(p), nil -} - -// Octet types from RFC 2616. -var octetTypes [256]byte - -const ( - isTokenOctet = 1 << iota - isSpaceOctet -) - -func init() { - // From RFC 2616 - // - // OCTET = - // CHAR = - // CTL = - // CR = - // LF = - // SP = - // HT = - // <"> = - // CRLF = CR LF - // LWS = [CRLF] 1*( SP | HT ) - // TEXT = - // separators = "(" | ")" | "<" | ">" | "@" | "," | ";" | ":" | "\" | <"> - // | "/" | "[" | "]" | "?" | "=" | "{" | "}" | SP | HT - // token = 1* - // qdtext = > - - for c := 0; c < 256; c++ { - var t byte - isCtl := c <= 31 || c == 127 - isChar := 0 <= c && c <= 127 - isSeparator := strings.IndexRune(" \t\"(),/:;<=>?@[]\\{}", rune(c)) >= 0 - if strings.IndexRune(" \t\r\n", rune(c)) >= 0 { - t |= isSpaceOctet - } - if isChar && !isCtl && !isSeparator { - t |= isTokenOctet - } - octetTypes[c] = t - } -} - -func skipSpace(s string) (rest string) { - i := 0 - for ; i < len(s); i++ { - if octetTypes[s[i]]&isSpaceOctet == 0 { - break - } - } - return s[i:] -} - -func nextToken(s string) (token, rest string) { - i := 0 - for ; i < len(s); i++ { - if octetTypes[s[i]]&isTokenOctet == 0 { - break - } - } - return s[:i], s[i:] -} - -func nextTokenOrQuoted(s string) (value string, rest string) { - if !strings.HasPrefix(s, "\"") { - return nextToken(s) - } - s = s[1:] - for i := 0; i < len(s); i++ { - switch s[i] { - case '"': - return s[:i], s[i+1:] - case '\\': - p := make([]byte, len(s)-1) - j := copy(p, s[:i]) - escape := true - for i = i + 1; i < len(s); i++ { - b := s[i] - switch { - case escape: - escape = false - p[j] = b - j += 1 - case b == '\\': - escape = true - case b == '"': - return string(p[:j]), s[i+1:] - default: - p[j] = b - j += 1 - } - } - return "", "" - } - } - return "", "" -} - -// tokenListContainsValue returns true if the 1#token header with the given -// name contains token. -func tokenListContainsValue(header http.Header, name string, value string) bool { -headers: - for _, s := range header[name] { - for { - var t string - t, s = nextToken(skipSpace(s)) - if t == "" { - continue headers - } - s = skipSpace(s) - if s != "" && s[0] != ',' { - continue headers - } - if strings.EqualFold(t, value) { - return true - } - if s == "" { - continue headers - } - s = s[1:] - } - } - return false -} - -// parseExtensiosn parses WebSocket extensions from a header. -func parseExtensions(header http.Header) []map[string]string { - - // From RFC 6455: - // - // Sec-WebSocket-Extensions = extension-list - // extension-list = 1#extension - // extension = extension-token *( ";" extension-param ) - // extension-token = registered-token - // registered-token = token - // extension-param = token [ "=" (token | quoted-string) ] - // ;When using the quoted-string syntax variant, the value - // ;after quoted-string unescaping MUST conform to the - // ;'token' ABNF. - - var result []map[string]string -headers: - for _, s := range header["Sec-Websocket-Extensions"] { - for { - var t string - t, s = nextToken(skipSpace(s)) - if t == "" { - continue headers - } - ext := map[string]string{"": t} - for { - s = skipSpace(s) - if !strings.HasPrefix(s, ";") { - break - } - var k string - k, s = nextToken(skipSpace(s[1:])) - if k == "" { - continue headers - } - s = skipSpace(s) - var v string - if strings.HasPrefix(s, "=") { - v, s = nextTokenOrQuoted(skipSpace(s[1:])) - s = skipSpace(s) - } - if s != "" && s[0] != ',' && s[0] != ';' { - continue headers - } - ext[k] = v - } - if s != "" && s[0] != ',' { - continue headers - } - result = append(result, ext) - if s == "" { - continue headers - } - s = s[1:] - } - } - return result -} diff --git a/vendor/github.com/moul/anonuuid/.gitignore b/vendor/github.com/moul/anonuuid/.gitignore deleted file mode 100644 index 8124bf789..000000000 --- a/vendor/github.com/moul/anonuuid/.gitignore +++ /dev/null @@ -1,28 +0,0 @@ -anonuuid - -/dist - -# Compiled Object files, Static and Dynamic libs (Shared Objects) -*.o -*.a -*.so - -# Folders -_obj -_test - -# Architecture specific extensions/prefixes -*.[568vq] -[568vq].out - -*.cgo1.go -*.cgo2.c -_cgo_defun.c -_cgo_gotypes.go -_cgo_export.* - -_testmain.go - -*.exe -*.test -*.prof diff --git a/vendor/github.com/moul/anonuuid/.godir b/vendor/github.com/moul/anonuuid/.godir deleted file mode 100644 index ea28ac285..000000000 --- a/vendor/github.com/moul/anonuuid/.godir +++ /dev/null @@ -1 +0,0 @@ -github.com/moul/anonuuid diff --git a/vendor/github.com/moul/anonuuid/.goxc.json b/vendor/github.com/moul/anonuuid/.goxc.json deleted file mode 100644 index 16f0b4589..000000000 --- a/vendor/github.com/moul/anonuuid/.goxc.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "AppName": "anonuuid", - "ArtifactsDest": "dist", - "OutPath": "{{.Dest}}{{.PS}}{{.Version}}{{.PS}}{{.Os}}_{{.Arch}}{{.PS}}{{.ExeName}}{{.Ext}}", - "TasksExclude": [ - "go-test", - "go-vet" - ], - "MainDirsExclude": "vendor,Godeps,testdata", - "PackageVersion": "1.0", - "ConfigVersion": "0.9" -} diff --git a/vendor/github.com/moul/anonuuid/.travis.yml b/vendor/github.com/moul/anonuuid/.travis.yml deleted file mode 100644 index 94f52d1b6..000000000 --- a/vendor/github.com/moul/anonuuid/.travis.yml +++ /dev/null @@ -1,30 +0,0 @@ -language: go - - -go: -- 1.3 -- 1.4 -- 1.5 -- tip - - -matrix: - allow_failures: - - go: tip - - -before_install: -- go get -u github.com/axw/gocov/gocov -- go get -u github.com/mattn/goveralls -- go get golang.org/x/tools/cmd/cover - - -#install: -#- go get -d -v -t ./... - - -script: -- go build -v ./cmd/... -- go test -v ./ -- go test -covermode=count -coverprofile=profile.out . -- goveralls -covermode=count -service=travis-ci -v -coverprofile=profile.out diff --git a/vendor/github.com/moul/anonuuid/LICENSE b/vendor/github.com/moul/anonuuid/LICENSE deleted file mode 100644 index 492e2c629..000000000 --- a/vendor/github.com/moul/anonuuid/LICENSE +++ /dev/null @@ -1,22 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2015 Manfred Touron - -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/github.com/moul/anonuuid/Makefile b/vendor/github.com/moul/anonuuid/Makefile deleted file mode 100644 index 4e2f3bb44..000000000 --- a/vendor/github.com/moul/anonuuid/Makefile +++ /dev/null @@ -1,73 +0,0 @@ -# Project-specific variables -BINARIES ?= anonuuid -CONVEY_PORT ?= 9042 - - -# Common variables -SOURCES := $(shell find . -name "*.go") -COMMANDS := $(shell go list ./... | grep -v /vendor/ | grep /cmd/) -PACKAGES := $(shell go list ./... | grep -v /vendor/ | grep -v /cmd/) -GOENV ?= GO15VENDOREXPERIMENT=1 -GO ?= $(GOENV) go -GODEP ?= $(GOENV) godep -USER ?= $(shell whoami) - - -all: build - - -.PHONY: build -build: $(BINARIES) - - -$(BINARIES): $(SOURCES) - $(GO) build -o $@ ./cmd/$@ - - -.PHONY: test -test: - $(GO) get -t . - $(GO) test -v . - - -.PHONY: godep-save -godep-save: - $(GODEP) save $(PACKAGES) $(COMMANDS) - - -.PHONY: clean -clean: - rm -f $(BINARIES) - - -.PHONY: re -re: clean all - - -.PHONY: convey -convey: - $(GO) get github.com/smartystreets/goconvey - goconvey -cover -port=$(CONVEY_PORT) -workDir="$(realpath .)" -depth=1 - - -.PHONY: cover -cover: profile.out - - -profile.out: $(SOURCES) - rm -f $@ - $(GO) test -covermode=count -coverpkg=. -coverprofile=$@ . - - -.PHONY: docker-build -docker-build: - go get github.com/laher/goxc - rm -rf contrib/docker/linux_386 - for binary in $(BINARIES); do \ - goxc -bc="linux,386" -d . -pv contrib/docker -n $$binary xc; \ - mv contrib/docker/linux_386/$$binary contrib/docker/entrypoint; \ - docker build -t $(USER)/$$binary contrib/docker; \ - docker run -it --rm $(USER)/$$binary || true; \ - docker inspect --type=image --format="{{ .Id }}" moul/$$binary || true; \ - echo "Now you can run 'docker push $(USER)/$$binary'"; \ - done diff --git a/vendor/github.com/moul/anonuuid/README.md b/vendor/github.com/moul/anonuuid/README.md deleted file mode 100644 index f5c9a57eb..000000000 --- a/vendor/github.com/moul/anonuuid/README.md +++ /dev/null @@ -1,170 +0,0 @@ -# AnonUUID - -[![Build Status](https://travis-ci.org/moul/anonuuid.svg)](https://travis-ci.org/moul/anonuuid) -[![GoDoc](https://godoc.org/github.com/moul/anonuuid?status.svg)](https://godoc.org/github.com/moul/anonuuid) -[![Coverage Status](https://coveralls.io/repos/moul/anonuuid/badge.svg?branch=master&service=github)](https://coveralls.io/github/moul/anonuuid?branch=master) - -:wrench: Anonymize UUIDs outputs (written in Golang) - -![AnonUUID Logo](https://raw.githubusercontent.com/moul/anonuuid/master/assets/anonuuid.png) - -**anonuuid** anonymize an input string by replacing all UUIDs by an anonymized -new one. - -The fake UUIDs are cached, so if AnonUUID encounter the same real UUIDs multiple -times, the translation will be the same. - -## Usage - -```console -$ anonuuid --help -NAME: - anonuuid - Anonymize UUIDs outputs - -USAGE: - anonuuid [global options] command [command options] [arguments...] - -VERSION: - 1.0.0-dev - -AUTHOR(S): - Manfred Touron - -COMMANDS: - help, h Shows a list of commands or help for one command - -GLOBAL OPTIONS: - --hexspeak Generate hexspeak style fake UUIDs - --random, -r Generate random fake UUIDs - --keep-beginning Keep first part of the UUID unchanged - --keep-end Keep last part of the UUID unchanged - --prefix, -p Prefix generated UUIDs - --suffix Suffix generated UUIDs - --help, -h show help - --version, -v print the version - ``` - -## Example - -Replace all UUIDs and cache the correspondance. - -```command -$ anonuuid git:(master) ✗ cat < 32 { - part = part[:32] - } - uuid := part[:8] + "-" + part[8:12] + "-1" + part[13:16] + "-" + part[16:20] + "-" + part[20:32] - - err := IsUUID(uuid) - if err != nil { - return "", err - } - - return uuid, nil -} - -// GenerateRandomUUID returns an UUID based on random strings -func GenerateRandomUUID(length int) (string, error) { - var letters = []rune("abcdef0123456789") - - b := make([]rune, length) - for i := range b { - b[i] = letters[rand.Intn(len(letters))] - } - return FormatUUID(string(b)) -} - -// GenerateHexspeakUUID returns an UUID formatted string containing hexspeak words -func GenerateHexspeakUUID(i int) (string, error) { - if i < 0 { - i = -i - } - hexspeaks := []string{ - "0ff1ce", - "31337", - "4b1d", - "badc0de", - "badcafe", - "badf00d", - "deadbabe", - "deadbeef", - "deadc0de", - "deadfeed", - "fee1bad", - } - return FormatUUID(hexspeaks[i%len(hexspeaks)]) -} - -// GenerateLenUUID returns an UUID formatted string based on an index number -func GenerateLenUUID(i int) (string, error) { - if i < 0 { - i = 2<<29 + i - } - return FormatUUID(fmt.Sprintf("%x", i)) -} diff --git a/vendor/github.com/moul/gotty-client/.gitignore b/vendor/github.com/moul/gotty-client/.gitignore deleted file mode 100644 index ef713932b..000000000 --- a/vendor/github.com/moul/gotty-client/.gitignore +++ /dev/null @@ -1,27 +0,0 @@ -/gotty-client -/dist - -# Compiled Object files, Static and Dynamic libs (Shared Objects) -*.o -*.a -*.so - -# Folders -_obj -_test - -# Architecture specific extensions/prefixes -*.[568vq] -[568vq].out - -*.cgo1.go -*.cgo2.c -_cgo_defun.c -_cgo_gotypes.go -_cgo_export.* - -_testmain.go - -*.exe -*.test -*.prof diff --git a/vendor/github.com/moul/gotty-client/.goxc.json b/vendor/github.com/moul/gotty-client/.goxc.json deleted file mode 100644 index 3c3675a6e..000000000 --- a/vendor/github.com/moul/gotty-client/.goxc.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "AppName": "gotty-client", - "ArtifactsDest": "dist", - "BuildConstraints": "!plan9,!nacl,!solaris", - "OutPath": "{{.Dest}}{{.PS}}{{.Version}}{{.PS}}{{.ExeName}}_{{.Version}}_{{.Os}}_{{.Arch}}{{.Ext}}", - "TasksExclude": [ - "go-test", - "go-vet" - ], - "MainDirsExclude": "vendor,Godeps,testdata", - "PackageVersion": "1.6.1+dev", - "ConfigVersion": "0.9" -} diff --git a/vendor/github.com/moul/gotty-client/.travis.yml b/vendor/github.com/moul/gotty-client/.travis.yml deleted file mode 100644 index d0ae7abcb..000000000 --- a/vendor/github.com/moul/gotty-client/.travis.yml +++ /dev/null @@ -1,14 +0,0 @@ -language: go - -go: -- 1.7 -- 1.8 -- tip - -matrix: - allow_failures: - - go: tip - -script: -- go build ./cmd/... -- go run ./cmd/gotty-client/main.go --help || true diff --git a/vendor/github.com/moul/gotty-client/Dockerfile b/vendor/github.com/moul/gotty-client/Dockerfile deleted file mode 100644 index e598be675..000000000 --- a/vendor/github.com/moul/gotty-client/Dockerfile +++ /dev/null @@ -1,11 +0,0 @@ -# build -FROM golang:1.9 as builder -RUN apt update && apt -y install jq -COPY . /go/src/github.com/moul/gotty-client -WORKDIR /go/src/github.com/moul/gotty-client -RUN make install - -# minimal runtime -FROM scratch -COPY --from=builder /go/bin/gotty-client /bin/gotty-client -ENTRYPOINT ["/bin/gotty-client"] diff --git a/vendor/github.com/moul/gotty-client/LICENSE b/vendor/github.com/moul/gotty-client/LICENSE deleted file mode 100644 index 492e2c629..000000000 --- a/vendor/github.com/moul/gotty-client/LICENSE +++ /dev/null @@ -1,22 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2015 Manfred Touron - -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/github.com/moul/gotty-client/LICENSE.apache b/vendor/github.com/moul/gotty-client/LICENSE.apache deleted file mode 100644 index 6fe259dd5..000000000 --- a/vendor/github.com/moul/gotty-client/LICENSE.apache +++ /dev/null @@ -1,192 +0,0 @@ - - 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 2013-2017 Docker, Inc. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - 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/moul/gotty-client/Makefile b/vendor/github.com/moul/gotty-client/Makefile deleted file mode 100644 index e8ff3b579..000000000 --- a/vendor/github.com/moul/gotty-client/Makefile +++ /dev/null @@ -1,73 +0,0 @@ -# Project-specific variables -BINARIES ?= gotty-client -GOTTY_URL := http://localhost:8081/ -VERSION := $(shell cat .goxc.json | jq -c .PackageVersion | sed 's/"//g') - -CONVEY_PORT ?= 9042 - - -# Common variables -SOURCES := $(shell find . -type f -name "*.go") -COMMANDS := $(shell go list ./... | grep -v /vendor/ | grep /cmd/) -PACKAGES := $(shell go list ./... | grep -v /vendor/ | grep -v /cmd/) -GOENV ?= GO15VENDOREXPERIMENT=1 -GO ?= $(GOENV) go -GODEP ?= $(GOENV) godep -USER ?= $(shell whoami) - - -all: build - - -.PHONY: build -build: $(BINARIES) - - -.PHONY: install -install: - $(GO) install ./cmd/gotty-client - - -$(BINARIES): $(SOURCES) - $(GO) build -i -o $@ ./cmd/$@ - - -.PHONY: test -test: -# $(GO) get -t . - $(GO) test -i -v . - $(GO) test -v . - - -.PHONY: godep-save -godep-save: - $(GODEP) save $(PACKAGES) $(COMMANDS) - - -.PHONY: clean -clean: - rm -f $(BINARIES) - - -.PHONY: re -re: clean all - - -.PHONY: convey -convey: - $(GO) get github.com/smartystreets/goconvey - goconvey -cover -port=$(CONVEY_PORT) -workDir="$(realpath .)" -depth=1 - - -.PHONY: cover -cover: profile.out - - -profile.out: $(SOURCES) - rm -f $@ - $(GO) test -covermode=count -coverpkg=. -coverprofile=$@ . - - -.PHONY: docker-build -docker-build: - docker build -t moul/gotty-client . diff --git a/vendor/github.com/moul/gotty-client/README.md b/vendor/github.com/moul/gotty-client/README.md deleted file mode 100644 index 91d3be331..000000000 --- a/vendor/github.com/moul/gotty-client/README.md +++ /dev/null @@ -1,205 +0,0 @@ -# gotty-client -:wrench: Terminal client for [GoTTY](https://github.com/yudai/gotty). - -![](https://raw.githubusercontent.com/moul/gotty-client/master/resources/gotty-client.png) - -[![Build Status](https://travis-ci.org/moul/gotty-client.svg?branch=master)](https://travis-ci.org/moul/gotty-client) -[![GoDoc](https://godoc.org/github.com/moul/gotty-client?status.svg)](https://godoc.org/github.com/moul/gotty-client) -[![FOSSA Status](https://app.fossa.io/api/projects/git%2Bhttps%3A%2F%2Fgithub.com%2Fmoul%2Fgotty-client.svg?type=shield)](https://app.fossa.io/projects/git%2Bhttps%3A%2F%2Fgithub.com%2Fmoul%2Fgotty-client?ref=badge_shield) - -```ruby - ┌─────────────────┐ - ┌──────▶│ /bin/bash │ - │ └─────────────────┘ - ┌──────────────┐ ┌──────────┐ - │ │ │ Gotty │ -┌───────┐ ┌──▶│ Browser │───────┐ │ │ -│ │ │ │ │ │ │ │ -│ │ │ └──────────────┘ │ │ │ ┌─────────────────┐ -│ Bob │───┤ websockets─▶│ │─▶│ emacs /var/www │ -│ │ │ ╔═ ══ ══ ══ ══ ╗ │ │ │ └─────────────────┘ -│ │ │ ║ ║ │ │ │ -└───────┘ └──▶║ gotty-client ───────┘ │ │ - ║ │ │ - ╚═ ══ ══ ══ ══ ╝ └──────────┘ - │ ┌─────────────────┐ - └──────▶│ tmux attach │ - └─────────────────┘ -``` - -## Example - -Server side ([GoTTY](https://github.com/yudai/gotty)) - -```console -$ gotty -p 9191 sh -c 'while true; do date; sleep 1; done' -2015/08/24 18:54:31 Server is starting with command: sh -c while true; do date; sleep 1; done -2015/08/24 18:54:31 URL: http://[::1]:9191/ -2015/08/24 18:54:34 GET /ws -2015/08/24 18:54:34 New client connected: 127.0.0.1:61811 -2015/08/24 18:54:34 Command is running for client 127.0.0.1:61811 with PID 64834 -2015/08/24 18:54:39 Command exited for: 127.0.0.1:61811 -2015/08/24 18:54:39 Connection closed: 127.0.0.1:61811 -... -``` - -**Client side** - -```console -$ gotty-client http://localhost:9191/ -INFO[0000] New title: GoTTY - sh -c while true; do date; sleep 1; done (jean-michel-van-damme.local) -WARN[0000] Unhandled protocol message: json pref: 2{} -Mon Aug 24 18:54:34 CEST 2015 -Mon Aug 24 18:54:35 CEST 2015 -Mon Aug 24 18:54:36 CEST 2015 -Mon Aug 24 18:54:37 CEST 2015 -Mon Aug 24 18:54:38 CEST 2015 -^C -``` - -## Usage - -```console -$ gotty-client -h -NAME: - gotty-client - GoTTY client for your terminal - -USAGE: - gotty-client [global options] command [command options] GOTTY_URL - -VERSION: - 1.3.0+ - -AUTHOR(S): - Manfred Touron - -COMMANDS: - help, h Shows a list of commands or help for one command - -GLOBAL OPTIONS: - --debug, -D Enable debug mode [$GOTTY_CLIENT_DEBUG] - --help, -h show help - --version, -v print the version -``` - -## Install - -Install latest version using Golang (recommended) - -```console -$ go get github.com/moul/gotty-client/cmd/gotty-client -``` - ---- - -Install latest version using Homebrew (Mac OS X) - -```console -$ brew install https://raw.githubusercontent.com/moul/gotty-client/master/contrib/homebrew/gotty-client.rb --HEAD -``` - -or the latest released version - -```console -$ brew install https://raw.githubusercontent.com/moul/gotty-client/master/contrib/homebrew/gotty-client.rb -``` - -## Changelog - -### master (unreleased) - -* Add `--detach-keys` option ([#52](https://github.com/moul/gotty-client/issues/52)) - -[full commits list](https://github.com/moul/gotty-client/compare/v1.6.1...master) - -### [v1.6.1](https://github.com/moul/gotty-client/releases/tag/v1.6.1) (2017-01-19) - -* Do not exit on EOF ([#45](https://github.com/moul/gotty-client/pull/45)) ([@gurjeet](https://github.com/gurjeet)) - -[full commits list](https://github.com/moul/gotty-client/compare/v1.6.0...v1.6.1) - -### [v1.6.0](https://github.com/moul/gotty-client/releases/tag/v1.6.0) (2016-05-23) - -* Support of `--use-proxy-from-env` (Add Proxy support) ([#36](https://github.com/moul/gotty-client/pull/36)) ([@byung2](https://github.com/byung2)) -* Add debug mode ([#18](https://github.com/moul/gotty-client/issues/18)) -* Fix argument passing ([#16](https://github.com/moul/gotty-client/issues/16)) -* Remove warnings + golang fixes and refactoring ([@QuentinPerez](https://github.com/QuentinPerez)) - -[full commits list](https://github.com/moul/gotty-client/compare/v1.5.0...v1.6.0) - -### [v1.5.0](https://github.com/moul/gotty-client/releases/tag/v1.5.0) (2016-02-18) - -* Add autocomplete support ([#19](https://github.com/moul/gotty-client/issues/19)) -* Switch from `Party` to `Godep` -* Fix terminal data being interpreted as format string ([#34](https://github.com/moul/gotty-client/pull/34)) ([@mickael9](https://github.com/mickael9)) - -[full commits list](https://github.com/moul/gotty-client/compare/v1.4.0...v1.5.0) - -### [v1.4.0](https://github.com/moul/gotty-client/releases/tag/v1.4.0) (2015-12-09) - -* Remove solaris,plan9,nacl for `.goxc.json` -* Add an error if the go version is lower than 1.5 -* Flexible parsing of the input URL -* Add tests -* Support of `--skip-tls-verify` - -[full commits list](https://github.com/moul/gotty-client/compare/v1.3.0...v1.4.0) - -### [v1.3.0](https://github.com/moul/gotty-client/releases/tag/v1.3.0) (2015-10-27) - -* Fix `connected` state when using `Connect()` + `Loop()` methods -* Add `ExitLoop` which allow to exit from `Loop` function - -[full commits list](https://github.com/moul/gotty-client/compare/v1.2.0...v1.3.0) - -### [v1.2.0](https://github.com/moul/gotty-client/releases/tag/v1.2.0) (2015-10-23) - -* Removed an annoying warning when exiting connection ([#22](https://github.com/moul/gotty-client/issues/22)) ([@QuentinPerez](https://github.com/QuentinPerez)) -* Add the ability to configure alternative stdout ([#21](https://github.com/moul/gotty-client/issues/21)) ([@QuentinPerez](https://github.com/QuentinPerez)) -* Refactored the goroutine system with select, improve speed and stability ([@QuentinPerez](https://github.com/QuentinPerez)) -* Add debug mode (`--debug`/`-D`) ([#18](https://github.com/moul/gotty-client/issues/18)) -* Improve error message when connecting by checking HTTP status code -* Fix arguments passing ([#16](https://github.com/moul/gotty-client/issues/16)) -* Dropped support for golang<1.5 -* Small fixes - -[full commits list](https://github.com/moul/gotty-client/compare/v1.1.0...v1.2.0) - -### [v1.1.0](https://github.com/moul/gotty-client/releases/tag/v1.1.0) (2015-10-10) - -* Handling arguments + using mutexes (thanks to [@QuentinPerez](https://github.com/QuentinPerez)) -* Add logo ([#9](https://github.com/moul/gotty-client/issues/9)) -* Using codegansta/cli for CLI parsing ([#3](https://github.com/moul/gotty-client/issues/3)) -* Fix panic when running on older GoTTY server ([#13](https://github.com/moul/gotty-client/issues/13)) -* Add 'homebrew support' ([#1](https://github.com/moul/gotty-client/issues/1)) -* Add Changelog ([#5](https://github.com/moul/gotty-client/issues/5)) -* Add GOXC configuration to build binaries for multiple architectures ([#2](https://github.com/moul/gotty-client/issues/2)) - -[full commits list](https://github.com/moul/gotty-client/compare/v1.0.1...v1.1.0) - -### [v1.0.1](https://github.com/moul/gotty-client/releases/tag/v1.0.1) (2015-09-27) - -* Using party to manage dependencies - -[full commits list](https://github.com/moul/gotty-client/compare/v1.0.0...v1.0.1) - -### [v1.0.0](https://github.com/moul/gotty-client/releases/tag/v1.0.0) (2015-09-27) - -Compatible with [GoTTY](https://github.com/yudai/gotty) version: [v0.0.10](https://github.com/yudai/gotty/releases/tag/v0.0.10) - -#### Features - -* Support **basic-auth** -* Support **terminal-(re)size** -* Support **write** -* Support **title** -* Support **custom URI** - -[full commits list](https://github.com/moul/gotty-client/compare/cf0c1146c7ce20fe0bd65764c13253bc575cd43a...v1.0.0) - -## License - -MIT - - -[![FOSSA Status](https://app.fossa.io/api/projects/git%2Bhttps%3A%2F%2Fgithub.com%2Fmoul%2Fgotty-client.svg?type=large)](https://app.fossa.io/projects/git%2Bhttps%3A%2F%2Fgithub.com%2Fmoul%2Fgotty-client?ref=badge_large) diff --git a/vendor/github.com/moul/gotty-client/arch.go b/vendor/github.com/moul/gotty-client/arch.go deleted file mode 100644 index 77a4deaec..000000000 --- a/vendor/github.com/moul/gotty-client/arch.go +++ /dev/null @@ -1,32 +0,0 @@ -// +build !windows - -package gottyclient - -import ( - "encoding/json" - "fmt" - "golang.org/x/sys/unix" - "os" - "os/signal" - "syscall" -) - -func notifySignalSIGWINCH(c chan<- os.Signal) { - signal.Notify(c, syscall.SIGWINCH) -} - -func resetSignalSIGWINCH() { - signal.Reset(syscall.SIGWINCH) -} - -func syscallTIOCGWINSZ() ([]byte, error) { - ws, err := unix.IoctlGetWinsize(0, 0) - if err != nil { - return nil, fmt.Errorf("ioctl error: %v", err) - } - b, err := json.Marshal(ws) - if err != nil { - return nil, fmt.Errorf("json.Marshal error: %v", err) - } - return b, err -} diff --git a/vendor/github.com/moul/gotty-client/arch_windows.go b/vendor/github.com/moul/gotty-client/arch_windows.go deleted file mode 100644 index 53aaffc5f..000000000 --- a/vendor/github.com/moul/gotty-client/arch_windows.go +++ /dev/null @@ -1,16 +0,0 @@ -package gottyclient - -import ( - "errors" - "os" -) - -func notifySignalSIGWINCH(c chan<- os.Signal) { -} - -func resetSignalSIGWINCH() { -} - -func syscallTIOCGWINSZ() ([]byte, error) { - return nil, errors.New("SIGWINCH isn't supported on this ARCH") -} diff --git a/vendor/github.com/moul/gotty-client/gotty-client.go b/vendor/github.com/moul/gotty-client/gotty-client.go deleted file mode 100644 index 58f4a8547..000000000 --- a/vendor/github.com/moul/gotty-client/gotty-client.go +++ /dev/null @@ -1,473 +0,0 @@ -package gottyclient - -import ( - "crypto/tls" - "encoding/base64" - "encoding/json" - "fmt" - "io" - "io/ioutil" - "net/http" - "net/url" - "os" - "regexp" - "strings" - "sync" - "time" - - "github.com/creack/goselect" - "github.com/gorilla/websocket" - "github.com/sirupsen/logrus" - "golang.org/x/crypto/ssh/terminal" -) - -// GetAuthTokenURL transforms a GoTTY http URL to its AuthToken file URL -func GetAuthTokenURL(httpURL string) (*url.URL, *http.Header, error) { - header := http.Header{} - target, err := url.Parse(httpURL) - if err != nil { - return nil, nil, err - } - - target.Path = strings.TrimLeft(target.Path+"auth_token.js", "/") - - if target.User != nil { - header.Add("Authorization", "Basic "+base64.StdEncoding.EncodeToString([]byte(target.User.String()))) - target.User = nil - } - - return target, &header, nil -} - -// GetURLQuery returns url.query -func GetURLQuery(rawurl string) (url.Values, error) { - target, err := url.Parse(rawurl) - if err != nil { - return nil, err - } - return target.Query(), nil -} - -// GetWebsocketURL transforms a GoTTY http URL to its WebSocket URL -func GetWebsocketURL(httpURL string) (*url.URL, *http.Header, error) { - header := http.Header{} - target, err := url.Parse(httpURL) - if err != nil { - return nil, nil, err - } - - if target.Scheme == "https" { - target.Scheme = "wss" - } else { - target.Scheme = "ws" - } - - target.Path = strings.TrimLeft(target.Path+"ws", "/") - - if target.User != nil { - header.Add("Authorization", "Basic "+base64.StdEncoding.EncodeToString([]byte(target.User.String()))) - target.User = nil - } - - return target, &header, nil -} - -type Client struct { - Dialer *websocket.Dialer - Conn *websocket.Conn - URL string - WriteMutex *sync.Mutex - Output io.Writer - poison chan bool - SkipTLSVerify bool - UseProxyFromEnv bool - Connected bool - EscapeKeys []byte -} - -type querySingleType struct { - AuthToken string `json:"AuthToken"` - Arguments string `json:"Arguments"` -} - -func (c *Client) write(data []byte) error { - c.WriteMutex.Lock() - defer c.WriteMutex.Unlock() - return c.Conn.WriteMessage(websocket.TextMessage, data) -} - -// GetAuthToken retrieves an Auth Token from dynamic auth_token.js file -func (c *Client) GetAuthToken() (string, error) { - target, header, err := GetAuthTokenURL(c.URL) - if err != nil { - return "", err - } - - logrus.Debugf("Fetching auth token auth-token: %q", target.String()) - req, err := http.NewRequest("GET", target.String(), nil) - req.Header = *header - tr := &http.Transport{} - if c.SkipTLSVerify { - conf := &tls.Config{InsecureSkipVerify: true} - tr.TLSClientConfig = conf - } - if c.UseProxyFromEnv { - tr.Proxy = http.ProxyFromEnvironment - } - client := &http.Client{Transport: tr} - resp, err := client.Do(req) - if err != nil { - return "", err - } - - switch resp.StatusCode { - case 200: - // Everything is OK - default: - return "", fmt.Errorf("unknown status code: %d (%s)", resp.StatusCode, http.StatusText(resp.StatusCode)) - } - - defer resp.Body.Close() - body, err := ioutil.ReadAll(resp.Body) - if err != nil { - return "", err - } - - re := regexp.MustCompile("var gotty_auth_token = '(.*)'") - output := re.FindStringSubmatch(string(body)) - if len(output) == 0 { - return "", fmt.Errorf("Cannot fetch GoTTY auth-token, please upgrade your GoTTY server.") - } - - return output[1], nil -} - -// Connect tries to dial a websocket server -func (c *Client) Connect() error { - // Retrieve AuthToken - authToken, err := c.GetAuthToken() - if err != nil { - return err - } - logrus.Debugf("Auth-token: %q", authToken) - - // Open WebSocket connection - target, header, err := GetWebsocketURL(c.URL) - if err != nil { - return err - } - logrus.Debugf("Connecting to websocket: %q", target.String()) - if c.SkipTLSVerify { - c.Dialer.TLSClientConfig = &tls.Config{InsecureSkipVerify: true} - } - if c.UseProxyFromEnv { - c.Dialer.Proxy = http.ProxyFromEnvironment - } - conn, _, err := c.Dialer.Dial(target.String(), *header) - if err != nil { - return err - } - c.Conn = conn - c.Connected = true - - // Pass arguments and auth-token - query, err := GetURLQuery(c.URL) - if err != nil { - return err - } - querySingle := querySingleType{ - Arguments: "?" + query.Encode(), - AuthToken: authToken, - } - json, err := json.Marshal(querySingle) - if err != nil { - logrus.Errorf("Failed to parse init message %v", err) - return err - } - // Send Json - logrus.Debugf("Sending arguments and auth-token") - err = c.write(json) - if err != nil { - return err - } - - go c.pingLoop() - - return nil -} - -func (c *Client) pingLoop() { - for { - logrus.Debugf("Sending ping") - c.write([]byte("1")) - time.Sleep(30 * time.Second) - } -} - -// Close will nicely close the dialer -func (c *Client) Close() { - c.Conn.Close() -} - -// ExitLoop will kill all goroutines launched by c.Loop() -// ExitLoop() -> wait Loop() -> Close() -func (c *Client) ExitLoop() { - fname := "ExitLoop" - openPoison(fname, c.poison) -} - -// Loop will look indefinitely for new messages -func (c *Client) Loop() error { - if !c.Connected { - err := c.Connect() - if err != nil { - return err - } - } - - wg := &sync.WaitGroup{} - - wg.Add(1) - go c.termsizeLoop(wg) - - wg.Add(1) - go c.readLoop(wg) - - wg.Add(1) - go c.writeLoop(wg) - - /* Wait for all of the above goroutines to finish */ - wg.Wait() - - logrus.Debug("Client.Loop() exiting") - return nil -} - -type winsize struct { - Rows uint16 `json:"rows"` - Columns uint16 `json:"columns"` - // unused - x uint16 - y uint16 -} - -type posionReason int - -const ( - committedSuicide = iota - killed -) - -func openPoison(fname string, poison chan bool) posionReason { - logrus.Debug(fname + " suicide") - - /* - * The close() may raise panic if multiple goroutines commit suicide at the - * same time. Prevent that panic from bubbling up. - */ - defer func() { - if r := recover(); r != nil { - logrus.Debug("Prevented panic() of simultaneous suicides", r) - } - }() - - /* Signal others to die */ - close(poison) - return committedSuicide -} - -func die(fname string, poison chan bool) posionReason { - logrus.Debug(fname + " died") - - wasOpen := <-poison - if wasOpen { - logrus.Error("ERROR: The channel was open when it wasn't suppoed to be") - } - - return killed -} - -func (c *Client) termsizeLoop(wg *sync.WaitGroup) posionReason { - - defer wg.Done() - fname := "termsizeLoop" - - ch := make(chan os.Signal, 1) - notifySignalSIGWINCH(ch) - defer resetSignalSIGWINCH() - - for { - if b, err := syscallTIOCGWINSZ(); err != nil { - logrus.Warn(err) - } else { - if err = c.write(append([]byte("2"), b...)); err != nil { - logrus.Warnf("ws.WriteMessage failed: %v", err) - } - } - select { - case <-c.poison: - /* Somebody poisoned the well; die */ - return die(fname, c.poison) - case <-ch: - } - } -} - -type exposeFd interface { - Fd() uintptr -} - -func (c *Client) writeLoop(wg *sync.WaitGroup) posionReason { - defer wg.Done() - fname := "writeLoop" - - buff := make([]byte, 128) - oldState, err := terminal.MakeRaw(0) - if err == nil { - defer terminal.Restore(0, oldState) - } - - rdfs := &goselect.FDSet{} - reader := io.ReadCloser(os.Stdin) - - pr := NewEscapeProxy(reader, c.EscapeKeys) - defer reader.Close() - - for { - select { - case <-c.poison: - /* Somebody poisoned the well; die */ - return die(fname, c.poison) - default: - } - - rdfs.Zero() - rdfs.Set(reader.(exposeFd).Fd()) - err := goselect.Select(1, rdfs, nil, nil, 50*time.Millisecond) - if err != nil { - return openPoison(fname, c.poison) - } - if rdfs.IsSet(reader.(exposeFd).Fd()) { - size, err := pr.Read(buff) - - if err != nil { - if err == io.EOF { - // Send EOF to GoTTY - - // Send 'Input' marker, as defined in GoTTY::client_context.go, - // followed by EOT (a translation of Ctrl-D for terminals) - err = c.write(append([]byte("0"), byte(4))) - - if err != nil { - return openPoison(fname, c.poison) - } - continue - } else { - return openPoison(fname, c.poison) - } - } - - if size <= 0 { - continue - } - - data := buff[:size] - err = c.write(append([]byte("0"), data...)) - if err != nil { - return openPoison(fname, c.poison) - } - } - } -} - -func (c *Client) readLoop(wg *sync.WaitGroup) posionReason { - - defer wg.Done() - fname := "readLoop" - - type MessageNonBlocking struct { - Data []byte - Err error - } - msgChan := make(chan MessageNonBlocking) - - for { - go func() { - _, data, err := c.Conn.ReadMessage() - msgChan <- MessageNonBlocking{Data: data, Err: err} - }() - - select { - case <-c.poison: - /* Somebody poisoned the well; die */ - return die(fname, c.poison) - case msg := <-msgChan: - if msg.Err != nil { - - if _, ok := msg.Err.(*websocket.CloseError); !ok { - logrus.Warnf("c.Conn.ReadMessage: %v", msg.Err) - } - return openPoison(fname, c.poison) - } - if len(msg.Data) == 0 { - - logrus.Warnf("An error has occured") - return openPoison(fname, c.poison) - } - switch msg.Data[0] { - case '0': // data - buf, err := base64.StdEncoding.DecodeString(string(msg.Data[1:])) - if err != nil { - logrus.Warnf("Invalid base64 content: %q", msg.Data[1:]) - break - } - c.Output.Write(buf) - case '1': // pong - case '2': // new title - newTitle := string(msg.Data[1:]) - fmt.Fprintf(c.Output, "\033]0;%s\007", newTitle) - case '3': // json prefs - logrus.Debugf("Unhandled protocol message: json pref: %s", string(msg.Data[1:])) - case '4': // autoreconnect - logrus.Debugf("Unhandled protocol message: autoreconnect: %s", string(msg.Data)) - default: - logrus.Warnf("Unhandled protocol message: %s", string(msg.Data)) - } - } - } -} - -// SetOutput changes the output stream -func (c *Client) SetOutput(w io.Writer) { - c.Output = w -} - -// ParseURL parses an URL which may be incomplete and tries to standardize it -func ParseURL(input string) (string, error) { - parsed, err := url.Parse(input) - if err != nil { - return "", err - } - switch parsed.Scheme { - case "http", "https": - // everything is ok - default: - return ParseURL(fmt.Sprintf("http://%s", input)) - } - return parsed.String(), nil -} - -// NewClient returns a GoTTY client object -func NewClient(inputURL string) (*Client, error) { - url, err := ParseURL(inputURL) - if err != nil { - return nil, err - } - return &Client{ - Dialer: &websocket.Dialer{}, - URL: url, - WriteMutex: &sync.Mutex{}, - Output: os.Stdout, - poison: make(chan bool), - }, nil -} diff --git a/vendor/github.com/moul/gotty-client/proxy.go b/vendor/github.com/moul/gotty-client/proxy.go deleted file mode 100644 index e42143ca8..000000000 --- a/vendor/github.com/moul/gotty-client/proxy.go +++ /dev/null @@ -1,90 +0,0 @@ -// Copyright 2013-2017 Docker, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); you may not -// use this file except in compliance with the License. You may obtain a copy of -// the License at http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -// License for the specific language governing permissions and limitations under -// the License. - -// CHANGES: -// - update package - - -package gottyclient - -import ( - "io" -) - -// EscapeError is special error which returned by a TTY proxy reader's Read() -// method in case its detach escape sequence is read. -type EscapeError struct{} - -func (EscapeError) Error() string { - return "read escape sequence" -} - -// escapeProxy is used only for attaches with a TTY. It is used to proxy -// stdin keypresses from the underlying reader and look for the passed in -// escape key sequence to signal a detach. -type escapeProxy struct { - escapeKeys []byte - escapeKeyPos int - r io.Reader -} - -// NewEscapeProxy returns a new TTY proxy reader which wraps the given reader -// and detects when the specified escape keys are read, in which case the Read -// method will return an error of type EscapeError. -func NewEscapeProxy(r io.Reader, escapeKeys []byte) io.Reader { - return &escapeProxy{ - escapeKeys: escapeKeys, - r: r, - } -} - -func (r *escapeProxy) Read(buf []byte) (int, error) { - nr, err := r.r.Read(buf) - - preserve := func() { - // this preserves the original key presses in the passed in buffer - nr += r.escapeKeyPos - preserve := make([]byte, 0, r.escapeKeyPos+len(buf)) - preserve = append(preserve, r.escapeKeys[:r.escapeKeyPos]...) - preserve = append(preserve, buf...) - r.escapeKeyPos = 0 - copy(buf[0:nr], preserve) - } - - if nr != 1 || err != nil { - if r.escapeKeyPos > 0 { - preserve() - } - return nr, err - } - - if buf[0] != r.escapeKeys[r.escapeKeyPos] { - if r.escapeKeyPos > 0 { - preserve() - } - return nr, nil - } - - if r.escapeKeyPos == len(r.escapeKeys)-1 { - return 0, EscapeError{} - } - - // Looks like we've got an escape key, but we need to match again on the next - // read. - // Store the current escape key we found so we can look for the next one on - // the next read. - // Since this is an escape key, make sure we don't let the caller read it - // If later on we find that this is not the escape sequence, we'll add the - // keys back - r.escapeKeyPos++ - return nr - r.escapeKeyPos, nil -} diff --git a/vendor/github.com/renstrom/fuzzysearch/LICENSE b/vendor/github.com/renstrom/fuzzysearch/LICENSE deleted file mode 100644 index 9cc753370..000000000 --- a/vendor/github.com/renstrom/fuzzysearch/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2015 Peter Renström - -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/github.com/renstrom/fuzzysearch/fuzzy/fuzzy.go b/vendor/github.com/renstrom/fuzzysearch/fuzzy/fuzzy.go deleted file mode 100644 index 63277d51e..000000000 --- a/vendor/github.com/renstrom/fuzzysearch/fuzzy/fuzzy.go +++ /dev/null @@ -1,167 +0,0 @@ -// Fuzzy searching allows for flexibly matching a string with partial input, -// useful for filtering data very quickly based on lightweight user input. -package fuzzy - -import ( - "unicode" - "unicode/utf8" -) - -var noop = func(r rune) rune { return r } - -// Match returns true if source matches target using a fuzzy-searching -// algorithm. Note that it doesn't implement Levenshtein distance (see -// RankMatch instead), but rather a simplified version where there's no -// approximation. The method will return true only if each character in the -// source can be found in the target and occurs after the preceding matches. -func Match(source, target string) bool { - return match(source, target, noop) -} - -// MatchFold is a case-insensitive version of Match. -func MatchFold(source, target string) bool { - return match(source, target, unicode.ToLower) -} - -func match(source, target string, fn func(rune) rune) bool { - lenDiff := len(target) - len(source) - - if lenDiff < 0 { - return false - } - - if lenDiff == 0 && source == target { - return true - } - -Outer: - for _, r1 := range source { - for i, r2 := range target { - if fn(r1) == fn(r2) { - target = target[i+utf8.RuneLen(r2):] - continue Outer - } - } - return false - } - - return true -} - -// Find will return a list of strings in targets that fuzzy matches source. -func Find(source string, targets []string) []string { - return find(source, targets, noop) -} - -// FindFold is a case-insensitive version of Find. -func FindFold(source string, targets []string) []string { - return find(source, targets, unicode.ToLower) -} - -func find(source string, targets []string, fn func(rune) rune) []string { - var matches []string - - for _, target := range targets { - if match(source, target, fn) { - matches = append(matches, target) - } - } - - return matches -} - -// RankMatch is similar to Match except it will measure the Levenshtein -// distance between the source and the target and return its result. If there -// was no match, it will return -1. -// Given the requirements of match, RankMatch only needs to perform a subset of -// the Levenshtein calculation, only deletions need be considered, required -// additions and substitutions would fail the match test. -func RankMatch(source, target string) int { - return rank(source, target, noop) -} - -// RankMatchFold is a case-insensitive version of RankMatch. -func RankMatchFold(source, target string) int { - return rank(source, target, unicode.ToLower) -} - -func rank(source, target string, fn func(rune) rune) int { - lenDiff := len(target) - len(source) - - if lenDiff < 0 { - return -1 - } - - if lenDiff == 0 && source == target { - return 0 - } - - runeDiff := 0 - -Outer: - for _, r1 := range source { - for i, r2 := range target { - if fn(r1) == fn(r2) { - target = target[i+utf8.RuneLen(r2):] - continue Outer - } else { - runeDiff++ - } - } - return -1 - } - - // Count up remaining char - for len(target) > 0 { - target = target[utf8.RuneLen(rune(target[0])):] - runeDiff++ - } - - return runeDiff -} - -// RankFind is similar to Find, except it will also rank all matches using -// Levenshtein distance. -func RankFind(source string, targets []string) Ranks { - var r Ranks - for _, target := range find(source, targets, noop) { - distance := LevenshteinDistance(source, target) - r = append(r, Rank{source, target, distance}) - } - return r -} - -// RankFindFold is a case-insensitive version of RankFind. -func RankFindFold(source string, targets []string) Ranks { - var r Ranks - for _, target := range find(source, targets, unicode.ToLower) { - distance := LevenshteinDistance(source, target) - r = append(r, Rank{source, target, distance}) - } - return r -} - -type Rank struct { - // Source is used as the source for matching. - Source string - - // Target is the word matched against. - Target string - - // Distance is the Levenshtein distance between Source and Target. - Distance int -} - -type Ranks []Rank - -func (r Ranks) Len() int { - return len(r) -} - -func (r Ranks) Swap(i, j int) { - r[i], r[j] = r[j], r[i] -} - -func (r Ranks) Less(i, j int) bool { - return r[i].Distance < r[j].Distance -} diff --git a/vendor/github.com/renstrom/fuzzysearch/fuzzy/levenshtein.go b/vendor/github.com/renstrom/fuzzysearch/fuzzy/levenshtein.go deleted file mode 100644 index 237923d34..000000000 --- a/vendor/github.com/renstrom/fuzzysearch/fuzzy/levenshtein.go +++ /dev/null @@ -1,43 +0,0 @@ -package fuzzy - -// LevenshteinDistance measures the difference between two strings. -// The Levenshtein distance between two words is the minimum number of -// single-character edits (i.e. insertions, deletions or substitutions) -// required to change one word into the other. -// -// This implemention is optimized to use O(min(m,n)) space and is based on the -// optimized C version found here: -// http://en.wikibooks.org/wiki/Algorithm_implementation/Strings/Levenshtein_distance#C -func LevenshteinDistance(s, t string) int { - r1, r2 := []rune(s), []rune(t) - column := make([]int, len(r1)+1) - - for y := 1; y <= len(r1); y++ { - column[y] = y - } - - for x := 1; x <= len(r2); x++ { - column[0] = x - - for y, lastDiag := 1, x-1; y <= len(r1); y++ { - oldDiag := column[y] - cost := 0 - if r1[y-1] != r2[x-1] { - cost = 1 - } - column[y] = min(column[y]+1, column[y-1]+1, lastDiag+cost) - lastDiag = oldDiag - } - } - - return column[len(r1)] -} - -func min(a, b, c int) int { - if a < b && a < c { - return a - } else if b < c { - return b - } - return c -} diff --git a/vendor/github.com/scaleway/scaleway-cli/LICENSE.md b/vendor/github.com/scaleway/scaleway-cli/LICENSE.md deleted file mode 100644 index 7503a16ca..000000000 --- a/vendor/github.com/scaleway/scaleway-cli/LICENSE.md +++ /dev/null @@ -1,22 +0,0 @@ -The MIT License -=============== - -Copyright (c) **2014-2016 Scaleway ([@scaleway](https://twitter.com/scaleway))** - -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/github.com/scaleway/scaleway-cli/pkg/api/README.md b/vendor/github.com/scaleway/scaleway-cli/pkg/api/README.md deleted file mode 100644 index 62dade6b9..000000000 --- a/vendor/github.com/scaleway/scaleway-cli/pkg/api/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# Scaleway's API - -## Deprecated in favor of https://github.com/scaleway/go-scaleway diff --git a/vendor/github.com/scaleway/scaleway-cli/pkg/api/api.go b/vendor/github.com/scaleway/scaleway-cli/pkg/api/api.go deleted file mode 100644 index 2b3d0288c..000000000 --- a/vendor/github.com/scaleway/scaleway-cli/pkg/api/api.go +++ /dev/null @@ -1,2844 +0,0 @@ -// Copyright (C) 2015 Scaleway. All rights reserved. -// Use of this source code is governed by a MIT-style -// license that can be found in the LICENSE.md file. - -// Interact with Scaleway API - -// Package api contains client and functions to interact with Scaleway API -package api - -import ( - "bytes" - "crypto/tls" - "encoding/json" - "errors" - "fmt" - "io" - "io/ioutil" - "net" - "net/http" - "net/http/httputil" - "net/url" - "os" - "sort" - "strconv" - "strings" - "text/tabwriter" - "text/template" - "time" - - "golang.org/x/sync/errgroup" -) - -// Default values -var ( - AccountAPI = "https://account.scaleway.com/" - MetadataAPI = "http://169.254.42.42/" - MarketplaceAPI = "https://api-marketplace.scaleway.com" - ComputeAPIPar1 = "https://cp-par1.scaleway.com/" - ComputeAPIAms1 = "https://cp-ams1.scaleway.com" - - URLPublicDNS = ".pub.cloud.scaleway.com" - URLPrivateDNS = ".priv.cloud.scaleway.com" -) - -func init() { - if url := os.Getenv("SCW_ACCOUNT_API"); url != "" { - AccountAPI = url - } - if url := os.Getenv("SCW_METADATA_API"); url != "" { - MetadataAPI = url - } - if url := os.Getenv("SCW_MARKETPLACE_API"); url != "" { - MarketplaceAPI = url - } - if url := os.Getenv("SCW_COMPUTE_PAR1_API"); url != "" { - ComputeAPIPar1 = url - } - if url := os.Getenv("SCW_COMPUTE_AMS1_API"); url != "" { - ComputeAPIAms1 = url - } -} - -const ( - perPage = 50 -) - -// ScalewayAPI is the interface used to communicate with the Scaleway API -type ScalewayAPI struct { - // Organization is the identifier of the Scaleway organization - Organization string - - // Token is the authentication token for the Scaleway organization - Token string - - // Password is the authentication password - password string - - userAgent string - - // Cache is used to quickly resolve identifiers from names - Cache *ScalewayCache - - client *http.Client - verbose bool - computeAPI string - - Region string - // - Logger -} - -// ScalewayAPIError represents a Scaleway API Error -type ScalewayAPIError struct { - // Message is a human-friendly error message - APIMessage string `json:"message,omitempty"` - - // Type is a string code that defines the kind of error - Type string `json:"type,omitempty"` - - // Fields contains detail about validation error - Fields map[string][]string `json:"fields,omitempty"` - - // StatusCode is the HTTP status code received - StatusCode int `json:"-"` - - // Message - Message string `json:"-"` -} - -// Error returns a string representing the error -func (e ScalewayAPIError) Error() string { - var b bytes.Buffer - - fmt.Fprintf(&b, "StatusCode: %v, ", e.StatusCode) - fmt.Fprintf(&b, "Type: %v, ", e.Type) - fmt.Fprintf(&b, "APIMessage: \x1b[31m%v\x1b[0m", e.APIMessage) - if len(e.Fields) > 0 { - fmt.Fprintf(&b, ", Details: %v", e.Fields) - } - return b.String() -} - -// HideAPICredentials removes API credentials from a string -func (s *ScalewayAPI) HideAPICredentials(input string) string { - output := input - if s.Token != "" { - output = strings.Replace(output, s.Token, "00000000-0000-4000-8000-000000000000", -1) - } - if s.Organization != "" { - output = strings.Replace(output, s.Organization, "00000000-0000-5000-9000-000000000000", -1) - } - if s.password != "" { - output = strings.Replace(output, s.password, "XX-XX-XX-XX", -1) - } - return output -} - -// ScalewayIPAddress represents a Scaleway IP address -type ScalewayIPAddress struct { - // Identifier is a unique identifier for the IP address - Identifier string `json:"id,omitempty"` - - // IP is an IPv4 address - IP string `json:"address,omitempty"` - - // Dynamic is a flag that defines an IP that change on each reboot - Dynamic *bool `json:"dynamic,omitempty"` -} - -// ScalewayVolume represents a Scaleway Volume -type ScalewayVolume struct { - // Identifier is a unique identifier for the volume - Identifier string `json:"id,omitempty"` - - // Size is the allocated size of the volume - Size uint64 `json:"size,omitempty"` - - // CreationDate is the creation date of the volume - CreationDate string `json:"creation_date,omitempty"` - - // ModificationDate is the date of the last modification of the volume - ModificationDate string `json:"modification_date,omitempty"` - - // Organization is the organization owning the volume - Organization string `json:"organization,omitempty"` - - // Name is the name of the volume - Name string `json:"name,omitempty"` - - // Server is the server using this image - Server *struct { - Identifier string `json:"id,omitempty"` - Name string `json:"name,omitempty"` - } `json:"server,omitempty"` - - // VolumeType is a Scaleway identifier for the kind of volume (default: l_ssd) - VolumeType string `json:"volume_type,omitempty"` - - // ExportURI represents the url used by initrd/scripts to attach the volume - ExportURI string `json:"export_uri,omitempty"` -} - -// ScalewayOneVolume represents the response of a GET /volumes/UUID API call -type ScalewayOneVolume struct { - Volume ScalewayVolume `json:"volume,omitempty"` -} - -// ScalewayVolumes represents a group of Scaleway volumes -type ScalewayVolumes struct { - // Volumes holds scaleway volumes of the response - Volumes []ScalewayVolume `json:"volumes,omitempty"` -} - -// ScalewayVolumeDefinition represents a Scaleway volume definition -type ScalewayVolumeDefinition struct { - // Name is the user-defined name of the volume - Name string `json:"name"` - - // Image is the image used by the volume - Size uint64 `json:"size"` - - // Bootscript is the bootscript used by the volume - Type string `json:"volume_type"` - - // Organization is the owner of the volume - Organization string `json:"organization"` -} - -// ScalewayVolumePutDefinition represents a Scaleway volume with nullable fields (for PUT) -type ScalewayVolumePutDefinition struct { - Identifier *string `json:"id,omitempty"` - Size *uint64 `json:"size,omitempty"` - CreationDate *string `json:"creation_date,omitempty"` - ModificationDate *string `json:"modification_date,omitempty"` - Organization *string `json:"organization,omitempty"` - Name *string `json:"name,omitempty"` - Server struct { - Identifier *string `json:"id,omitempty"` - Name *string `json:"name,omitempty"` - } `json:"server,omitempty"` - VolumeType *string `json:"volume_type,omitempty"` - ExportURI *string `json:"export_uri,omitempty"` -} - -// ScalewayImage represents a Scaleway Image -type ScalewayImage struct { - // Identifier is a unique identifier for the image - Identifier string `json:"id,omitempty"` - - // Name is a user-defined name for the image - Name string `json:"name,omitempty"` - - // CreationDate is the creation date of the image - CreationDate string `json:"creation_date,omitempty"` - - // ModificationDate is the date of the last modification of the image - ModificationDate string `json:"modification_date,omitempty"` - - // RootVolume is the root volume bound to the image - RootVolume ScalewayVolume `json:"root_volume,omitempty"` - - // Public is true for public images and false for user images - Public bool `json:"public,omitempty"` - - // Bootscript is the bootscript bound to the image - DefaultBootscript *ScalewayBootscript `json:"default_bootscript,omitempty"` - - // Organization is the owner of the image - Organization string `json:"organization,omitempty"` - - // Arch is the architecture target of the image - Arch string `json:"arch,omitempty"` - - // FIXME: extra_volumes -} - -// ScalewayImageIdentifier represents a Scaleway Image Identifier -type ScalewayImageIdentifier struct { - Identifier string - Arch string - Region string - Owner string -} - -// ScalewayOneImage represents the response of a GET /images/UUID API call -type ScalewayOneImage struct { - Image ScalewayImage `json:"image,omitempty"` -} - -// ScalewayImages represents a group of Scaleway images -type ScalewayImages struct { - // Images holds scaleway images of the response - Images []ScalewayImage `json:"images,omitempty"` -} - -// ProductNetworkInterface gives interval and external allowed bandwidth -type ProductNetworkInterface struct { - InternalBandwidth uint64 `json:"internal_bandwidth,omitempty"` - InternetBandwidth uint64 `json:"internet_bandwidth,omitempty"` -} - -// ProductNetwork lists all the network interfaces -type ProductNetwork struct { - Interfaces []ProductNetworkInterface `json:"interfaces,omitempty"` - TotalInternalBandwidth uint64 `json:"sum_internal_bandwidth,omitempty"` - TotalInternetBandwidth uint64 `json:"sum_internet_bandwidth,omitempty"` - IPv6_Support bool `json:"ipv6_support,omitempty"` -} - -// ProductVolumeConstraint contains any volume constraint that the offer has -type ProductVolumeConstraint struct { - MinSize uint64 `json:"min_size,omitempty"` - MaxSize uint64 `json:"max_size,omitempty"` -} - -// ProductServerOffer represents a specific offer -type ProductServer struct { - Arch string `json:"arch,omitempty"` - Ncpus uint64 `json:"ncpus,omitempty"` - Ram uint64 `json:"ram,omitempty"` - Baremetal bool `json:"baremetal,omitempty"` - VolumesConstraint ProductVolumeConstraint `json:"volumes_constraint,omitempty"` - AltNames []string `json:"alt_names,omitempty"` - Network ProductNetwork `json:"network,omitempty"` -} - -// Products holds a map of all Scaleway servers -type ScalewayProductsServers struct { - Servers map[string]ProductServer `json:"servers"` -} - -// ScalewaySnapshot represents a Scaleway Snapshot -type ScalewaySnapshot struct { - // Identifier is a unique identifier for the snapshot - Identifier string `json:"id,omitempty"` - - // Name is a user-defined name for the snapshot - Name string `json:"name,omitempty"` - - // CreationDate is the creation date of the snapshot - CreationDate string `json:"creation_date,omitempty"` - - // ModificationDate is the date of the last modification of the snapshot - ModificationDate string `json:"modification_date,omitempty"` - - // Size is the allocated size of the volume - Size uint64 `json:"size,omitempty"` - - // Organization is the owner of the snapshot - Organization string `json:"organization"` - - // State is the current state of the snapshot - State string `json:"state"` - - // VolumeType is the kind of volume behind the snapshot - VolumeType string `json:"volume_type"` - - // BaseVolume is the volume from which the snapshot inherits - BaseVolume ScalewayVolume `json:"base_volume,omitempty"` -} - -// ScalewayOneSnapshot represents the response of a GET /snapshots/UUID API call -type ScalewayOneSnapshot struct { - Snapshot ScalewaySnapshot `json:"snapshot,omitempty"` -} - -// ScalewaySnapshots represents a group of Scaleway snapshots -type ScalewaySnapshots struct { - // Snapshots holds scaleway snapshots of the response - Snapshots []ScalewaySnapshot `json:"snapshots,omitempty"` -} - -// ScalewayBootscript represents a Scaleway Bootscript -type ScalewayBootscript struct { - Bootcmdargs string `json:"bootcmdargs,omitempty"` - Dtb string `json:"dtb,omitempty"` - Initrd string `json:"initrd,omitempty"` - Kernel string `json:"kernel,omitempty"` - - // Arch is the architecture target of the bootscript - Arch string `json:"architecture,omitempty"` - - // Identifier is a unique identifier for the bootscript - Identifier string `json:"id,omitempty"` - - // Organization is the owner of the bootscript - Organization string `json:"organization,omitempty"` - - // Name is a user-defined name for the bootscript - Title string `json:"title,omitempty"` - - // Public is true for public bootscripts and false for user bootscripts - Public bool `json:"public,omitempty"` - - Default bool `json:"default,omitempty"` -} - -// ScalewayOneBootscript represents the response of a GET /bootscripts/UUID API call -type ScalewayOneBootscript struct { - Bootscript ScalewayBootscript `json:"bootscript,omitempty"` -} - -// ScalewayBootscripts represents a group of Scaleway bootscripts -type ScalewayBootscripts struct { - // Bootscripts holds Scaleway bootscripts of the response - Bootscripts []ScalewayBootscript `json:"bootscripts,omitempty"` -} - -// ScalewayTask represents a Scaleway Task -type ScalewayTask struct { - // Identifier is a unique identifier for the task - Identifier string `json:"id,omitempty"` - - // StartDate is the start date of the task - StartDate string `json:"started_at,omitempty"` - - // TerminationDate is the termination date of the task - TerminationDate string `json:"terminated_at,omitempty"` - - HrefFrom string `json:"href_from,omitempty"` - - Description string `json:"description,omitempty"` - - Status string `json:"status,omitempty"` - - Progress int `json:"progress,omitempty"` -} - -// ScalewayOneTask represents the response of a GET /tasks/UUID API call -type ScalewayOneTask struct { - Task ScalewayTask `json:"task,omitempty"` -} - -// ScalewayTasks represents a group of Scaleway tasks -type ScalewayTasks struct { - // Tasks holds scaleway tasks of the response - Tasks []ScalewayTask `json:"tasks,omitempty"` -} - -// ScalewaySecurityGroupRule definition -type ScalewaySecurityGroupRule struct { - Direction string `json:"direction"` - Protocol string `json:"protocol"` - IPRange string `json:"ip_range"` - DestPortFrom int `json:"dest_port_from,omitempty"` - Action string `json:"action"` - Position int `json:"position"` - DestPortTo string `json:"dest_port_to"` - Editable bool `json:"editable"` - ID string `json:"id"` -} - -// ScalewayGetSecurityGroupRules represents the response of a GET /security_group/{groupID}/rules -type ScalewayGetSecurityGroupRules struct { - Rules []ScalewaySecurityGroupRule `json:"rules"` -} - -// ScalewayGetSecurityGroupRule represents the response of a GET /security_group/{groupID}/rules/{ruleID} -type ScalewayGetSecurityGroupRule struct { - Rules ScalewaySecurityGroupRule `json:"rule"` -} - -// ScalewayNewSecurityGroupRule definition POST/PUT request /security_group/{groupID} -type ScalewayNewSecurityGroupRule struct { - Action string `json:"action"` - Direction string `json:"direction"` - IPRange string `json:"ip_range"` - Protocol string `json:"protocol"` - DestPortFrom int `json:"dest_port_from,omitempty"` -} - -// ScalewaySecurityGroups definition -type ScalewaySecurityGroups struct { - Description string `json:"description"` - ID string `json:"id"` - Organization string `json:"organization"` - Name string `json:"name"` - Servers []ScalewaySecurityGroup `json:"servers"` - EnableDefaultSecurity bool `json:"enable_default_security"` - OrganizationDefault bool `json:"organization_default"` - Stateful bool `json:"stateful"` - InboundDefaultPolicy string `json:"inbound_default_policy"` - OutboundDefaultPolicy string `json:"outbound_default_policy"` -} - -// ScalewayGetSecurityGroups represents the response of a GET /security_groups/ -type ScalewayGetSecurityGroups struct { - SecurityGroups []ScalewaySecurityGroups `json:"security_groups"` -} - -// ScalewayGetSecurityGroup represents the response of a GET /security_groups/{groupID} -type ScalewayGetSecurityGroup struct { - SecurityGroups ScalewaySecurityGroups `json:"security_group"` -} - -// ScalewayIPDefinition represents the IP's fields -type ScalewayIPDefinition struct { - Organization string `json:"organization"` - Reverse *string `json:"reverse"` - ID string `json:"id"` - Server *struct { - Identifier string `json:"id,omitempty"` - Name string `json:"name,omitempty"` - } `json:"server"` - Address string `json:"address"` -} - -// ScalewayGetIPS represents the response of a GET /ips/ -type ScalewayGetIPS struct { - IPS []ScalewayIPDefinition `json:"ips"` -} - -// ScalewayGetIP represents the response of a GET /ips/{id_ip} -type ScalewayGetIP struct { - IP ScalewayIPDefinition `json:"ip"` -} - -// ScalewaySecurityGroup represents a Scaleway security group -type ScalewaySecurityGroup struct { - // Identifier is a unique identifier for the security group - Identifier string `json:"id,omitempty"` - - // Name is the user-defined name of the security group - Name string `json:"name,omitempty"` -} - -// ScalewayNewSecurityGroup definition POST request /security_groups -type ScalewayNewSecurityGroup struct { - Organization string `json:"organization"` - Name string `json:"name"` - Description string `json:"description"` - Stateful bool `json:"stateful"` - InboundDefaultPolicy string `json:"inbound_default_policy"` - OutboundDefaultPolicy string `json:"outbound_default_policy"` -} - -// ScalewayUpdateSecurityGroup definition PUT request /security_groups -type ScalewayUpdateSecurityGroup struct { - Organization string `json:"organization"` - Name string `json:"name"` - Description string `json:"description"` - OrganizationDefault bool `json:"organization_default"` - Stateful bool `json:"stateful"` - InboundDefaultPolicy string `json:"inbound_default_policy"` - OutboundDefaultPolicy string `json:"outbound_default_policy"` -} - -// ScalewayServer represents a Scaleway server -type ScalewayServer struct { - // Arch is the architecture target of the server - Arch string `json:"arch,omitempty"` - - // Identifier is a unique identifier for the server - Identifier string `json:"id,omitempty"` - - // Name is the user-defined name of the server - Name string `json:"name,omitempty"` - - // CreationDate is the creation date of the server - CreationDate string `json:"creation_date,omitempty"` - - // ModificationDate is the date of the last modification of the server - ModificationDate string `json:"modification_date,omitempty"` - - // Image is the image used by the server - Image ScalewayImage `json:"image,omitempty"` - - // DynamicIPRequired is a flag that defines a server with a dynamic ip address attached - DynamicIPRequired *bool `json:"dynamic_ip_required,omitempty"` - - // PublicIP is the public IP address bound to the server - PublicAddress ScalewayIPAddress `json:"public_ip,omitempty"` - - // State is the current status of the server - State string `json:"state,omitempty"` - - // BootType is the boot method used, can be local or bootscript - BootType string `json:"boot_type,omitempty"` - - // StateDetail is the detailed status of the server - StateDetail string `json:"state_detail,omitempty"` - - // PrivateIP represents the private IPV4 attached to the server (changes on each boot) - PrivateIP string `json:"private_ip,omitempty"` - - // Bootscript is the unique identifier of the selected bootscript - Bootscript *ScalewayBootscript `json:"bootscript,omitempty"` - - // Hostname represents the ServerName in a format compatible with unix's hostname - Hostname string `json:"hostname,omitempty"` - - // Tags represents user-defined tags - Tags []string `json:"tags,omitempty"` - - // Volumes are the attached volumes - Volumes map[string]ScalewayVolume `json:"volumes,omitempty"` - - // SecurityGroup is the selected security group object - SecurityGroup ScalewaySecurityGroup `json:"security_group,omitempty"` - - // Organization is the owner of the server - Organization string `json:"organization,omitempty"` - - // CommercialType is the commercial type of the server (i.e: C1, C2[SML], START1-S) - CommercialType string `json:"commercial_type,omitempty"` - - // Location of the server - Location struct { - Platform string `json:"platform_id,omitempty"` - Chassis string `json:"chassis_id,omitempty"` - Cluster string `json:"cluster_id,omitempty"` - Hypervisor string `json:"hypervisor_id,omitempty"` - Blade string `json:"blade_id,omitempty"` - Node string `json:"node_id,omitempty"` - ZoneID string `json:"zone_id,omitempty"` - } `json:"location,omitempty"` - - IPV6 *ScalewayIPV6Definition `json:"ipv6,omitempty"` - - EnableIPV6 bool `json:"enable_ipv6,omitempty"` - - // This fields are not returned by the API, we generate it - DNSPublic string `json:"dns_public,omitempty"` - DNSPrivate string `json:"dns_private,omitempty"` -} - -// ScalewayIPV6Definition represents a Scaleway ipv6 -type ScalewayIPV6Definition struct { - Netmask string `json:"netmask"` - Gateway string `json:"gateway"` - Address string `json:"address"` -} - -// ScalewayServerPatchDefinition represents a Scaleway server with nullable fields (for PATCH) -type ScalewayServerPatchDefinition struct { - Arch *string `json:"arch,omitempty"` - Name *string `json:"name,omitempty"` - CreationDate *string `json:"creation_date,omitempty"` - ModificationDate *string `json:"modification_date,omitempty"` - Image *ScalewayImage `json:"image,omitempty"` - DynamicIPRequired *bool `json:"dynamic_ip_required,omitempty"` - PublicAddress *ScalewayIPAddress `json:"public_ip,omitempty"` - State *string `json:"state,omitempty"` - StateDetail *string `json:"state_detail,omitempty"` - PrivateIP *string `json:"private_ip,omitempty"` - Bootscript *string `json:"bootscript,omitempty"` - Hostname *string `json:"hostname,omitempty"` - Volumes *map[string]ScalewayVolume `json:"volumes,omitempty"` - SecurityGroup *ScalewaySecurityGroup `json:"security_group,omitempty"` - Organization *string `json:"organization,omitempty"` - Tags *[]string `json:"tags,omitempty"` - IPV6 *ScalewayIPV6Definition `json:"ipv6,omitempty"` - EnableIPV6 *bool `json:"enable_ipv6,omitempty"` - BootType *string `json:"boot_type,omitempty"` -} - -// ScalewayServerDefinition represents a Scaleway server with image definition -type ScalewayServerDefinition struct { - // Name is the user-defined name of the server - Name string `json:"name"` - - // Image is the image used by the server - Image *string `json:"image,omitempty"` - - // Volumes are the attached volumes - Volumes map[string]string `json:"volumes,omitempty"` - - // DynamicIPRequired is a flag that defines a server with a dynamic ip address attached - DynamicIPRequired *bool `json:"dynamic_ip_required,omitempty"` - - // Bootscript is the bootscript used by the server - Bootscript *string `json:"bootscript"` - - // Tags are the metadata tags attached to the server - Tags []string `json:"tags,omitempty"` - - // Organization is the owner of the server - Organization string `json:"organization"` - - // CommercialType is the commercial type of the server (i.e: C1, C2[SML], START1-S) - CommercialType string `json:"commercial_type"` - - PublicIP string `json:"public_ip,omitempty"` - - EnableIPV6 bool `json:"enable_ipv6,omitempty"` - - SecurityGroup string `json:"security_group,omitempty"` - - BootType string `json:"boot_type,omitempty"` -} - -// ScalewayOneServer represents the response of a GET /servers/UUID API call -type ScalewayOneServer struct { - Server ScalewayServer `json:"server,omitempty"` -} - -// ScalewayServers represents a group of Scaleway servers -type ScalewayServers struct { - // Servers holds scaleway servers of the response - Servers []ScalewayServer `json:"servers,omitempty"` -} - -// ScalewayServerAction represents an action to perform on a Scaleway server -type ScalewayServerAction struct { - // Action is the name of the action to trigger - Action string `json:"action,omitempty"` -} - -// ScalewaySnapshotDefinition represents a Scaleway snapshot definition -type ScalewaySnapshotDefinition struct { - VolumeIDentifier string `json:"volume_id"` - Name string `json:"name,omitempty"` - Organization string `json:"organization"` -} - -// ScalewayImageDefinition represents a Scaleway image definition -type ScalewayImageDefinition struct { - SnapshotIDentifier string `json:"root_volume"` - Name string `json:"name,omitempty"` - Organization string `json:"organization"` - Arch string `json:"arch"` - DefaultBootscript *string `json:"default_bootscript,omitempty"` -} - -// ScalewayRoleDefinition represents a Scaleway Token UserId Role -type ScalewayRoleDefinition struct { - Organization ScalewayOrganizationDefinition `json:"organization,omitempty"` - Role string `json:"role,omitempty"` -} - -// ScalewayTokenDefinition represents a Scaleway Token -type ScalewayTokenDefinition struct { - UserID string `json:"user_id"` - Description string `json:"description,omitempty"` - Roles ScalewayRoleDefinition `json:"roles"` - Expires string `json:"expires"` - InheritsUsersPerms bool `json:"inherits_user_perms"` - ID string `json:"id"` -} - -// ScalewayTokensDefinition represents a Scaleway Tokens -type ScalewayTokensDefinition struct { - Token ScalewayTokenDefinition `json:"token"` -} - -// ScalewayGetTokens represents a list of Scaleway Tokens -type ScalewayGetTokens struct { - Tokens []ScalewayTokenDefinition `json:"tokens"` -} - -// ScalewayContainerData represents a Scaleway container data (S3) -type ScalewayContainerData struct { - LastModified string `json:"last_modified"` - Name string `json:"name"` - Size string `json:"size"` -} - -// ScalewayGetContainerDatas represents a list of Scaleway containers data (S3) -type ScalewayGetContainerDatas struct { - Container []ScalewayContainerData `json:"container"` -} - -// ScalewayContainer represents a Scaleway container (S3) -type ScalewayContainer struct { - ScalewayOrganizationDefinition `json:"organization"` - Name string `json:"name"` - Size string `json:"size"` -} - -// ScalewayGetContainers represents a list of Scaleway containers (S3) -type ScalewayGetContainers struct { - Containers []ScalewayContainer `json:"containers"` -} - -// ScalewayConnectResponse represents the answer from POST /tokens -type ScalewayConnectResponse struct { - Token ScalewayTokenDefinition `json:"token"` -} - -// ScalewayConnect represents the data to connect -type ScalewayConnect struct { - Email string `json:"email"` - Password string `json:"password"` - Description string `json:"description"` - Expires bool `json:"expires"` -} - -// ScalewayConnectInterface is the interface implemented by ScalewayConnect, -// ScalewayConnectByOTP and ScalewayConnectByBackupCode -type ScalewayConnectInterface interface { - GetPassword() string -} - -func (s *ScalewayConnect) GetPassword() string { - return s.Password -} - -type ScalewayConnectByOTP struct { - ScalewayConnect - TwoFAToken string `json:"2FA_token"` -} - -type ScalewayConnectByBackupCode struct { - ScalewayConnect - TwoFABackupCode string `json:"2FA_backup_code"` -} - -// ScalewayOrganizationDefinition represents a Scaleway Organization -type ScalewayOrganizationDefinition struct { - ID string `json:"id"` - Name string `json:"name"` - Users []ScalewayUserDefinition `json:"users"` -} - -// ScalewayOrganizationsDefinition represents a Scaleway Organizations -type ScalewayOrganizationsDefinition struct { - Organizations []ScalewayOrganizationDefinition `json:"organizations"` -} - -// ScalewayUserDefinition represents a Scaleway User -type ScalewayUserDefinition struct { - Email string `json:"email"` - Firstname string `json:"firstname"` - Fullname string `json:"fullname"` - ID string `json:"id"` - Lastname string `json:"lastname"` - Organizations []ScalewayOrganizationDefinition `json:"organizations"` - Roles []ScalewayRoleDefinition `json:"roles"` - SSHPublicKeys []ScalewayKeyDefinition `json:"ssh_public_keys"` -} - -// ScalewayUsersDefinition represents the response of a GET /user -type ScalewayUsersDefinition struct { - User ScalewayUserDefinition `json:"user"` -} - -// ScalewayKeyDefinition represents a key -type ScalewayKeyDefinition struct { - Key string `json:"key"` - Fingerprint string `json:"fingerprint,omitempty"` -} - -// ScalewayUserPatchSSHKeyDefinition represents a User Patch -type ScalewayUserPatchSSHKeyDefinition struct { - SSHPublicKeys []ScalewayKeyDefinition `json:"ssh_public_keys"` -} - -// ScalewayDashboardResp represents a dashboard received from the API -type ScalewayDashboardResp struct { - Dashboard ScalewayDashboard -} - -// ScalewayDashboard represents a dashboard -type ScalewayDashboard struct { - VolumesCount int `json:"volumes_count"` - RunningServersCount int `json:"running_servers_count"` - ImagesCount int `json:"images_count"` - SnapshotsCount int `json:"snapshots_count"` - ServersCount int `json:"servers_count"` - IPsCount int `json:"ips_count"` -} - -// ScalewayPermissions represents the response of GET /permissions -type ScalewayPermissions map[string]ScalewayPermCategory - -// ScalewayPermCategory represents ScalewayPermissions's fields -type ScalewayPermCategory map[string][]string - -// ScalewayPermissionDefinition represents the permissions -type ScalewayPermissionDefinition struct { - Permissions ScalewayPermissions `json:"permissions"` -} - -// ScalewayUserdatas represents the response of a GET /user_data -type ScalewayUserdatas struct { - UserData []string `json:"user_data"` -} - -// ScalewayQuota represents a map of quota (name, value) -type ScalewayQuota map[string]int - -// ScalewayGetQuotas represents the response of GET /organizations/{orga_id}/quotas -type ScalewayGetQuotas struct { - Quotas ScalewayQuota `json:"quotas"` -} - -// ScalewayUserdata represents []byte -type ScalewayUserdata []byte - -// FuncMap used for json inspection -var FuncMap = template.FuncMap{ - "json": func(v interface{}) string { - a, _ := json.Marshal(v) - return string(a) - }, -} - -// MarketLocalImageDefinition represents localImage of marketplace version -type MarketLocalImageDefinition struct { - Arch string `json:"arch"` - ID string `json:"id"` - Zone string `json:"zone"` -} - -// MarketLocalImages represents an array of local images -type MarketLocalImages struct { - LocalImages []MarketLocalImageDefinition `json:"local_images"` -} - -// MarketLocalImage represents local image -type MarketLocalImage struct { - LocalImages MarketLocalImageDefinition `json:"local_image"` -} - -// MarketVersionDefinition represents version of marketplace image -type MarketVersionDefinition struct { - CreationDate string `json:"creation_date"` - ID string `json:"id"` - Image struct { - ID string `json:"id"` - Name string `json:"name"` - } `json:"image"` - ModificationDate string `json:"modification_date"` - Name string `json:"name"` - MarketLocalImages -} - -// MarketVersions represents an array of marketplace image versions -type MarketVersions struct { - Versions []MarketVersionDefinition `json:"versions"` -} - -// MarketVersion represents version of marketplace image -type MarketVersion struct { - Version MarketVersionDefinition `json:"version"` -} - -// MarketImage represents MarketPlace image -type MarketImage struct { - Categories []string `json:"categories"` - CreationDate string `json:"creation_date"` - CurrentPublicVersion string `json:"current_public_version"` - Description string `json:"description"` - ID string `json:"id"` - Logo string `json:"logo"` - ModificationDate string `json:"modification_date"` - Name string `json:"name"` - Organization struct { - ID string `json:"id"` - Name string `json:"name"` - } `json:"organization"` - Public bool `json:"-"` - MarketVersions -} - -// MarketImages represents MarketPlace images -type MarketImages struct { - Images []MarketImage `json:"images"` -} - -// NewScalewayAPI creates a ready-to-use ScalewayAPI client -func NewScalewayAPI(organization, token, userAgent, region string, options ...func(*ScalewayAPI)) (*ScalewayAPI, error) { - s := &ScalewayAPI{ - // exposed - Organization: organization, - Token: token, - Logger: NewDefaultLogger(), - - // internal - client: &http.Client{}, - verbose: os.Getenv("SCW_VERBOSE_API") != "", - password: "", - userAgent: userAgent, - } - for _, option := range options { - option(s) - } - cache, err := NewScalewayCache(func() { s.Logger.Debugf("Writing cache file to disk") }) - if err != nil { - return nil, err - } - s.Cache = cache - if os.Getenv("SCW_TLSVERIFY") == "0" { - s.client.Transport = &http.Transport{ - TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, - } - } - switch region { - case "par1", "": - s.computeAPI = ComputeAPIPar1 - case "ams1": - s.computeAPI = ComputeAPIAms1 - default: - return nil, fmt.Errorf("%s isn't a valid region", region) - } - s.Region = region - if url := os.Getenv("SCW_COMPUTE_API"); url != "" { - s.computeAPI = url - } - return s, nil -} - -// ClearCache clears the cache -func (s *ScalewayAPI) ClearCache() { - s.Cache.Clear() -} - -// Sync flushes out the cache to the disk -func (s *ScalewayAPI) Sync() { - s.Cache.Save() -} - -func (s *ScalewayAPI) response(method, uri string, content io.Reader) (resp *http.Response, err error) { - var ( - req *http.Request - ) - - req, err = http.NewRequest(method, uri, content) - if err != nil { - err = fmt.Errorf("response %s %s", method, uri) - return - } - req.Header.Set("X-Auth-Token", s.Token) - req.Header.Set("Content-Type", "application/json") - req.Header.Set("User-Agent", s.userAgent) - s.LogHTTP(req) - if s.verbose { - dump, _ := httputil.DumpRequest(req, true) - s.Debugf("%v", string(dump)) - } else { - s.Debugf("[%s]: %v", method, uri) - } - resp, err = s.client.Do(req) - return -} - -// GetResponsePaginate fetchs all resources and returns an http.Response object for the requested resource -func (s *ScalewayAPI) GetResponsePaginate(apiURL, resource string, values url.Values) (*http.Response, error) { - resp, err := s.response("HEAD", fmt.Sprintf("%s/%s?%s", strings.TrimRight(apiURL, "/"), resource, values.Encode()), nil) - if err != nil { - return nil, err - } - - count := resp.Header.Get("X-Total-Count") - var maxElem int - if count == "" { - maxElem = 0 - } else { - maxElem, err = strconv.Atoi(count) - if err != nil { - return nil, err - } - } - - get := maxElem / perPage - if (float32(maxElem) / perPage) > float32(get) { - get++ - } - - if get <= 1 { // If there is 0 or 1 page of result, the response is not paginated - if len(values) == 0 { - return s.response("GET", fmt.Sprintf("%s/%s", strings.TrimRight(apiURL, "/"), resource), nil) - } - return s.response("GET", fmt.Sprintf("%s/%s?%s", strings.TrimRight(apiURL, "/"), resource, values.Encode()), nil) - } - - fetchAll := !(values.Get("per_page") != "" || values.Get("page") != "") - if fetchAll { - var g errgroup.Group - - ch := make(chan *http.Response, get) - for i := 1; i <= get; i++ { - i := i // closure tricks - g.Go(func() (err error) { - var resp *http.Response - - val := url.Values{} - val.Set("per_page", fmt.Sprintf("%v", perPage)) - val.Set("page", fmt.Sprintf("%v", i)) - resp, err = s.response("GET", fmt.Sprintf("%s/%s?%s", strings.TrimRight(apiURL, "/"), resource, val.Encode()), nil) - ch <- resp - return - }) - } - if err = g.Wait(); err != nil { - return nil, err - } - newBody := make(map[string][]json.RawMessage) - body := make(map[string][]json.RawMessage) - key := "" - for i := 0; i < get; i++ { - res := <-ch - if res.StatusCode != http.StatusOK { - return res, nil - } - content, err := ioutil.ReadAll(res.Body) - res.Body.Close() - if err != nil { - return nil, err - } - if err := json.Unmarshal(content, &body); err != nil { - return nil, err - } - - if i == 0 { - resp = res - for k := range body { - key = k - break - } - } - newBody[key] = append(newBody[key], body[key]...) - } - payload := new(bytes.Buffer) - if err := json.NewEncoder(payload).Encode(newBody); err != nil { - return nil, err - } - resp.Body = ioutil.NopCloser(payload) - } else { - resp, err = s.response("GET", fmt.Sprintf("%s/%s?%s", strings.TrimRight(apiURL, "/"), resource, values.Encode()), nil) - } - return resp, err -} - -// PostResponse returns an http.Response object for the updated resource -func (s *ScalewayAPI) PostResponse(apiURL, resource string, data interface{}) (*http.Response, error) { - payload := new(bytes.Buffer) - if err := json.NewEncoder(payload).Encode(data); err != nil { - return nil, err - } - return s.response("POST", fmt.Sprintf("%s/%s", strings.TrimRight(apiURL, "/"), resource), payload) -} - -// PatchResponse returns an http.Response object for the updated resource -func (s *ScalewayAPI) PatchResponse(apiURL, resource string, data interface{}) (*http.Response, error) { - payload := new(bytes.Buffer) - if err := json.NewEncoder(payload).Encode(data); err != nil { - return nil, err - } - return s.response("PATCH", fmt.Sprintf("%s/%s", strings.TrimRight(apiURL, "/"), resource), payload) -} - -// PutResponse returns an http.Response object for the updated resource -func (s *ScalewayAPI) PutResponse(apiURL, resource string, data interface{}) (*http.Response, error) { - payload := new(bytes.Buffer) - if err := json.NewEncoder(payload).Encode(data); err != nil { - return nil, err - } - return s.response("PUT", fmt.Sprintf("%s/%s", strings.TrimRight(apiURL, "/"), resource), payload) -} - -// DeleteResponse returns an http.Response object for the deleted resource -func (s *ScalewayAPI) DeleteResponse(apiURL, resource string) (*http.Response, error) { - return s.response("DELETE", fmt.Sprintf("%s/%s", strings.TrimRight(apiURL, "/"), resource), nil) -} - -// handleHTTPError checks the statusCode and displays the error -func (s *ScalewayAPI) handleHTTPError(goodStatusCode []int, resp *http.Response) ([]byte, error) { - body, err := ioutil.ReadAll(resp.Body) - if err != nil { - return nil, err - } - if s.verbose { - resp.Body = ioutil.NopCloser(bytes.NewBuffer(body)) - dump, err := httputil.DumpResponse(resp, true) - if err == nil { - var js bytes.Buffer - - err = json.Indent(&js, body, "", " ") - if err != nil { - s.Debugf("[Response]: [%v]\n%v", resp.StatusCode, string(dump)) - } else { - s.Debugf("[Response]: [%v]\n%v", resp.StatusCode, js.String()) - } - } - } else { - s.Debugf("[Response]: [%v]\n%v", resp.StatusCode, string(body)) - } - - if resp.StatusCode >= http.StatusInternalServerError { - return nil, errors.New(string(body)) - } - good := false - for _, code := range goodStatusCode { - if code == resp.StatusCode { - good = true - } - } - if !good { - var scwError ScalewayAPIError - - if err := json.Unmarshal(body, &scwError); err != nil { - return nil, err - } - scwError.StatusCode = resp.StatusCode - s.Debugf("%s", scwError.Error()) - return nil, scwError - } - return body, nil -} - -func (s *ScalewayAPI) fetchServers(api string, query url.Values, out chan<- ScalewayServers) func() error { - return func() error { - resp, err := s.GetResponsePaginate(api, "servers", query) - if err != nil { - return err - } - defer resp.Body.Close() - - body, err := s.handleHTTPError([]int{http.StatusOK}, resp) - if err != nil { - return err - } - var servers ScalewayServers - - if err = json.Unmarshal(body, &servers); err != nil { - return err - } - out <- servers - return nil - } -} - -// GetServers gets the list of servers from the ScalewayAPI -func (s *ScalewayAPI) GetServers(all bool, limit int) (*[]ScalewayServer, error) { - query := url.Values{} - if !all { - query.Set("state", "running") - } - if limit > 0 { - // FIXME: wait for the API to be ready - // query.Set("per_page", strconv.Itoa(limit)) - panic("Not implemented yet") - } - if all && limit == 0 { - s.Cache.ClearServers() - } - var ( - g errgroup.Group - apis = []string{ - ComputeAPIPar1, - ComputeAPIAms1, - } - ) - - serverChan := make(chan ScalewayServers, len(apis)) - for _, api := range apis { - g.Go(s.fetchServers(api, query, serverChan)) - } - - if err := g.Wait(); err != nil { - return nil, err - } - close(serverChan) - var servers ScalewayServers - - for server := range serverChan { - servers.Servers = append(servers.Servers, server.Servers...) - } - - for i, server := range servers.Servers { - servers.Servers[i].DNSPublic = server.Identifier + URLPublicDNS - servers.Servers[i].DNSPrivate = server.Identifier + URLPrivateDNS - s.Cache.InsertServer(server.Identifier, server.Location.ZoneID, server.Arch, server.Organization, server.Name) - } - return &servers.Servers, nil -} - -// ScalewaySortServers represents a wrapper to sort by CreationDate the servers -type ScalewaySortServers []ScalewayServer - -func (s ScalewaySortServers) Len() int { - return len(s) -} - -func (s ScalewaySortServers) Swap(i, j int) { - s[i], s[j] = s[j], s[i] -} - -func (s ScalewaySortServers) Less(i, j int) bool { - date1, _ := time.Parse("2006-01-02T15:04:05.000000+00:00", s[i].CreationDate) - date2, _ := time.Parse("2006-01-02T15:04:05.000000+00:00", s[j].CreationDate) - return date2.Before(date1) -} - -// GetServer gets a server from the ScalewayAPI -func (s *ScalewayAPI) GetServer(serverID string) (*ScalewayServer, error) { - if serverID == "" { - return nil, fmt.Errorf("cannot get server without serverID") - } - resp, err := s.GetResponsePaginate(s.computeAPI, "servers/"+serverID, url.Values{}) - if err != nil { - return nil, err - } - defer resp.Body.Close() - - body, err := s.handleHTTPError([]int{http.StatusOK}, resp) - if err != nil { - return nil, err - } - - var oneServer ScalewayOneServer - - if err = json.Unmarshal(body, &oneServer); err != nil { - return nil, err - } - // FIXME arch, owner, title - oneServer.Server.DNSPublic = oneServer.Server.Identifier + URLPublicDNS - oneServer.Server.DNSPrivate = oneServer.Server.Identifier + URLPrivateDNS - s.Cache.InsertServer(oneServer.Server.Identifier, oneServer.Server.Location.ZoneID, oneServer.Server.Arch, oneServer.Server.Organization, oneServer.Server.Name) - return &oneServer.Server, nil -} - -// PostServerAction posts an action on a server -func (s *ScalewayAPI) PostServerAction(serverID, action string) error { - data := ScalewayServerAction{ - Action: action, - } - resp, err := s.PostResponse(s.computeAPI, fmt.Sprintf("servers/%s/action", serverID), data) - if err != nil { - return err - } - defer resp.Body.Close() - - _, err = s.handleHTTPError([]int{http.StatusAccepted}, resp) - return err -} - -// DeleteServer deletes a server -func (s *ScalewayAPI) DeleteServer(serverID string) error { - defer s.Cache.RemoveServer(serverID) - resp, err := s.DeleteResponse(s.computeAPI, fmt.Sprintf("servers/%s", serverID)) - if err != nil { - return err - } - defer resp.Body.Close() - - if _, err = s.handleHTTPError([]int{http.StatusNoContent}, resp); err != nil { - return err - } - return nil -} - -// PostServer creates a new server -func (s *ScalewayAPI) PostServer(definition ScalewayServerDefinition) (string, error) { - definition.Organization = s.Organization - - resp, err := s.PostResponse(s.computeAPI, "servers", definition) - if err != nil { - return "", err - } - defer resp.Body.Close() - - body, err := s.handleHTTPError([]int{http.StatusCreated}, resp) - if err != nil { - return "", err - } - var server ScalewayOneServer - - if err = json.Unmarshal(body, &server); err != nil { - return "", err - } - // FIXME arch, owner, title - s.Cache.InsertServer(server.Server.Identifier, server.Server.Location.ZoneID, server.Server.Arch, server.Server.Organization, server.Server.Name) - return server.Server.Identifier, nil -} - -// PatchUserSSHKey updates a user -func (s *ScalewayAPI) PatchUserSSHKey(UserID string, definition ScalewayUserPatchSSHKeyDefinition) error { - resp, err := s.PatchResponse(AccountAPI, fmt.Sprintf("users/%s", UserID), definition) - if err != nil { - return err - } - - defer resp.Body.Close() - - if _, err := s.handleHTTPError([]int{http.StatusOK}, resp); err != nil { - return err - } - return nil -} - -// PatchServer updates a server -func (s *ScalewayAPI) PatchServer(serverID string, definition ScalewayServerPatchDefinition) error { - resp, err := s.PatchResponse(s.computeAPI, fmt.Sprintf("servers/%s", serverID), definition) - if err != nil { - return err - } - defer resp.Body.Close() - - if _, err := s.handleHTTPError([]int{http.StatusOK}, resp); err != nil { - return err - } - return nil -} - -// PostSnapshot creates a new snapshot -func (s *ScalewayAPI) PostSnapshot(volumeID string, name string) (string, error) { - definition := ScalewaySnapshotDefinition{ - VolumeIDentifier: volumeID, - Name: name, - Organization: s.Organization, - } - resp, err := s.PostResponse(s.computeAPI, "snapshots", definition) - if err != nil { - return "", err - } - defer resp.Body.Close() - - body, err := s.handleHTTPError([]int{http.StatusCreated}, resp) - if err != nil { - return "", err - } - var snapshot ScalewayOneSnapshot - - if err = json.Unmarshal(body, &snapshot); err != nil { - return "", err - } - // FIXME arch, owner, title - s.Cache.InsertSnapshot(snapshot.Snapshot.Identifier, s.Region, "", snapshot.Snapshot.Organization, snapshot.Snapshot.Name) - return snapshot.Snapshot.Identifier, nil -} - -// PostImage creates a new image -func (s *ScalewayAPI) PostImage(volumeID string, name string, bootscript string, arch string) (string, error) { - definition := ScalewayImageDefinition{ - SnapshotIDentifier: volumeID, - Name: name, - Organization: s.Organization, - Arch: arch, - } - if bootscript != "" { - definition.DefaultBootscript = &bootscript - } - - resp, err := s.PostResponse(s.computeAPI, "images", definition) - if err != nil { - return "", err - } - defer resp.Body.Close() - - body, err := s.handleHTTPError([]int{http.StatusCreated}, resp) - if err != nil { - return "", err - } - var image ScalewayOneImage - - if err = json.Unmarshal(body, &image); err != nil { - return "", err - } - // FIXME region, arch, owner, title - s.Cache.InsertImage(image.Image.Identifier, s.Region, image.Image.Arch, image.Image.Organization, image.Image.Name, "") - return image.Image.Identifier, nil -} - -// PostVolume creates a new volume -func (s *ScalewayAPI) PostVolume(definition ScalewayVolumeDefinition) (string, error) { - definition.Organization = s.Organization - if definition.Type == "" { - definition.Type = "l_ssd" - } - - resp, err := s.PostResponse(s.computeAPI, "volumes", definition) - if err != nil { - return "", err - } - defer resp.Body.Close() - - body, err := s.handleHTTPError([]int{http.StatusCreated}, resp) - if err != nil { - return "", err - } - var volume ScalewayOneVolume - - if err = json.Unmarshal(body, &volume); err != nil { - return "", err - } - // FIXME: s.Cache.InsertVolume(volume.Volume.Identifier, volume.Volume.Name) - return volume.Volume.Identifier, nil -} - -// PutVolume updates a volume -func (s *ScalewayAPI) PutVolume(volumeID string, definition ScalewayVolumePutDefinition) error { - resp, err := s.PutResponse(s.computeAPI, fmt.Sprintf("volumes/%s", volumeID), definition) - if err != nil { - return err - } - defer resp.Body.Close() - - _, err = s.handleHTTPError([]int{http.StatusOK}, resp) - return err -} - -// ResolveServer attempts to find a matching Identifier for the input string -func (s *ScalewayAPI) ResolveServer(needle string) (ScalewayResolverResults, error) { - servers, err := s.Cache.LookUpServers(needle, true) - if err != nil { - return servers, err - } - if len(servers) == 0 { - if _, err = s.GetServers(true, 0); err != nil { - return nil, err - } - servers, err = s.Cache.LookUpServers(needle, true) - } - return servers, err -} - -// ResolveVolume attempts to find a matching Identifier for the input string -func (s *ScalewayAPI) ResolveVolume(needle string) (ScalewayResolverResults, error) { - volumes, err := s.Cache.LookUpVolumes(needle, true) - if err != nil { - return volumes, err - } - if len(volumes) == 0 { - if _, err = s.GetVolumes(); err != nil { - return nil, err - } - volumes, err = s.Cache.LookUpVolumes(needle, true) - } - return volumes, err -} - -// ResolveSnapshot attempts to find a matching Identifier for the input string -func (s *ScalewayAPI) ResolveSnapshot(needle string) (ScalewayResolverResults, error) { - snapshots, err := s.Cache.LookUpSnapshots(needle, true) - if err != nil { - return snapshots, err - } - if len(snapshots) == 0 { - if _, err = s.GetSnapshots(); err != nil { - return nil, err - } - snapshots, err = s.Cache.LookUpSnapshots(needle, true) - } - return snapshots, err -} - -// ResolveImage attempts to find a matching Identifier for the input string -func (s *ScalewayAPI) ResolveImage(needle string) (ScalewayResolverResults, error) { - images, err := s.Cache.LookUpImages(needle, true) - if err != nil { - return images, err - } - if len(images) == 0 { - if _, err = s.GetImages(); err != nil { - return nil, err - } - images, err = s.Cache.LookUpImages(needle, true) - } - return images, err -} - -// ResolveBootscript attempts to find a matching Identifier for the input string -func (s *ScalewayAPI) ResolveBootscript(needle string) (ScalewayResolverResults, error) { - bootscripts, err := s.Cache.LookUpBootscripts(needle, true) - if err != nil { - return bootscripts, err - } - if len(bootscripts) == 0 { - if _, err = s.GetBootscripts(); err != nil { - return nil, err - } - bootscripts, err = s.Cache.LookUpBootscripts(needle, true) - } - return bootscripts, err -} - -// GetImages gets the list of images from the ScalewayAPI -func (s *ScalewayAPI) GetImages() (*[]MarketImage, error) { - images, err := s.GetMarketPlaceImages("") - if err != nil { - return nil, err - } - s.Cache.ClearImages() - for i, image := range images.Images { - if image.CurrentPublicVersion != "" { - for _, version := range image.Versions { - if version.ID == image.CurrentPublicVersion { - for _, localImage := range version.LocalImages { - images.Images[i].Public = true - s.Cache.InsertImage(localImage.ID, localImage.Zone, localImage.Arch, image.Organization.ID, image.Name, image.CurrentPublicVersion) - } - } - } - } - } - values := url.Values{} - values.Set("organization", s.Organization) - resp, err := s.GetResponsePaginate(s.computeAPI, "images", values) - if err != nil { - return nil, err - } - defer resp.Body.Close() - - body, err := s.handleHTTPError([]int{http.StatusOK}, resp) - if err != nil { - return nil, err - } - var OrgaImages ScalewayImages - - if err = json.Unmarshal(body, &OrgaImages); err != nil { - return nil, err - } - - for _, orgaImage := range OrgaImages.Images { - images.Images = append(images.Images, MarketImage{ - Categories: []string{"MyImages"}, - CreationDate: orgaImage.CreationDate, - CurrentPublicVersion: orgaImage.Identifier, - ModificationDate: orgaImage.ModificationDate, - Name: orgaImage.Name, - Public: false, - MarketVersions: MarketVersions{ - Versions: []MarketVersionDefinition{ - { - CreationDate: orgaImage.CreationDate, - ID: orgaImage.Identifier, - ModificationDate: orgaImage.ModificationDate, - MarketLocalImages: MarketLocalImages{ - LocalImages: []MarketLocalImageDefinition{ - { - Arch: orgaImage.Arch, - ID: orgaImage.Identifier, - // TODO: fecth images from ams1 and par1 - Zone: s.Region, - }, - }, - }, - }, - }, - }, - }) - s.Cache.InsertImage(orgaImage.Identifier, s.Region, orgaImage.Arch, orgaImage.Organization, orgaImage.Name, "") - } - return &images.Images, nil -} - -// GetImage gets an image from the ScalewayAPI -func (s *ScalewayAPI) GetImage(imageID string) (*ScalewayImage, error) { - resp, err := s.GetResponsePaginate(s.computeAPI, "images/"+imageID, url.Values{}) - if err != nil { - return nil, err - } - defer resp.Body.Close() - - body, err := s.handleHTTPError([]int{http.StatusOK}, resp) - if err != nil { - return nil, err - } - var oneImage ScalewayOneImage - - if err = json.Unmarshal(body, &oneImage); err != nil { - return nil, err - } - // FIXME owner, title - s.Cache.InsertImage(oneImage.Image.Identifier, s.Region, oneImage.Image.Arch, oneImage.Image.Organization, oneImage.Image.Name, "") - return &oneImage.Image, nil -} - -// DeleteImage deletes a image -func (s *ScalewayAPI) DeleteImage(imageID string) error { - defer s.Cache.RemoveImage(imageID) - resp, err := s.DeleteResponse(s.computeAPI, fmt.Sprintf("images/%s", imageID)) - if err != nil { - return err - } - defer resp.Body.Close() - - if _, err := s.handleHTTPError([]int{http.StatusNoContent}, resp); err != nil { - return err - } - return nil -} - -// DeleteSnapshot deletes a snapshot -func (s *ScalewayAPI) DeleteSnapshot(snapshotID string) error { - defer s.Cache.RemoveSnapshot(snapshotID) - resp, err := s.DeleteResponse(s.computeAPI, fmt.Sprintf("snapshots/%s", snapshotID)) - if err != nil { - return err - } - defer resp.Body.Close() - - if _, err := s.handleHTTPError([]int{http.StatusNoContent}, resp); err != nil { - return err - } - return nil -} - -// DeleteVolume deletes a volume -func (s *ScalewayAPI) DeleteVolume(volumeID string) error { - defer s.Cache.RemoveVolume(volumeID) - resp, err := s.DeleteResponse(s.computeAPI, fmt.Sprintf("volumes/%s", volumeID)) - if err != nil { - return err - } - defer resp.Body.Close() - - if _, err := s.handleHTTPError([]int{http.StatusNoContent}, resp); err != nil { - return err - } - return nil -} - -// GetSnapshots gets the list of snapshots from the ScalewayAPI -func (s *ScalewayAPI) GetSnapshots() (*[]ScalewaySnapshot, error) { - query := url.Values{} - s.Cache.ClearSnapshots() - - resp, err := s.GetResponsePaginate(s.computeAPI, "snapshots", query) - if err != nil { - return nil, err - } - defer resp.Body.Close() - - body, err := s.handleHTTPError([]int{http.StatusOK}, resp) - if err != nil { - return nil, err - } - var snapshots ScalewaySnapshots - - if err = json.Unmarshal(body, &snapshots); err != nil { - return nil, err - } - for _, snapshot := range snapshots.Snapshots { - // FIXME region, arch, owner, title - s.Cache.InsertSnapshot(snapshot.Identifier, s.Region, "", snapshot.Organization, snapshot.Name) - } - return &snapshots.Snapshots, nil -} - -// GetSnapshot gets a snapshot from the ScalewayAPI -func (s *ScalewayAPI) GetSnapshot(snapshotID string) (*ScalewaySnapshot, error) { - resp, err := s.GetResponsePaginate(s.computeAPI, "snapshots/"+snapshotID, url.Values{}) - if err != nil { - return nil, err - } - defer resp.Body.Close() - - body, err := s.handleHTTPError([]int{http.StatusOK}, resp) - if err != nil { - return nil, err - } - var oneSnapshot ScalewayOneSnapshot - - if err = json.Unmarshal(body, &oneSnapshot); err != nil { - return nil, err - } - // FIXME region, arch, owner, title - s.Cache.InsertSnapshot(oneSnapshot.Snapshot.Identifier, s.Region, "", oneSnapshot.Snapshot.Organization, oneSnapshot.Snapshot.Name) - return &oneSnapshot.Snapshot, nil -} - -// GetVolumes gets the list of volumes from the ScalewayAPI -func (s *ScalewayAPI) GetVolumes() (*[]ScalewayVolume, error) { - query := url.Values{} - s.Cache.ClearVolumes() - - resp, err := s.GetResponsePaginate(s.computeAPI, "volumes", query) - if err != nil { - return nil, err - } - defer resp.Body.Close() - - body, err := s.handleHTTPError([]int{http.StatusOK}, resp) - if err != nil { - return nil, err - } - - var volumes ScalewayVolumes - - if err = json.Unmarshal(body, &volumes); err != nil { - return nil, err - } - for _, volume := range volumes.Volumes { - // FIXME region, arch, owner, title - s.Cache.InsertVolume(volume.Identifier, s.Region, "", volume.Organization, volume.Name) - } - return &volumes.Volumes, nil -} - -// GetVolume gets a volume from the ScalewayAPI -func (s *ScalewayAPI) GetVolume(volumeID string) (*ScalewayVolume, error) { - resp, err := s.GetResponsePaginate(s.computeAPI, "volumes/"+volumeID, url.Values{}) - if err != nil { - return nil, err - } - defer resp.Body.Close() - - body, err := s.handleHTTPError([]int{http.StatusOK}, resp) - if err != nil { - return nil, err - } - var oneVolume ScalewayOneVolume - - if err = json.Unmarshal(body, &oneVolume); err != nil { - return nil, err - } - // FIXME region, arch, owner, title - s.Cache.InsertVolume(oneVolume.Volume.Identifier, s.Region, "", oneVolume.Volume.Organization, oneVolume.Volume.Name) - return &oneVolume.Volume, nil -} - -// GetBootscripts gets the list of bootscripts from the ScalewayAPI -func (s *ScalewayAPI) GetBootscripts() (*[]ScalewayBootscript, error) { - query := url.Values{} - - s.Cache.ClearBootscripts() - resp, err := s.GetResponsePaginate(s.computeAPI, "bootscripts", query) - if err != nil { - return nil, err - } - defer resp.Body.Close() - - body, err := s.handleHTTPError([]int{http.StatusOK}, resp) - if err != nil { - return nil, err - } - var bootscripts ScalewayBootscripts - - if err = json.Unmarshal(body, &bootscripts); err != nil { - return nil, err - } - for _, bootscript := range bootscripts.Bootscripts { - // FIXME region, arch, owner, title - s.Cache.InsertBootscript(bootscript.Identifier, s.Region, bootscript.Arch, bootscript.Organization, bootscript.Title) - } - return &bootscripts.Bootscripts, nil -} - -// GetBootscript gets a bootscript from the ScalewayAPI -func (s *ScalewayAPI) GetBootscript(bootscriptID string) (*ScalewayBootscript, error) { - resp, err := s.GetResponsePaginate(s.computeAPI, "bootscripts/"+bootscriptID, url.Values{}) - if err != nil { - return nil, err - } - defer resp.Body.Close() - - body, err := s.handleHTTPError([]int{http.StatusOK}, resp) - if err != nil { - return nil, err - } - var oneBootscript ScalewayOneBootscript - - if err = json.Unmarshal(body, &oneBootscript); err != nil { - return nil, err - } - // FIXME region, arch, owner, title - s.Cache.InsertBootscript(oneBootscript.Bootscript.Identifier, s.Region, oneBootscript.Bootscript.Arch, oneBootscript.Bootscript.Organization, oneBootscript.Bootscript.Title) - return &oneBootscript.Bootscript, nil -} - -// GetUserdatas gets list of userdata for a server -func (s *ScalewayAPI) GetUserdatas(serverID string, metadata bool) (*ScalewayUserdatas, error) { - var uri, endpoint string - - endpoint = s.computeAPI - if metadata { - uri = "/user_data" - endpoint = MetadataAPI - } else { - uri = fmt.Sprintf("servers/%s/user_data", serverID) - } - - resp, err := s.GetResponsePaginate(endpoint, uri, url.Values{}) - if err != nil { - return nil, err - } - defer resp.Body.Close() - - body, err := s.handleHTTPError([]int{http.StatusOK}, resp) - if err != nil { - return nil, err - } - var userdatas ScalewayUserdatas - - if err = json.Unmarshal(body, &userdatas); err != nil { - return nil, err - } - return &userdatas, nil -} - -func (s *ScalewayUserdata) String() string { - return string(*s) -} - -// GetUserdata gets a specific userdata for a server -func (s *ScalewayAPI) GetUserdata(serverID, key string, metadata bool) (*ScalewayUserdata, error) { - var uri, endpoint string - - endpoint = s.computeAPI - if metadata { - uri = fmt.Sprintf("/user_data/%s", key) - endpoint = MetadataAPI - } else { - uri = fmt.Sprintf("servers/%s/user_data/%s", serverID, key) - } - - var err error - resp, err := s.GetResponsePaginate(endpoint, uri, url.Values{}) - if err != nil { - return nil, err - } - defer resp.Body.Close() - - if resp.StatusCode != http.StatusOK { - return nil, fmt.Errorf("no such user_data %q (%d)", key, resp.StatusCode) - } - var data ScalewayUserdata - data, err = ioutil.ReadAll(resp.Body) - return &data, err -} - -// PatchUserdata sets a user data -func (s *ScalewayAPI) PatchUserdata(serverID, key string, value []byte, metadata bool) error { - var resource, endpoint string - - endpoint = s.computeAPI - if metadata { - resource = fmt.Sprintf("/user_data/%s", key) - endpoint = MetadataAPI - } else { - resource = fmt.Sprintf("servers/%s/user_data/%s", serverID, key) - } - - uri := fmt.Sprintf("%s/%s", strings.TrimRight(endpoint, "/"), resource) - payload := new(bytes.Buffer) - payload.Write(value) - - req, err := http.NewRequest("PATCH", uri, payload) - if err != nil { - return err - } - - req.Header.Set("X-Auth-Token", s.Token) - req.Header.Set("Content-Type", "text/plain") - req.Header.Set("User-Agent", s.userAgent) - - s.LogHTTP(req) - - resp, err := s.client.Do(req) - if err != nil { - return err - } - defer resp.Body.Close() - - if resp.StatusCode == http.StatusNoContent { - return nil - } - - return fmt.Errorf("cannot set user_data (%d)", resp.StatusCode) -} - -// DeleteUserdata deletes a server user_data -func (s *ScalewayAPI) DeleteUserdata(serverID, key string, metadata bool) error { - var url, endpoint string - - endpoint = s.computeAPI - if metadata { - url = fmt.Sprintf("/user_data/%s", key) - endpoint = MetadataAPI - } else { - url = fmt.Sprintf("servers/%s/user_data/%s", serverID, key) - } - - resp, err := s.DeleteResponse(endpoint, url) - if err != nil { - return err - } - defer resp.Body.Close() - - _, err = s.handleHTTPError([]int{http.StatusNoContent}, resp) - return err -} - -// GetTasks get the list of tasks from the ScalewayAPI -func (s *ScalewayAPI) GetTasks() (*[]ScalewayTask, error) { - query := url.Values{} - resp, err := s.GetResponsePaginate(s.computeAPI, "tasks", query) - if err != nil { - return nil, err - } - defer resp.Body.Close() - - body, err := s.handleHTTPError([]int{http.StatusOK}, resp) - if err != nil { - return nil, err - } - var tasks ScalewayTasks - - if err = json.Unmarshal(body, &tasks); err != nil { - return nil, err - } - return &tasks.Tasks, nil -} - -// CheckCredentials performs a dummy check to ensure we can contact the API -func (s *ScalewayAPI) CheckCredentials() error { - query := url.Values{} - - resp, err := s.GetResponsePaginate(AccountAPI, "tokens", query) - if err != nil { - return err - } - defer resp.Body.Close() - body, err := s.handleHTTPError([]int{http.StatusOK}, resp) - if err != nil { - return err - } - var tokens ScalewayGetTokens - if err = json.Unmarshal(body, &tokens); err != nil { - return err - } - return nil -} - -// GetUserID returns the userID -func (s *ScalewayAPI) GetUserID() (string, error) { - resp, err := s.GetResponsePaginate(AccountAPI, fmt.Sprintf("tokens/%s", s.Token), url.Values{}) - if err != nil { - return "", err - } - defer resp.Body.Close() - - body, err := s.handleHTTPError([]int{http.StatusOK}, resp) - if err != nil { - return "", err - } - var token ScalewayTokensDefinition - - if err = json.Unmarshal(body, &token); err != nil { - return "", err - } - return token.Token.UserID, nil -} - -// GetOrganization returns Organization -func (s *ScalewayAPI) GetOrganization() (*ScalewayOrganizationsDefinition, error) { - resp, err := s.GetResponsePaginate(AccountAPI, "organizations", url.Values{}) - if err != nil { - return nil, err - } - defer resp.Body.Close() - - body, err := s.handleHTTPError([]int{http.StatusOK}, resp) - if err != nil { - return nil, err - } - var data ScalewayOrganizationsDefinition - - if err = json.Unmarshal(body, &data); err != nil { - return nil, err - } - return &data, nil -} - -// GetUser returns the user -func (s *ScalewayAPI) GetUser() (*ScalewayUserDefinition, error) { - userID, err := s.GetUserID() - if err != nil { - return nil, err - } - resp, err := s.GetResponsePaginate(AccountAPI, fmt.Sprintf("users/%s", userID), url.Values{}) - if err != nil { - return nil, err - } - defer resp.Body.Close() - - body, err := s.handleHTTPError([]int{http.StatusOK}, resp) - if err != nil { - return nil, err - } - var user ScalewayUsersDefinition - - if err = json.Unmarshal(body, &user); err != nil { - return nil, err - } - return &user.User, nil -} - -// GetPermissions returns the permissions -func (s *ScalewayAPI) GetPermissions() (*ScalewayPermissionDefinition, error) { - resp, err := s.GetResponsePaginate(AccountAPI, fmt.Sprintf("tokens/%s/permissions", s.Token), url.Values{}) - if err != nil { - return nil, err - } - defer resp.Body.Close() - - body, err := s.handleHTTPError([]int{http.StatusOK}, resp) - if err != nil { - return nil, err - } - var permissions ScalewayPermissionDefinition - - if err = json.Unmarshal(body, &permissions); err != nil { - return nil, err - } - return &permissions, nil -} - -// GetDashboard returns the dashboard -func (s *ScalewayAPI) GetDashboard() (*ScalewayDashboard, error) { - resp, err := s.GetResponsePaginate(s.computeAPI, "dashboard", url.Values{}) - if err != nil { - return nil, err - } - defer resp.Body.Close() - - body, err := s.handleHTTPError([]int{http.StatusOK}, resp) - if err != nil { - return nil, err - } - var dashboard ScalewayDashboardResp - - if err = json.Unmarshal(body, &dashboard); err != nil { - return nil, err - } - return &dashboard.Dashboard, nil -} - -// GetServerID returns exactly one server matching -func (s *ScalewayAPI) GetServerID(needle string) (string, error) { - // Parses optional type prefix, i.e: "server:name" -> "name" - _, needle = parseNeedle(needle) - - servers, err := s.ResolveServer(needle) - if err != nil { - return "", fmt.Errorf("Unable to resolve server %s: %s", needle, err) - } - if len(servers) == 1 { - return servers[0].Identifier, nil - } - if len(servers) == 0 { - return "", fmt.Errorf("No such server: %s", needle) - } - return "", showResolverResults(needle, servers) -} - -func showResolverResults(needle string, results ScalewayResolverResults) error { - w := tabwriter.NewWriter(os.Stderr, 20, 1, 3, ' ', 0) - defer w.Flush() - sort.Sort(results) - fmt.Fprintf(w, " IMAGEID\tFROM\tNAME\tZONE\tARCH\n") - for _, result := range results { - if result.Arch == "" { - result.Arch = "n/a" - } - fmt.Fprintf(w, "- %s\t%s\t%s\t%s\t%s\n", result.TruncIdentifier(), result.CodeName(), result.Name, result.Region, result.Arch) - } - return fmt.Errorf("Too many candidates for %s (%d)", needle, len(results)) -} - -// GetVolumeID returns exactly one volume matching -func (s *ScalewayAPI) GetVolumeID(needle string) (string, error) { - // Parses optional type prefix, i.e: "volume:name" -> "name" - _, needle = parseNeedle(needle) - - volumes, err := s.ResolveVolume(needle) - if err != nil { - return "", fmt.Errorf("Unable to resolve volume %s: %s", needle, err) - } - if len(volumes) == 1 { - return volumes[0].Identifier, nil - } - if len(volumes) == 0 { - return "", fmt.Errorf("No such volume: %s", needle) - } - return "", showResolverResults(needle, volumes) -} - -// GetSnapshotID returns exactly one snapshot matching -func (s *ScalewayAPI) GetSnapshotID(needle string) (string, error) { - // Parses optional type prefix, i.e: "snapshot:name" -> "name" - _, needle = parseNeedle(needle) - - snapshots, err := s.ResolveSnapshot(needle) - if err != nil { - return "", fmt.Errorf("Unable to resolve snapshot %s: %s", needle, err) - } - if len(snapshots) == 1 { - return snapshots[0].Identifier, nil - } - if len(snapshots) == 0 { - return "", fmt.Errorf("No such snapshot: %s", needle) - } - return "", showResolverResults(needle, snapshots) -} - -// FilterImagesByArch removes entry that doesn't match with architecture -func FilterImagesByArch(res ScalewayResolverResults, arch string) (ret ScalewayResolverResults) { - if arch == "*" { - return res - } - for _, result := range res { - if result.Arch == arch { - ret = append(ret, result) - } - } - return -} - -// FilterImagesByRegion removes entry that doesn't match with region -func FilterImagesByRegion(res ScalewayResolverResults, region string) (ret ScalewayResolverResults) { - if region == "*" { - return res - } - for _, result := range res { - if result.Region == region { - ret = append(ret, result) - } - } - return -} - -// GetImageID returns exactly one image matching -func (s *ScalewayAPI) GetImageID(needle, arch string) (*ScalewayImageIdentifier, error) { - // Parses optional type prefix, i.e: "image:name" -> "name" - _, needle = parseNeedle(needle) - - images, err := s.ResolveImage(needle) - if err != nil { - return nil, fmt.Errorf("Unable to resolve image %s: %s", needle, err) - } - images = FilterImagesByArch(images, arch) - images = FilterImagesByRegion(images, s.Region) - if len(images) == 1 { - return &ScalewayImageIdentifier{ - Identifier: images[0].Identifier, - Arch: images[0].Arch, - // FIXME region, owner hardcoded - Region: images[0].Region, - Owner: "", - }, nil - } - if len(images) == 0 { - return nil, fmt.Errorf("No such image (zone %s, arch %s) : %s", s.Region, arch, needle) - } - return nil, showResolverResults(needle, images) -} - -// GetSecurityGroups returns a ScalewaySecurityGroups -func (s *ScalewayAPI) GetSecurityGroups() (*ScalewayGetSecurityGroups, error) { - resp, err := s.GetResponsePaginate(s.computeAPI, "security_groups", url.Values{}) - if err != nil { - return nil, err - } - defer resp.Body.Close() - - body, err := s.handleHTTPError([]int{http.StatusOK}, resp) - if err != nil { - return nil, err - } - var securityGroups ScalewayGetSecurityGroups - - if err = json.Unmarshal(body, &securityGroups); err != nil { - return nil, err - } - return &securityGroups, nil -} - -// GetSecurityGroupRules returns a ScalewaySecurityGroupRules -func (s *ScalewayAPI) GetSecurityGroupRules(groupID string) (*ScalewayGetSecurityGroupRules, error) { - resp, err := s.GetResponsePaginate(s.computeAPI, fmt.Sprintf("security_groups/%s/rules", groupID), url.Values{}) - if err != nil { - return nil, err - } - defer resp.Body.Close() - - body, err := s.handleHTTPError([]int{http.StatusOK}, resp) - if err != nil { - return nil, err - } - var securityGroupRules ScalewayGetSecurityGroupRules - - if err = json.Unmarshal(body, &securityGroupRules); err != nil { - return nil, err - } - return &securityGroupRules, nil -} - -// GetASecurityGroupRule returns a ScalewaySecurityGroupRule -func (s *ScalewayAPI) GetASecurityGroupRule(groupID string, rulesID string) (*ScalewayGetSecurityGroupRule, error) { - resp, err := s.GetResponsePaginate(s.computeAPI, fmt.Sprintf("security_groups/%s/rules/%s", groupID, rulesID), url.Values{}) - if err != nil { - return nil, err - } - defer resp.Body.Close() - - body, err := s.handleHTTPError([]int{http.StatusOK}, resp) - if err != nil { - return nil, err - } - var securityGroupRules ScalewayGetSecurityGroupRule - - if err = json.Unmarshal(body, &securityGroupRules); err != nil { - return nil, err - } - return &securityGroupRules, nil -} - -// GetASecurityGroup returns a ScalewaySecurityGroup -func (s *ScalewayAPI) GetASecurityGroup(groupsID string) (*ScalewayGetSecurityGroup, error) { - resp, err := s.GetResponsePaginate(s.computeAPI, fmt.Sprintf("security_groups/%s", groupsID), url.Values{}) - if err != nil { - return nil, err - } - defer resp.Body.Close() - - body, err := s.handleHTTPError([]int{http.StatusOK}, resp) - if err != nil { - return nil, err - } - var securityGroups ScalewayGetSecurityGroup - - if err = json.Unmarshal(body, &securityGroups); err != nil { - return nil, err - } - return &securityGroups, nil -} - -// PostSecurityGroup posts a group on a server -func (s *ScalewayAPI) PostSecurityGroup(group ScalewayNewSecurityGroup) error { - resp, err := s.PostResponse(s.computeAPI, "security_groups", group) - if err != nil { - return err - } - defer resp.Body.Close() - - _, err = s.handleHTTPError([]int{http.StatusCreated}, resp) - return err -} - -// PostSecurityGroupRule posts a rule on a server -func (s *ScalewayAPI) PostSecurityGroupRule(SecurityGroupID string, rules ScalewayNewSecurityGroupRule) error { - resp, err := s.PostResponse(s.computeAPI, fmt.Sprintf("security_groups/%s/rules", SecurityGroupID), rules) - if err != nil { - return err - } - defer resp.Body.Close() - - _, err = s.handleHTTPError([]int{http.StatusCreated}, resp) - return err -} - -// DeleteSecurityGroup deletes a SecurityGroup -func (s *ScalewayAPI) DeleteSecurityGroup(securityGroupID string) error { - resp, err := s.DeleteResponse(s.computeAPI, fmt.Sprintf("security_groups/%s", securityGroupID)) - if err != nil { - return err - } - defer resp.Body.Close() - - _, err = s.handleHTTPError([]int{http.StatusNoContent}, resp) - return err -} - -// PutSecurityGroup updates a SecurityGroup -func (s *ScalewayAPI) PutSecurityGroup(group ScalewayUpdateSecurityGroup, securityGroupID string) error { - resp, err := s.PutResponse(s.computeAPI, fmt.Sprintf("security_groups/%s", securityGroupID), group) - if err != nil { - return err - } - defer resp.Body.Close() - - _, err = s.handleHTTPError([]int{http.StatusOK}, resp) - return err -} - -// PutSecurityGroupRule updates a SecurityGroupRule -func (s *ScalewayAPI) PutSecurityGroupRule(rules ScalewayNewSecurityGroupRule, securityGroupID, RuleID string) error { - resp, err := s.PutResponse(s.computeAPI, fmt.Sprintf("security_groups/%s/rules/%s", securityGroupID, RuleID), rules) - if err != nil { - return err - } - defer resp.Body.Close() - - _, err = s.handleHTTPError([]int{http.StatusOK}, resp) - return err -} - -// DeleteSecurityGroupRule deletes a SecurityGroupRule -func (s *ScalewayAPI) DeleteSecurityGroupRule(SecurityGroupID, RuleID string) error { - resp, err := s.DeleteResponse(s.computeAPI, fmt.Sprintf("security_groups/%s/rules/%s", SecurityGroupID, RuleID)) - if err != nil { - return err - } - defer resp.Body.Close() - - _, err = s.handleHTTPError([]int{http.StatusNoContent}, resp) - return err -} - -// GetContainers returns a ScalewayGetContainers -func (s *ScalewayAPI) GetContainers() (*ScalewayGetContainers, error) { - resp, err := s.GetResponsePaginate(s.computeAPI, "containers", url.Values{}) - if err != nil { - return nil, err - } - defer resp.Body.Close() - - body, err := s.handleHTTPError([]int{http.StatusOK}, resp) - if err != nil { - return nil, err - } - var containers ScalewayGetContainers - - if err = json.Unmarshal(body, &containers); err != nil { - return nil, err - } - return &containers, nil -} - -// GetContainerDatas returns a ScalewayGetContainerDatas -func (s *ScalewayAPI) GetContainerDatas(container string) (*ScalewayGetContainerDatas, error) { - resp, err := s.GetResponsePaginate(s.computeAPI, fmt.Sprintf("containers/%s", container), url.Values{}) - if err != nil { - return nil, err - } - defer resp.Body.Close() - - body, err := s.handleHTTPError([]int{http.StatusOK}, resp) - if err != nil { - return nil, err - } - var datas ScalewayGetContainerDatas - - if err = json.Unmarshal(body, &datas); err != nil { - return nil, err - } - return &datas, nil -} - -// GetIPS returns a ScalewayGetIPS -func (s *ScalewayAPI) GetIPS() (*ScalewayGetIPS, error) { - resp, err := s.GetResponsePaginate(s.computeAPI, "ips", url.Values{}) - if err != nil { - return nil, err - } - defer resp.Body.Close() - - body, err := s.handleHTTPError([]int{http.StatusOK}, resp) - if err != nil { - return nil, err - } - var ips ScalewayGetIPS - - if err = json.Unmarshal(body, &ips); err != nil { - return nil, err - } - return &ips, nil -} - -// NewIP returns a new IP -func (s *ScalewayAPI) NewIP() (*ScalewayGetIP, error) { - var orga struct { - Organization string `json:"organization"` - } - orga.Organization = s.Organization - resp, err := s.PostResponse(s.computeAPI, "ips", orga) - if err != nil { - return nil, err - } - defer resp.Body.Close() - - body, err := s.handleHTTPError([]int{http.StatusCreated}, resp) - if err != nil { - return nil, err - } - var ip ScalewayGetIP - - if err = json.Unmarshal(body, &ip); err != nil { - return nil, err - } - return &ip, nil -} - -// AttachIP attachs an IP to a server -func (s *ScalewayAPI) AttachIP(ipID, serverID string) error { - var update struct { - Address string `json:"address"` - ID string `json:"id"` - Reverse *string `json:"reverse"` - Organization string `json:"organization"` - Server string `json:"server"` - } - - ip, err := s.GetIP(ipID) - if err != nil { - return err - } - update.Address = ip.IP.Address - update.ID = ip.IP.ID - update.Organization = ip.IP.Organization - update.Server = serverID - resp, err := s.PutResponse(s.computeAPI, fmt.Sprintf("ips/%s", ipID), update) - if err != nil { - return err - } - _, err = s.handleHTTPError([]int{http.StatusOK}, resp) - return err -} - -// DetachIP detaches an IP from a server -func (s *ScalewayAPI) DetachIP(ipID string) error { - ip, err := s.GetIP(ipID) - if err != nil { - return err - } - ip.IP.Server = nil - resp, err := s.PutResponse(s.computeAPI, fmt.Sprintf("ips/%s", ipID), ip.IP) - if err != nil { - return err - } - defer resp.Body.Close() - _, err = s.handleHTTPError([]int{http.StatusOK}, resp) - return err -} - -// DeleteIP deletes an IP -func (s *ScalewayAPI) DeleteIP(ipID string) error { - resp, err := s.DeleteResponse(s.computeAPI, fmt.Sprintf("ips/%s", ipID)) - if err != nil { - return err - } - defer resp.Body.Close() - _, err = s.handleHTTPError([]int{http.StatusNoContent}, resp) - return err -} - -// GetIP returns a ScalewayGetIP -func (s *ScalewayAPI) GetIP(ipID string) (*ScalewayGetIP, error) { - resp, err := s.GetResponsePaginate(s.computeAPI, fmt.Sprintf("ips/%s", ipID), url.Values{}) - if err != nil { - return nil, err - } - defer resp.Body.Close() - - body, err := s.handleHTTPError([]int{http.StatusOK}, resp) - if err != nil { - return nil, err - } - var ip ScalewayGetIP - - if err = json.Unmarshal(body, &ip); err != nil { - return nil, err - } - return &ip, nil -} - -// GetQuotas returns a ScalewayGetQuotas -func (s *ScalewayAPI) GetQuotas() (*ScalewayGetQuotas, error) { - resp, err := s.GetResponsePaginate(AccountAPI, fmt.Sprintf("organizations/%s/quotas", s.Organization), url.Values{}) - if err != nil { - return nil, err - } - defer resp.Body.Close() - - body, err := s.handleHTTPError([]int{http.StatusOK}, resp) - if err != nil { - return nil, err - } - var quotas ScalewayGetQuotas - - if err = json.Unmarshal(body, "as); err != nil { - return nil, err - } - return "as, nil -} - -// GetBootscriptID returns exactly one bootscript matching -func (s *ScalewayAPI) GetBootscriptID(needle, arch string) (string, error) { - // Parses optional type prefix, i.e: "bootscript:name" -> "name" - _, needle = parseNeedle(needle) - - bootscripts, err := s.ResolveBootscript(needle) - if err != nil { - return "", fmt.Errorf("Unable to resolve bootscript %s: %s", needle, err) - } - bootscripts.FilterByArch(arch) - if len(bootscripts) == 1 { - return bootscripts[0].Identifier, nil - } - if len(bootscripts) == 0 { - return "", fmt.Errorf("No such bootscript: %s", needle) - } - return "", showResolverResults(needle, bootscripts) -} - -func rootNetDial(network, addr string) (net.Conn, error) { - dialer := net.Dialer{ - Timeout: 10 * time.Second, - KeepAlive: 10 * time.Second, - } - - // bruteforce privileged ports - var localAddr net.Addr - var err error - for port := 1; port <= 1024; port++ { - localAddr, err = net.ResolveTCPAddr("tcp", fmt.Sprintf(":%d", port)) - - // this should never happen - if err != nil { - return nil, err - } - - dialer.LocalAddr = localAddr - - conn, err := dialer.Dial(network, addr) - - // if err is nil, dialer.Dial succeed, so let's go - // else, err != nil, but we don't care - if err == nil { - return conn, nil - } - } - // if here, all privileged ports were tried without success - return nil, fmt.Errorf("bind: permission denied, are you root ?") -} - -// SetPassword register the password -func (s *ScalewayAPI) SetPassword(password string) { - s.password = password -} - -// GetMarketPlaceImages returns images from marketplace -func (s *ScalewayAPI) GetMarketPlaceImages(uuidImage string) (*MarketImages, error) { - resp, err := s.GetResponsePaginate(MarketplaceAPI, fmt.Sprintf("images/%s", uuidImage), url.Values{}) - if err != nil { - return nil, err - } - defer resp.Body.Close() - - body, err := s.handleHTTPError([]int{http.StatusOK}, resp) - if err != nil { - return nil, err - } - var ret MarketImages - - if uuidImage != "" { - ret.Images = make([]MarketImage, 1) - - var img MarketImage - - if err = json.Unmarshal(body, &img); err != nil { - return nil, err - } - ret.Images[0] = img - } else { - if err = json.Unmarshal(body, &ret); err != nil { - return nil, err - } - } - return &ret, nil -} - -// GetMarketPlaceImageVersions returns image version -func (s *ScalewayAPI) GetMarketPlaceImageVersions(uuidImage, uuidVersion string) (*MarketVersions, error) { - resp, err := s.GetResponsePaginate(MarketplaceAPI, fmt.Sprintf("images/%v/versions/%s", uuidImage, uuidVersion), url.Values{}) - if err != nil { - return nil, err - } - defer resp.Body.Close() - - body, err := s.handleHTTPError([]int{http.StatusOK}, resp) - if err != nil { - return nil, err - } - var ret MarketVersions - - if uuidImage != "" { - var version MarketVersion - ret.Versions = make([]MarketVersionDefinition, 1) - - if err = json.Unmarshal(body, &version); err != nil { - return nil, err - } - ret.Versions[0] = version.Version - } else { - if err = json.Unmarshal(body, &ret); err != nil { - return nil, err - } - } - return &ret, nil -} - -// GetMarketPlaceImageCurrentVersion return the image current version -func (s *ScalewayAPI) GetMarketPlaceImageCurrentVersion(uuidImage string) (*MarketVersion, error) { - resp, err := s.GetResponsePaginate(MarketplaceAPI, fmt.Sprintf("images/%v/versions/current", uuidImage), url.Values{}) - if err != nil { - return nil, err - } - defer resp.Body.Close() - - body, err := s.handleHTTPError([]int{http.StatusOK}, resp) - if err != nil { - return nil, err - } - var ret MarketVersion - - if err = json.Unmarshal(body, &ret); err != nil { - return nil, err - } - return &ret, nil -} - -// GetMarketPlaceLocalImages returns images from local region -func (s *ScalewayAPI) GetMarketPlaceLocalImages(uuidImage, uuidVersion, uuidLocalImage string) (*MarketLocalImages, error) { - resp, err := s.GetResponsePaginate(MarketplaceAPI, fmt.Sprintf("images/%v/versions/%s/local_images/%s", uuidImage, uuidVersion, uuidLocalImage), url.Values{}) - if err != nil { - return nil, err - } - defer resp.Body.Close() - body, err := s.handleHTTPError([]int{http.StatusOK}, resp) - if err != nil { - return nil, err - } - var ret MarketLocalImages - if uuidLocalImage != "" { - var localImage MarketLocalImage - ret.LocalImages = make([]MarketLocalImageDefinition, 1) - - if err = json.Unmarshal(body, &localImage); err != nil { - return nil, err - } - ret.LocalImages[0] = localImage.LocalImages - } else { - if err = json.Unmarshal(body, &ret); err != nil { - return nil, err - } - } - return &ret, nil -} - -// PostMarketPlaceImage adds new image -func (s *ScalewayAPI) PostMarketPlaceImage(images MarketImage) error { - resp, err := s.PostResponse(MarketplaceAPI, "images/", images) - if err != nil { - return err - } - defer resp.Body.Close() - _, err = s.handleHTTPError([]int{http.StatusAccepted}, resp) - return err -} - -// PostMarketPlaceImageVersion adds new image version -func (s *ScalewayAPI) PostMarketPlaceImageVersion(uuidImage string, version MarketVersion) error { - resp, err := s.PostResponse(MarketplaceAPI, fmt.Sprintf("images/%v/versions", uuidImage), version) - if err != nil { - return err - } - defer resp.Body.Close() - _, err = s.handleHTTPError([]int{http.StatusAccepted}, resp) - return err -} - -// PostMarketPlaceLocalImage adds new local image -func (s *ScalewayAPI) PostMarketPlaceLocalImage(uuidImage, uuidVersion, uuidLocalImage string, local MarketLocalImage) error { - resp, err := s.PostResponse(MarketplaceAPI, fmt.Sprintf("images/%v/versions/%s/local_images/%v", uuidImage, uuidVersion, uuidLocalImage), local) - if err != nil { - return err - } - defer resp.Body.Close() - _, err = s.handleHTTPError([]int{http.StatusAccepted}, resp) - return err -} - -// PutMarketPlaceImage updates image -func (s *ScalewayAPI) PutMarketPlaceImage(uudiImage string, images MarketImage) error { - resp, err := s.PutResponse(MarketplaceAPI, fmt.Sprintf("images/%v", uudiImage), images) - if err != nil { - return err - } - defer resp.Body.Close() - _, err = s.handleHTTPError([]int{http.StatusOK}, resp) - return err -} - -// PutMarketPlaceImageVersion updates image version -func (s *ScalewayAPI) PutMarketPlaceImageVersion(uuidImage, uuidVersion string, version MarketVersion) error { - resp, err := s.PutResponse(MarketplaceAPI, fmt.Sprintf("images/%v/versions/%v", uuidImage, uuidVersion), version) - if err != nil { - return err - } - defer resp.Body.Close() - _, err = s.handleHTTPError([]int{http.StatusOK}, resp) - return err -} - -// PutMarketPlaceLocalImage updates local image -func (s *ScalewayAPI) PutMarketPlaceLocalImage(uuidImage, uuidVersion, uuidLocalImage string, local MarketLocalImage) error { - resp, err := s.PostResponse(MarketplaceAPI, fmt.Sprintf("images/%v/versions/%s/local_images/%v", uuidImage, uuidVersion, uuidLocalImage), local) - if err != nil { - return err - } - defer resp.Body.Close() - _, err = s.handleHTTPError([]int{http.StatusOK}, resp) - return err -} - -// DeleteMarketPlaceImage deletes image -func (s *ScalewayAPI) DeleteMarketPlaceImage(uudImage string) error { - resp, err := s.DeleteResponse(MarketplaceAPI, fmt.Sprintf("images/%v", uudImage)) - if err != nil { - return err - } - defer resp.Body.Close() - _, err = s.handleHTTPError([]int{http.StatusNoContent}, resp) - return err -} - -// DeleteMarketPlaceImageVersion delete image version -func (s *ScalewayAPI) DeleteMarketPlaceImageVersion(uuidImage, uuidVersion string) error { - resp, err := s.DeleteResponse(MarketplaceAPI, fmt.Sprintf("images/%v/versions/%v", uuidImage, uuidVersion)) - if err != nil { - return err - } - defer resp.Body.Close() - _, err = s.handleHTTPError([]int{http.StatusNoContent}, resp) - return err -} - -// DeleteMarketPlaceLocalImage deletes local image -func (s *ScalewayAPI) DeleteMarketPlaceLocalImage(uuidImage, uuidVersion, uuidLocalImage string) error { - resp, err := s.DeleteResponse(MarketplaceAPI, fmt.Sprintf("images/%v/versions/%s/local_images/%v", uuidImage, uuidVersion, uuidLocalImage)) - if err != nil { - return err - } - defer resp.Body.Close() - _, err = s.handleHTTPError([]int{http.StatusNoContent}, resp) - return err -} - -// ResolveTTYUrl return an URL to get a tty -func (s *ScalewayAPI) ResolveTTYUrl() string { - switch s.Region { - case "par1", "": - return "https://tty-par1.scaleway.com/v2/" - case "ams1": - return "https://tty-ams1.scaleway.com" - } - return "" -} - -// GetProductServers Fetches all the server type and their constraints from the Products API -func (s *ScalewayAPI) GetProductsServers() (*ScalewayProductsServers, error) { - resp, err := s.GetResponsePaginate(s.computeAPI, "products/servers", url.Values{}) - if err != nil { - return nil, err - } - defer resp.Body.Close() - - body, err := s.handleHTTPError([]int{http.StatusOK}, resp) - if err != nil { - return nil, err - } - - var productServers ScalewayProductsServers - if err = json.Unmarshal(body, &productServers); err != nil { - return nil, err - } - - return &productServers, nil -} diff --git a/vendor/github.com/scaleway/scaleway-cli/pkg/api/cache.go b/vendor/github.com/scaleway/scaleway-cli/pkg/api/cache.go deleted file mode 100644 index 0a57796e9..000000000 --- a/vendor/github.com/scaleway/scaleway-cli/pkg/api/cache.go +++ /dev/null @@ -1,816 +0,0 @@ -// Copyright (C) 2015 Scaleway. All rights reserved. -// Use of this source code is governed by a MIT-style -// license that can be found in the LICENSE.md file. - -package api - -import ( - "encoding/json" - "fmt" - "io/ioutil" - "os" - "path/filepath" - "regexp" - "strings" - "sync" - - "github.com/moul/anonuuid" - "github.com/renstrom/fuzzysearch/fuzzy" -) - -const ( - // CacheRegion permits to access at the region field - CacheRegion = iota - // CacheArch permits to access at the arch field - CacheArch - // CacheOwner permits to access at the owner field - CacheOwner - // CacheTitle permits to access at the title field - CacheTitle - // CacheMarketPlaceUUID is used to determine the UUID of local images - CacheMarketPlaceUUID - // CacheMaxfield is used to determine the size of array - CacheMaxfield -) - -// ScalewayCache is used not to query the API to resolve full identifiers -type ScalewayCache struct { - // Images contains names of Scaleway images indexed by identifier - Images map[string][CacheMaxfield]string `json:"images"` - - // Snapshots contains names of Scaleway snapshots indexed by identifier - Snapshots map[string][CacheMaxfield]string `json:"snapshots"` - - // Volumes contains names of Scaleway volumes indexed by identifier - Volumes map[string][CacheMaxfield]string `json:"volumes"` - - // Bootscripts contains names of Scaleway bootscripts indexed by identifier - Bootscripts map[string][CacheMaxfield]string `json:"bootscripts"` - - // Servers contains names of Scaleway servers indexed by identifier - Servers map[string][CacheMaxfield]string `json:"servers"` - - // Path is the path to the cache file - Path string `json:"-"` - - // Modified tells if the cache needs to be overwritten or not - Modified bool `json:"-"` - - // Lock allows ScalewayCache to be used concurrently - Lock sync.Mutex `json:"-"` - - hookSave func() -} - -const ( - // IdentifierUnknown is used when we don't know explicitly the type key of the object (used for nil comparison) - IdentifierUnknown = 1 << iota - // IdentifierServer is the type key of cached server objects - IdentifierServer - // IdentifierImage is the type key of cached image objects - IdentifierImage - // IdentifierSnapshot is the type key of cached snapshot objects - IdentifierSnapshot - // IdentifierBootscript is the type key of cached bootscript objects - IdentifierBootscript - // IdentifierVolume is the type key of cached volume objects - IdentifierVolume -) - -// ScalewayResolverResult is a structure containing human-readable information -// about resolver results. This structure is used to display the user choices. -type ScalewayResolverResult struct { - Identifier string - Type int - Name string - Arch string - Needle string - RankMatch int - Region string -} - -// ScalewayResolverResults is a list of `ScalewayResolverResult` -type ScalewayResolverResults []ScalewayResolverResult - -// NewScalewayResolverResult returns a new ScalewayResolverResult -func NewScalewayResolverResult(Identifier, Name, Arch, Region string, Type int) (ScalewayResolverResult, error) { - if err := anonuuid.IsUUID(Identifier); err != nil { - return ScalewayResolverResult{}, err - } - return ScalewayResolverResult{ - Identifier: Identifier, - Type: Type, - Name: Name, - Arch: Arch, - Region: Region, - }, nil -} - -func (s ScalewayResolverResults) Len() int { - return len(s) -} - -func (s ScalewayResolverResults) Swap(i, j int) { - s[i], s[j] = s[j], s[i] -} - -func (s ScalewayResolverResults) Less(i, j int) bool { - return s[i].RankMatch < s[j].RankMatch -} - -// TruncIdentifier returns first 8 characters of an Identifier (UUID) -func (s *ScalewayResolverResult) TruncIdentifier() string { - return s.Identifier[:8] -} - -func identifierTypeName(kind int) string { - switch kind { - case IdentifierServer: - return "Server" - case IdentifierImage: - return "Image" - case IdentifierSnapshot: - return "Snapshot" - case IdentifierVolume: - return "Volume" - case IdentifierBootscript: - return "Bootscript" - } - return "" -} - -// CodeName returns a full resource name with typed prefix -func (s *ScalewayResolverResult) CodeName() string { - name := strings.ToLower(s.Name) - name = regexp.MustCompile(`[^a-z0-9-]`).ReplaceAllString(name, "-") - name = regexp.MustCompile(`--+`).ReplaceAllString(name, "-") - name = strings.Trim(name, "-") - - return fmt.Sprintf("%s:%s", strings.ToLower(identifierTypeName(s.Type)), name) -} - -// FilterByArch deletes the elements which not match with arch -func (s *ScalewayResolverResults) FilterByArch(arch string) { -REDO: - for i := range *s { - if (*s)[i].Arch != arch { - (*s)[i] = (*s)[len(*s)-1] - *s = (*s)[:len(*s)-1] - goto REDO - } - } -} - -// NewScalewayCache loads a per-user cache -func NewScalewayCache(hookSave func()) (*ScalewayCache, error) { - var cache ScalewayCache - - cache.hookSave = hookSave - homeDir := os.Getenv("HOME") // *nix - if homeDir == "" { // Windows - homeDir = os.Getenv("USERPROFILE") - } - if homeDir == "" { - homeDir = "/tmp" - } - cachePath := filepath.Join(homeDir, ".scw-cache.db") - cache.Path = cachePath - _, err := os.Stat(cachePath) - if os.IsNotExist(err) { - cache.Clear() - return &cache, nil - } else if err != nil { - return nil, err - } - file, err := ioutil.ReadFile(cachePath) - if err != nil { - return nil, err - } - err = json.Unmarshal(file, &cache) - if err != nil { - // fix compatibility with older version - if err = os.Remove(cachePath); err != nil { - return nil, err - } - cache.Clear() - return &cache, nil - } - if cache.Images == nil { - cache.Images = make(map[string][CacheMaxfield]string) - } - if cache.Snapshots == nil { - cache.Snapshots = make(map[string][CacheMaxfield]string) - } - if cache.Volumes == nil { - cache.Volumes = make(map[string][CacheMaxfield]string) - } - if cache.Servers == nil { - cache.Servers = make(map[string][CacheMaxfield]string) - } - if cache.Bootscripts == nil { - cache.Bootscripts = make(map[string][CacheMaxfield]string) - } - return &cache, nil -} - -// Clear removes all information from the cache -func (c *ScalewayCache) Clear() { - c.Images = make(map[string][CacheMaxfield]string) - c.Snapshots = make(map[string][CacheMaxfield]string) - c.Volumes = make(map[string][CacheMaxfield]string) - c.Bootscripts = make(map[string][CacheMaxfield]string) - c.Servers = make(map[string][CacheMaxfield]string) - c.Modified = true -} - -// Flush flushes the cache database -func (c *ScalewayCache) Flush() error { - return os.Remove(c.Path) -} - -// Save atomically overwrites the current cache database -func (c *ScalewayCache) Save() error { - c.Lock.Lock() - defer c.Lock.Unlock() - - c.hookSave() - if c.Modified { - file, err := ioutil.TempFile(filepath.Dir(c.Path), filepath.Base(c.Path)) - if err != nil { - return err - } - - if err := json.NewEncoder(file).Encode(c); err != nil { - file.Close() - os.Remove(file.Name()) - return err - } - - file.Close() - if err := os.Rename(file.Name(), c.Path); err != nil { - os.Remove(file.Name()) - return err - } - } - return nil -} - -// ComputeRankMatch fills `ScalewayResolverResult.RankMatch` with its `fuzzy` score -func (s *ScalewayResolverResult) ComputeRankMatch(needle string) { - s.Needle = needle - s.RankMatch = fuzzy.RankMatch(needle, s.Name) -} - -// LookUpImages attempts to return identifiers matching a pattern -func (c *ScalewayCache) LookUpImages(needle string, acceptUUID bool) (ScalewayResolverResults, error) { - c.Lock.Lock() - defer c.Lock.Unlock() - - var res ScalewayResolverResults - var exactMatches ScalewayResolverResults - - if acceptUUID && anonuuid.IsUUID(needle) == nil { - if fields, ok := c.Images[needle]; ok { - entry, err := NewScalewayResolverResult(needle, fields[CacheTitle], fields[CacheArch], fields[CacheRegion], IdentifierImage) - if err != nil { - return ScalewayResolverResults{}, err - } - entry.ComputeRankMatch(needle) - res = append(res, entry) - } - } - - needle = regexp.MustCompile(`^user/`).ReplaceAllString(needle, "") - // FIXME: if 'user/' is in needle, only watch for a user image - nameRegex := regexp.MustCompile(`(?i)` + regexp.MustCompile(`[_-]`).ReplaceAllString(needle, ".*")) - for identifier, fields := range c.Images { - if fields[CacheTitle] == needle { - entry, err := NewScalewayResolverResult(identifier, fields[CacheTitle], fields[CacheArch], fields[CacheRegion], IdentifierImage) - if err != nil { - return ScalewayResolverResults{}, err - } - entry.ComputeRankMatch(needle) - exactMatches = append(exactMatches, entry) - } - if strings.HasPrefix(identifier, needle) || nameRegex.MatchString(fields[CacheTitle]) { - entry, err := NewScalewayResolverResult(identifier, fields[CacheTitle], fields[CacheArch], fields[CacheRegion], IdentifierImage) - if err != nil { - return ScalewayResolverResults{}, err - } - entry.ComputeRankMatch(needle) - res = append(res, entry) - } else if strings.HasPrefix(fields[CacheMarketPlaceUUID], needle) || nameRegex.MatchString(fields[CacheMarketPlaceUUID]) { - entry, err := NewScalewayResolverResult(identifier, fields[CacheTitle], fields[CacheArch], fields[CacheRegion], IdentifierImage) - if err != nil { - return ScalewayResolverResults{}, err - } - entry.ComputeRankMatch(needle) - res = append(res, entry) - } - } - - if len(exactMatches) == 1 { - return exactMatches, nil - } - - return removeDuplicatesResults(res), nil -} - -// LookUpSnapshots attempts to return identifiers matching a pattern -func (c *ScalewayCache) LookUpSnapshots(needle string, acceptUUID bool) (ScalewayResolverResults, error) { - c.Lock.Lock() - defer c.Lock.Unlock() - - var res ScalewayResolverResults - var exactMatches ScalewayResolverResults - - if acceptUUID && anonuuid.IsUUID(needle) == nil { - if fields, ok := c.Snapshots[needle]; ok { - entry, err := NewScalewayResolverResult(needle, fields[CacheTitle], fields[CacheArch], fields[CacheRegion], IdentifierSnapshot) - if err != nil { - return ScalewayResolverResults{}, err - } - entry.ComputeRankMatch(needle) - res = append(res, entry) - } - } - - needle = regexp.MustCompile(`^user/`).ReplaceAllString(needle, "") - nameRegex := regexp.MustCompile(`(?i)` + regexp.MustCompile(`[_-]`).ReplaceAllString(needle, ".*")) - for identifier, fields := range c.Snapshots { - if fields[CacheTitle] == needle { - entry, err := NewScalewayResolverResult(identifier, fields[CacheTitle], fields[CacheArch], fields[CacheRegion], IdentifierSnapshot) - if err != nil { - return ScalewayResolverResults{}, err - } - entry.ComputeRankMatch(needle) - exactMatches = append(exactMatches, entry) - } - if strings.HasPrefix(identifier, needle) || nameRegex.MatchString(fields[CacheTitle]) { - entry, err := NewScalewayResolverResult(identifier, fields[CacheTitle], fields[CacheArch], fields[CacheRegion], IdentifierSnapshot) - if err != nil { - return ScalewayResolverResults{}, err - } - entry.ComputeRankMatch(needle) - res = append(res, entry) - } - } - - if len(exactMatches) == 1 { - return exactMatches, nil - } - - return removeDuplicatesResults(res), nil -} - -// LookUpVolumes attempts to return identifiers matching a pattern -func (c *ScalewayCache) LookUpVolumes(needle string, acceptUUID bool) (ScalewayResolverResults, error) { - c.Lock.Lock() - defer c.Lock.Unlock() - - var res ScalewayResolverResults - var exactMatches ScalewayResolverResults - - if acceptUUID && anonuuid.IsUUID(needle) == nil { - if fields, ok := c.Volumes[needle]; ok { - entry, err := NewScalewayResolverResult(needle, fields[CacheTitle], fields[CacheArch], fields[CacheRegion], IdentifierVolume) - if err != nil { - return ScalewayResolverResults{}, err - } - entry.ComputeRankMatch(needle) - res = append(res, entry) - } - } - - nameRegex := regexp.MustCompile(`(?i)` + regexp.MustCompile(`[_-]`).ReplaceAllString(needle, ".*")) - for identifier, fields := range c.Volumes { - if fields[CacheTitle] == needle { - entry, err := NewScalewayResolverResult(identifier, fields[CacheTitle], fields[CacheArch], fields[CacheRegion], IdentifierVolume) - if err != nil { - return ScalewayResolverResults{}, err - } - entry.ComputeRankMatch(needle) - exactMatches = append(exactMatches, entry) - } - if strings.HasPrefix(identifier, needle) || nameRegex.MatchString(fields[CacheTitle]) { - entry, err := NewScalewayResolverResult(identifier, fields[CacheTitle], fields[CacheArch], fields[CacheRegion], IdentifierVolume) - if err != nil { - return ScalewayResolverResults{}, err - } - entry.ComputeRankMatch(needle) - res = append(res, entry) - } - } - - if len(exactMatches) == 1 { - return exactMatches, nil - } - - return removeDuplicatesResults(res), nil -} - -// LookUpBootscripts attempts to return identifiers matching a pattern -func (c *ScalewayCache) LookUpBootscripts(needle string, acceptUUID bool) (ScalewayResolverResults, error) { - c.Lock.Lock() - defer c.Lock.Unlock() - - var res ScalewayResolverResults - var exactMatches ScalewayResolverResults - - if acceptUUID && anonuuid.IsUUID(needle) == nil { - if fields, ok := c.Bootscripts[needle]; ok { - entry, err := NewScalewayResolverResult(needle, fields[CacheTitle], fields[CacheArch], fields[CacheRegion], IdentifierBootscript) - if err != nil { - return ScalewayResolverResults{}, err - } - entry.ComputeRankMatch(needle) - res = append(res, entry) - } - } - - nameRegex := regexp.MustCompile(`(?i)` + regexp.MustCompile(`[_-]`).ReplaceAllString(needle, ".*")) - for identifier, fields := range c.Bootscripts { - if fields[CacheTitle] == needle { - entry, err := NewScalewayResolverResult(identifier, fields[CacheTitle], fields[CacheArch], fields[CacheRegion], IdentifierBootscript) - if err != nil { - return ScalewayResolverResults{}, err - } - entry.ComputeRankMatch(needle) - exactMatches = append(exactMatches, entry) - } - if strings.HasPrefix(identifier, needle) || nameRegex.MatchString(fields[CacheTitle]) { - entry, err := NewScalewayResolverResult(identifier, fields[CacheTitle], fields[CacheArch], fields[CacheRegion], IdentifierBootscript) - if err != nil { - return ScalewayResolverResults{}, err - } - entry.ComputeRankMatch(needle) - res = append(res, entry) - } - } - - if len(exactMatches) == 1 { - return exactMatches, nil - } - - return removeDuplicatesResults(res), nil -} - -// LookUpServers attempts to return identifiers matching a pattern -func (c *ScalewayCache) LookUpServers(needle string, acceptUUID bool) (ScalewayResolverResults, error) { - c.Lock.Lock() - defer c.Lock.Unlock() - - var res ScalewayResolverResults - var exactMatches ScalewayResolverResults - - if acceptUUID && anonuuid.IsUUID(needle) == nil { - if fields, ok := c.Servers[needle]; ok { - entry, err := NewScalewayResolverResult(needle, fields[CacheTitle], fields[CacheArch], fields[CacheRegion], IdentifierServer) - if err != nil { - return ScalewayResolverResults{}, err - } - entry.ComputeRankMatch(needle) - res = append(res, entry) - } - } - - nameRegex := regexp.MustCompile(`(?i)` + regexp.MustCompile(`[_-]`).ReplaceAllString(needle, ".*")) - for identifier, fields := range c.Servers { - if fields[CacheTitle] == needle { - entry, err := NewScalewayResolverResult(identifier, fields[CacheTitle], fields[CacheArch], fields[CacheRegion], IdentifierServer) - if err != nil { - return ScalewayResolverResults{}, err - } - entry.ComputeRankMatch(needle) - exactMatches = append(exactMatches, entry) - } - if strings.HasPrefix(identifier, needle) || nameRegex.MatchString(fields[CacheTitle]) { - entry, err := NewScalewayResolverResult(identifier, fields[CacheTitle], fields[CacheArch], fields[CacheRegion], IdentifierServer) - if err != nil { - return ScalewayResolverResults{}, err - } - entry.ComputeRankMatch(needle) - res = append(res, entry) - } - } - - if len(exactMatches) == 1 { - return exactMatches, nil - } - - return removeDuplicatesResults(res), nil -} - -// removeDuplicatesResults transforms an array into a unique array -func removeDuplicatesResults(elements ScalewayResolverResults) ScalewayResolverResults { - encountered := map[string]ScalewayResolverResult{} - - // Create a map of all unique elements. - for v := range elements { - encountered[elements[v].Identifier] = elements[v] - } - - // Place all keys from the map into a slice. - results := ScalewayResolverResults{} - for _, result := range encountered { - results = append(results, result) - } - return results -} - -// parseNeedle parses a user needle and try to extract a forced object type -// i.e: -// - server:blah-blah -> kind=server, needle=blah-blah -// - blah-blah -> kind="", needle=blah-blah -// - not-existing-type:blah-blah -func parseNeedle(input string) (identifierType int, needle string) { - parts := strings.Split(input, ":") - if len(parts) == 2 { - switch parts[0] { - case "server": - return IdentifierServer, parts[1] - case "image": - return IdentifierImage, parts[1] - case "snapshot": - return IdentifierSnapshot, parts[1] - case "bootscript": - return IdentifierBootscript, parts[1] - case "volume": - return IdentifierVolume, parts[1] - } - } - return IdentifierUnknown, input -} - -// LookUpIdentifiers attempts to return identifiers matching a pattern -func (c *ScalewayCache) LookUpIdentifiers(needle string) (ScalewayResolverResults, error) { - results := ScalewayResolverResults{} - - identifierType, needle := parseNeedle(needle) - - if identifierType&(IdentifierUnknown|IdentifierServer) > 0 { - servers, err := c.LookUpServers(needle, false) - if err != nil { - return ScalewayResolverResults{}, err - } - for _, result := range servers { - entry, err := NewScalewayResolverResult(result.Identifier, result.Name, result.Arch, result.Region, IdentifierServer) - if err != nil { - return ScalewayResolverResults{}, err - } - entry.ComputeRankMatch(needle) - results = append(results, entry) - } - } - - if identifierType&(IdentifierUnknown|IdentifierImage) > 0 { - images, err := c.LookUpImages(needle, false) - if err != nil { - return ScalewayResolverResults{}, err - } - for _, result := range images { - entry, err := NewScalewayResolverResult(result.Identifier, result.Name, result.Arch, result.Region, IdentifierImage) - if err != nil { - return ScalewayResolverResults{}, err - } - entry.ComputeRankMatch(needle) - results = append(results, entry) - } - } - - if identifierType&(IdentifierUnknown|IdentifierSnapshot) > 0 { - snapshots, err := c.LookUpSnapshots(needle, false) - if err != nil { - return ScalewayResolverResults{}, err - } - for _, result := range snapshots { - entry, err := NewScalewayResolverResult(result.Identifier, result.Name, result.Arch, result.Region, IdentifierSnapshot) - if err != nil { - return ScalewayResolverResults{}, err - } - entry.ComputeRankMatch(needle) - results = append(results, entry) - } - } - - if identifierType&(IdentifierUnknown|IdentifierVolume) > 0 { - volumes, err := c.LookUpVolumes(needle, false) - if err != nil { - return ScalewayResolverResults{}, err - } - for _, result := range volumes { - entry, err := NewScalewayResolverResult(result.Identifier, result.Name, result.Arch, result.Region, IdentifierVolume) - if err != nil { - return ScalewayResolverResults{}, err - } - entry.ComputeRankMatch(needle) - results = append(results, entry) - } - } - - if identifierType&(IdentifierUnknown|IdentifierBootscript) > 0 { - bootscripts, err := c.LookUpBootscripts(needle, false) - if err != nil { - return ScalewayResolverResults{}, err - } - for _, result := range bootscripts { - entry, err := NewScalewayResolverResult(result.Identifier, result.Name, result.Arch, result.Region, IdentifierBootscript) - if err != nil { - return ScalewayResolverResults{}, err - } - entry.ComputeRankMatch(needle) - results = append(results, entry) - } - } - return results, nil -} - -// InsertServer registers a server in the cache -func (c *ScalewayCache) InsertServer(identifier, region, arch, owner, name string) { - c.Lock.Lock() - defer c.Lock.Unlock() - - fields, exists := c.Servers[identifier] - if !exists || fields[CacheTitle] != name { - c.Servers[identifier] = [CacheMaxfield]string{region, arch, owner, name} - c.Modified = true - } -} - -// RemoveServer removes a server from the cache -func (c *ScalewayCache) RemoveServer(identifier string) { - c.Lock.Lock() - defer c.Lock.Unlock() - - delete(c.Servers, identifier) - c.Modified = true -} - -// ClearServers removes all servers from the cache -func (c *ScalewayCache) ClearServers() { - c.Lock.Lock() - defer c.Lock.Unlock() - - c.Servers = make(map[string][CacheMaxfield]string) - c.Modified = true -} - -// InsertImage registers an image in the cache -func (c *ScalewayCache) InsertImage(identifier, region, arch, owner, name, marketPlaceUUID string) { - c.Lock.Lock() - defer c.Lock.Unlock() - - fields, exists := c.Images[identifier] - if !exists || fields[CacheTitle] != name { - c.Images[identifier] = [CacheMaxfield]string{region, arch, owner, name, marketPlaceUUID} - c.Modified = true - } -} - -// RemoveImage removes a server from the cache -func (c *ScalewayCache) RemoveImage(identifier string) { - c.Lock.Lock() - defer c.Lock.Unlock() - - delete(c.Images, identifier) - c.Modified = true -} - -// ClearImages removes all images from the cache -func (c *ScalewayCache) ClearImages() { - c.Lock.Lock() - defer c.Lock.Unlock() - - c.Images = make(map[string][CacheMaxfield]string) - c.Modified = true -} - -// InsertSnapshot registers an snapshot in the cache -func (c *ScalewayCache) InsertSnapshot(identifier, region, arch, owner, name string) { - c.Lock.Lock() - defer c.Lock.Unlock() - - fields, exists := c.Snapshots[identifier] - if !exists || fields[CacheTitle] != name { - c.Snapshots[identifier] = [CacheMaxfield]string{region, arch, owner, name} - c.Modified = true - } -} - -// RemoveSnapshot removes a server from the cache -func (c *ScalewayCache) RemoveSnapshot(identifier string) { - c.Lock.Lock() - defer c.Lock.Unlock() - - delete(c.Snapshots, identifier) - c.Modified = true -} - -// ClearSnapshots removes all snapshots from the cache -func (c *ScalewayCache) ClearSnapshots() { - c.Lock.Lock() - defer c.Lock.Unlock() - - c.Snapshots = make(map[string][CacheMaxfield]string) - c.Modified = true -} - -// InsertVolume registers an volume in the cache -func (c *ScalewayCache) InsertVolume(identifier, region, arch, owner, name string) { - c.Lock.Lock() - defer c.Lock.Unlock() - - fields, exists := c.Volumes[identifier] - if !exists || fields[CacheTitle] != name { - c.Volumes[identifier] = [CacheMaxfield]string{region, arch, owner, name} - c.Modified = true - } -} - -// RemoveVolume removes a server from the cache -func (c *ScalewayCache) RemoveVolume(identifier string) { - c.Lock.Lock() - defer c.Lock.Unlock() - - delete(c.Volumes, identifier) - c.Modified = true -} - -// ClearVolumes removes all volumes from the cache -func (c *ScalewayCache) ClearVolumes() { - c.Lock.Lock() - defer c.Lock.Unlock() - - c.Volumes = make(map[string][CacheMaxfield]string) - c.Modified = true -} - -// InsertBootscript registers an bootscript in the cache -func (c *ScalewayCache) InsertBootscript(identifier, region, arch, owner, name string) { - c.Lock.Lock() - defer c.Lock.Unlock() - - fields, exists := c.Bootscripts[identifier] - if !exists || fields[CacheTitle] != name { - c.Bootscripts[identifier] = [CacheMaxfield]string{region, arch, owner, name} - c.Modified = true - } -} - -// RemoveBootscript removes a bootscript from the cache -func (c *ScalewayCache) RemoveBootscript(identifier string) { - c.Lock.Lock() - defer c.Lock.Unlock() - - delete(c.Bootscripts, identifier) - c.Modified = true -} - -// ClearBootscripts removes all bootscripts from the cache -func (c *ScalewayCache) ClearBootscripts() { - c.Lock.Lock() - defer c.Lock.Unlock() - - c.Bootscripts = make(map[string][CacheMaxfield]string) - c.Modified = true -} - -// GetNbServers returns the number of servers in the cache -func (c *ScalewayCache) GetNbServers() int { - c.Lock.Lock() - defer c.Lock.Unlock() - - return len(c.Servers) -} - -// GetNbImages returns the number of images in the cache -func (c *ScalewayCache) GetNbImages() int { - c.Lock.Lock() - defer c.Lock.Unlock() - - return len(c.Images) -} - -// GetNbSnapshots returns the number of snapshots in the cache -func (c *ScalewayCache) GetNbSnapshots() int { - c.Lock.Lock() - defer c.Lock.Unlock() - - return len(c.Snapshots) -} - -// GetNbVolumes returns the number of volumes in the cache -func (c *ScalewayCache) GetNbVolumes() int { - c.Lock.Lock() - defer c.Lock.Unlock() - - return len(c.Volumes) -} - -// GetNbBootscripts returns the number of bootscripts in the cache -func (c *ScalewayCache) GetNbBootscripts() int { - c.Lock.Lock() - defer c.Lock.Unlock() - - return len(c.Bootscripts) -} diff --git a/vendor/github.com/scaleway/scaleway-cli/pkg/api/helpers.go b/vendor/github.com/scaleway/scaleway-cli/pkg/api/helpers.go deleted file mode 100644 index d0e33f932..000000000 --- a/vendor/github.com/scaleway/scaleway-cli/pkg/api/helpers.go +++ /dev/null @@ -1,725 +0,0 @@ -// Copyright (C) 2015 Scaleway. All rights reserved. -// Use of this source code is governed by a MIT-style -// license that can be found in the LICENSE.md file. - -package api - -import ( - "errors" - "fmt" - "math" - "os" - "sort" - "strings" - "sync" - "time" - - "github.com/docker/docker/pkg/namesgenerator" - "github.com/dustin/go-humanize" - "github.com/moul/anonuuid" - "github.com/scaleway/scaleway-cli/pkg/utils" - "github.com/sirupsen/logrus" - log "github.com/sirupsen/logrus" -) - -// ScalewayResolvedIdentifier represents a list of matching identifier for a specifier pattern -type ScalewayResolvedIdentifier struct { - // Identifiers holds matching identifiers - Identifiers ScalewayResolverResults - - // Needle is the criteria used to lookup identifiers - Needle string -} - -// ScalewayImageInterface is an interface to multiple Scaleway items -type ScalewayImageInterface struct { - CreationDate time.Time - Identifier string - Name string - Tag string - VirtualSize uint64 - Public bool - Type string - Organization string - Archs []string - Region []string -} - -// ResolveGateway tries to resolve a server public ip address, else returns the input string, i.e. IPv4, hostname -func ResolveGateway(api *ScalewayAPI, gateway string) (string, error) { - if gateway == "" { - return "", nil - } - - // Parses optional type prefix, i.e: "server:name" -> "name" - _, gateway = parseNeedle(gateway) - - servers, err := api.ResolveServer(gateway) - if err != nil { - return "", err - } - - if len(servers) == 0 { - return gateway, nil - } - - if len(servers) > 1 { - return "", showResolverResults(gateway, servers) - } - - // if len(servers) == 1 { - server, err := api.GetServer(servers[0].Identifier) - if err != nil { - return "", err - } - return server.PublicAddress.IP, nil -} - -// CreateVolumeFromHumanSize creates a volume on the API with a human readable size -func CreateVolumeFromHumanSize(api *ScalewayAPI, size string) (*string, error) { - bytes, err := humanize.ParseBytes(size) - if err != nil { - return nil, err - } - - var newVolume ScalewayVolumeDefinition - newVolume.Name = size - newVolume.Size = bytes - newVolume.Type = "l_ssd" - - volumeID, err := api.PostVolume(newVolume) - if err != nil { - return nil, err - } - - return &volumeID, nil -} - -// VolumesFromSize returns a string of standard sized volumes from a given size -func VolumesFromSize(size uint64) string { - const DefaultVolumeSize float64 = 50000000000 - StdVolumeSizes := []struct { - kind string - capacity float64 - }{ - {"150G", 150000000000}, - {"100G", 100000000000}, - {"50G", 50000000000}, - } - - RequiredSize := float64(size) - DefaultVolumeSize - Volumes := "" - for _, v := range StdVolumeSizes { - q := RequiredSize / v.capacity - r := math.Mod(RequiredSize, v.capacity) - RequiredSize = r - - if q > 0 { - Volumes += strings.Repeat(v.kind+" ", int(q)) - } - if r == 0 { - break - } - } - - return strings.TrimSpace(Volumes) -} - -// fillIdentifierCache fills the cache by fetching from the API -func fillIdentifierCache(api *ScalewayAPI, identifierType int) { - log.Debugf("Filling the cache") - var wg sync.WaitGroup - wg.Add(5) - go func() { - if identifierType&(IdentifierUnknown|IdentifierServer) > 0 { - api.GetServers(true, 0) - } - wg.Done() - }() - go func() { - if identifierType&(IdentifierUnknown|IdentifierImage) > 0 { - api.GetImages() - } - wg.Done() - }() - go func() { - if identifierType&(IdentifierUnknown|IdentifierSnapshot) > 0 { - api.GetSnapshots() - } - wg.Done() - }() - go func() { - if identifierType&(IdentifierUnknown|IdentifierVolume) > 0 { - api.GetVolumes() - } - wg.Done() - }() - go func() { - if identifierType&(IdentifierUnknown|IdentifierBootscript) > 0 { - api.GetBootscripts() - } - wg.Done() - }() - wg.Wait() -} - -// GetIdentifier returns a an identifier if the resolved needles only match one element, else, it exists the program -func GetIdentifier(api *ScalewayAPI, needle string) (*ScalewayResolverResult, error) { - idents, err := ResolveIdentifier(api, needle) - if err != nil { - return nil, err - } - - if len(idents) == 1 { - return &idents[0], nil - } - if len(idents) == 0 { - return nil, fmt.Errorf("No such identifier: %s", needle) - } - - sort.Sort(idents) - for _, identifier := range idents { - // FIXME: also print the name - fmt.Fprintf(os.Stderr, "- %s\n", identifier.Identifier) - } - return nil, fmt.Errorf("Too many candidates for %s (%d)", needle, len(idents)) -} - -// ResolveIdentifier resolves needle provided by the user -func ResolveIdentifier(api *ScalewayAPI, needle string) (ScalewayResolverResults, error) { - idents, err := api.Cache.LookUpIdentifiers(needle) - if err != nil { - return idents, err - } - if len(idents) > 0 { - return idents, nil - } - - identifierType, _ := parseNeedle(needle) - fillIdentifierCache(api, identifierType) - - return api.Cache.LookUpIdentifiers(needle) -} - -// ResolveIdentifiers resolves needles provided by the user -func ResolveIdentifiers(api *ScalewayAPI, needles []string, out chan ScalewayResolvedIdentifier) { - // first attempt, only lookup from the cache - var unresolved []string - for _, needle := range needles { - idents, err := api.Cache.LookUpIdentifiers(needle) - if err != nil { - api.Logger.Fatalf("%s", err) - } - if len(idents) == 0 { - unresolved = append(unresolved, needle) - } else { - out <- ScalewayResolvedIdentifier{ - Identifiers: idents, - Needle: needle, - } - } - } - // fill the cache by fetching from the API and resolve missing identifiers - if len(unresolved) > 0 { - // compute identifierType: - // if identifierType is the same for every unresolved needle, - // we use it directly, else, we choose IdentifierUnknown to - // fulfill every types of cache - identifierType, _ := parseNeedle(unresolved[0]) - for _, needle := range unresolved { - newIdentifierType, _ := parseNeedle(needle) - if identifierType != newIdentifierType { - identifierType = IdentifierUnknown - break - } - } - - // fill all the cache - fillIdentifierCache(api, identifierType) - - // lookup again in the cache - for _, needle := range unresolved { - idents, err := api.Cache.LookUpIdentifiers(needle) - if err != nil { - api.Logger.Fatalf("%s", err) - } - out <- ScalewayResolvedIdentifier{ - Identifiers: idents, - Needle: needle, - } - } - } - - close(out) -} - -// InspectIdentifierResult is returned by `InspectIdentifiers` and contains the inspected `Object` with its `Type` -type InspectIdentifierResult struct { - Type int - Object interface{} -} - -// InspectIdentifiers inspects identifiers concurrently -func InspectIdentifiers(api *ScalewayAPI, ci chan ScalewayResolvedIdentifier, cj chan InspectIdentifierResult, arch string) { - var wg sync.WaitGroup - for { - idents, ok := <-ci - if !ok { - break - } - idents.Identifiers = FilterImagesByArch(idents.Identifiers, arch) - idents.Identifiers = FilterImagesByRegion(idents.Identifiers, api.Region) - if len(idents.Identifiers) != 1 { - if len(idents.Identifiers) == 0 { - log.Errorf("Unable to resolve identifier %s", idents.Needle) - } else { - logrus.Fatal(showResolverResults(idents.Needle, idents.Identifiers)) - } - } else { - ident := idents.Identifiers[0] - wg.Add(1) - go func() { - var obj interface{} - var err error - - switch ident.Type { - case IdentifierServer: - obj, err = api.GetServer(ident.Identifier) - case IdentifierImage: - obj, err = api.GetImage(ident.Identifier) - case IdentifierSnapshot: - obj, err = api.GetSnapshot(ident.Identifier) - case IdentifierVolume: - obj, err = api.GetVolume(ident.Identifier) - case IdentifierBootscript: - obj, err = api.GetBootscript(ident.Identifier) - } - if err == nil && obj != nil { - cj <- InspectIdentifierResult{ - Type: ident.Type, - Object: obj, - } - } - wg.Done() - }() - } - } - wg.Wait() - close(cj) -} - -// ConfigCreateServer represents the options sent to CreateServer and defining a server -type ConfigCreateServer struct { - ImageName string - Name string - Bootscript string - Env string - AdditionalVolumes string - IP string - CommercialType string - DynamicIPRequired bool - EnableIPV6 bool - BootType string -} - -// Return offer from any of the product name or alternate names -func OfferNameFromName(name string, products *ScalewayProductsServers) (*ProductServer, error) { - offer, ok := products.Servers[name] - if ok { - return &offer, nil - } - - for _, v := range products.Servers { - for _, alt := range v.AltNames { - alt := strings.ToUpper(alt) - if alt == name { - return &v, nil - } - } - } - - return nil, fmt.Errorf("Unknow commercial type: %v", name) -} - -// CreateServer creates a server using API based on typical server fields -func CreateServer(api *ScalewayAPI, c *ConfigCreateServer) (string, error) { - commercialType := os.Getenv("SCW_COMMERCIAL_TYPE") - if commercialType == "" { - commercialType = c.CommercialType - } - if len(commercialType) < 2 { - return "", errors.New("Invalid commercial type") - } - - if c.BootType != "local" && c.BootType != "bootscript" { - return "", errors.New("Invalid boot type") - } - - if c.Name == "" { - c.Name = strings.Replace(namesgenerator.GetRandomName(0), "_", "-", -1) - } - - var server ScalewayServerDefinition - - server.CommercialType = strings.ToUpper(commercialType) - server.Volumes = make(map[string]string) - server.DynamicIPRequired = &c.DynamicIPRequired - server.EnableIPV6 = c.EnableIPV6 - server.BootType = c.BootType - if commercialType == "" { - return "", errors.New("You need to specify a commercial-type") - } - if c.IP != "" { - if anonuuid.IsUUID(c.IP) == nil { - server.PublicIP = c.IP - } else { - ips, err := api.GetIPS() - if err != nil { - return "", err - } - for _, ip := range ips.IPS { - if ip.Address == c.IP { - server.PublicIP = ip.ID - break - } - } - if server.PublicIP == "" { - return "", fmt.Errorf("IP address %v not found", c.IP) - } - } - } - server.Tags = []string{} - if c.Env != "" { - server.Tags = strings.Split(c.Env, " ") - } - - products, err := api.GetProductsServers() - if err != nil { - return "", fmt.Errorf("Unable to fetch products list from the Scaleway API: %v", err) - } - offer, err := OfferNameFromName(server.CommercialType, products) - if err != nil { - return "", fmt.Errorf("Unknow commercial type %v: %v", server.CommercialType, err) - } - if offer.VolumesConstraint.MinSize > 0 && c.AdditionalVolumes == "" { - c.AdditionalVolumes = VolumesFromSize(offer.VolumesConstraint.MinSize) - log.Debugf("%s needs at least %s. Automatically creates the following volumes: %s", - server.CommercialType, humanize.Bytes(offer.VolumesConstraint.MinSize), c.AdditionalVolumes) - } - if c.AdditionalVolumes != "" { - volumes := strings.Split(c.AdditionalVolumes, " ") - for i := range volumes { - volumeID, err := CreateVolumeFromHumanSize(api, volumes[i]) - if err != nil { - return "", err - } - - volumeIDx := fmt.Sprintf("%d", i+1) - server.Volumes[volumeIDx] = *volumeID - } - } - - arch := os.Getenv("SCW_TARGET_ARCH") - if arch == "" { - arch = offer.Arch - } - imageIdentifier := &ScalewayImageIdentifier{ - Arch: arch, - } - server.Name = c.Name - inheritingVolume := false - _, err = humanize.ParseBytes(c.ImageName) - if err == nil { - // Create a new root volume - volumeID, errCreateVol := CreateVolumeFromHumanSize(api, c.ImageName) - if errCreateVol != nil { - return "", errCreateVol - } - server.Volumes["0"] = *volumeID - } else { - // Use an existing image - inheritingVolume = true - if anonuuid.IsUUID(c.ImageName) == nil { - server.Image = &c.ImageName - } else { - imageIdentifier, err = api.GetImageID(c.ImageName, arch) - if err != nil { - return "", err - } - if imageIdentifier.Identifier != "" { - server.Image = &imageIdentifier.Identifier - } else { - snapshotID, errGetSnapID := api.GetSnapshotID(c.ImageName) - if errGetSnapID != nil { - return "", errGetSnapID - } - snapshot, errGetSnap := api.GetSnapshot(snapshotID) - if errGetSnap != nil { - return "", errGetSnap - } - if snapshot.BaseVolume.Identifier == "" { - return "", fmt.Errorf("snapshot %v does not have base volume", snapshot.Name) - } - server.Volumes["0"] = snapshot.BaseVolume.Identifier - } - } - } - - if c.Bootscript != "" { - bootscript := "" - - if anonuuid.IsUUID(c.Bootscript) == nil { - bootscript = c.Bootscript - } else { - var errGetBootScript error - - bootscript, errGetBootScript = api.GetBootscriptID(c.Bootscript, imageIdentifier.Arch) - if errGetBootScript != nil { - return "", errGetBootScript - } - } - server.Bootscript = &bootscript - } - serverID, err := api.PostServer(server) - if err != nil { - return "", err - } - - // For inherited volumes, we prefix the name with server hostname - if inheritingVolume { - createdServer, err := api.GetServer(serverID) - if err != nil { - return "", err - } - currentVolume := createdServer.Volumes["0"] - - var volumePayload ScalewayVolumePutDefinition - newName := fmt.Sprintf("%s-%s", createdServer.Hostname, currentVolume.Name) - volumePayload.Name = &newName - volumePayload.CreationDate = ¤tVolume.CreationDate - volumePayload.Organization = ¤tVolume.Organization - volumePayload.Server.Identifier = ¤tVolume.Server.Identifier - volumePayload.Server.Name = ¤tVolume.Server.Name - volumePayload.Identifier = ¤tVolume.Identifier - volumePayload.Size = ¤tVolume.Size - volumePayload.ModificationDate = ¤tVolume.ModificationDate - volumePayload.ExportURI = ¤tVolume.ExportURI - volumePayload.VolumeType = ¤tVolume.VolumeType - - err = api.PutVolume(currentVolume.Identifier, volumePayload) - if err != nil { - return "", err - } - } - - return serverID, nil -} - -// WaitForServerState asks API in a loop until a server matches a wanted state -func WaitForServerState(api *ScalewayAPI, serverID string, targetState string) (*ScalewayServer, error) { - var server *ScalewayServer - var err error - - var currentState string - - for { - server, err = api.GetServer(serverID) - if err != nil { - return nil, err - } - if currentState != server.State { - log.Infof("Server changed state to '%s'", server.State) - currentState = server.State - } - if server.State == targetState { - break - } - time.Sleep(1 * time.Second) - } - - return server, nil -} - -// WaitForServerReady wait for a server state to be running, then wait for the SSH port to be available -func WaitForServerReady(api *ScalewayAPI, serverID, gateway string) (*ScalewayServer, error) { - promise := make(chan bool) - var server *ScalewayServer - var err error - var currentState string - - go func() { - defer close(promise) - - for { - server, err = api.GetServer(serverID) - if err != nil { - promise <- false - return - } - if currentState != server.State { - log.Infof("Server changed state to '%s'", server.State) - currentState = server.State - } - if server.State == "running" { - break - } - if server.State == "stopped" { - err = fmt.Errorf("The server has been stopped") - promise <- false - return - } - time.Sleep(1 * time.Second) - } - - if gateway == "" { - ip := server.PublicAddress.IP - if ip == "" && server.EnableIPV6 { - ip = fmt.Sprintf("[%s]", server.IPV6.Address) - } - dest := fmt.Sprintf("%s:22", ip) - log.Debugf("Waiting for server SSH port %s", dest) - err = utils.WaitForTCPPortOpen(dest) - if err != nil { - promise <- false - return - } - } else { - dest := fmt.Sprintf("%s:22", gateway) - log.Debugf("Waiting for server SSH port %s", dest) - err = utils.WaitForTCPPortOpen(dest) - if err != nil { - promise <- false - return - } - log.Debugf("Check for SSH port through the gateway: %s", server.PrivateIP) - timeout := time.Tick(120 * time.Second) - for { - select { - case <-timeout: - err = fmt.Errorf("Timeout: unable to ping %s", server.PrivateIP) - goto OUT - default: - if utils.SSHExec("", server.PrivateIP, "root", 22, []string{ - "nc", - "-z", - "-w", - "1", - server.PrivateIP, - "22", - }, false, gateway, false) == nil { - goto OUT - } - time.Sleep(2 * time.Second) - } - } - OUT: - if err != nil { - logrus.Info(err) - err = nil - } - } - promise <- true - }() - - loop := 0 - for { - select { - case done := <-promise: - utils.LogQuiet("\r \r") - if !done { - return nil, err - } - return server, nil - case <-time.After(time.Millisecond * 100): - utils.LogQuiet(fmt.Sprintf("\r%c\r", "-\\|/"[loop%4])) - loop = loop + 1 - if loop == 5 { - loop = 0 - } - } - } -} - -// WaitForServerStopped wait for a server state to be stopped -func WaitForServerStopped(api *ScalewayAPI, serverID string) (*ScalewayServer, error) { - server, err := WaitForServerState(api, serverID, "stopped") - if err != nil { - return nil, err - } - return server, nil -} - -// ByCreationDate sorts images by CreationDate field -type ByCreationDate []ScalewayImageInterface - -func (a ByCreationDate) Len() int { return len(a) } -func (a ByCreationDate) Swap(i, j int) { a[i], a[j] = a[j], a[i] } -func (a ByCreationDate) Less(i, j int) bool { return a[j].CreationDate.Before(a[i].CreationDate) } - -// StartServer start a server based on its needle, can optionaly block while server is booting -func StartServer(api *ScalewayAPI, needle string, wait bool) error { - server, err := api.GetServerID(needle) - if err != nil { - return err - } - - if err = api.PostServerAction(server, "poweron"); err != nil { - return err - } - - if wait { - _, err = WaitForServerReady(api, server, "") - if err != nil { - return fmt.Errorf("failed to wait for server %s to be ready, %v", needle, err) - } - } - return nil -} - -// StartServerOnce wraps StartServer for golang channel -func StartServerOnce(api *ScalewayAPI, needle string, wait bool, successChan chan string, errChan chan error) { - err := StartServer(api, needle, wait) - - if err != nil { - errChan <- err - return - } - successChan <- needle -} - -// DeleteServerForce tries to delete a server using multiple ways -func (a *ScalewayAPI) DeleteServerForce(serverID string) error { - // FIXME: also delete attached volumes and ip address - // FIXME: call delete and stop -t in parallel to speed up process - err := a.DeleteServer(serverID) - if err == nil { - logrus.Infof("Server '%s' successfully deleted", serverID) - return nil - } - - err = a.PostServerAction(serverID, "terminate") - if err == nil { - logrus.Infof("Server '%s' successfully terminated", serverID) - return nil - } - - // FIXME: retry in a loop until timeout or Control+C - logrus.Errorf("Failed to delete server %s", serverID) - logrus.Errorf("Try to run 'scw rm -f %s' later", serverID) - return err -} - -// GetSSHFingerprintFromServer returns an array which containts ssh-host-fingerprints -func (a *ScalewayAPI) GetSSHFingerprintFromServer(serverID string) []string { - ret := []string{} - - if value, err := a.GetUserdata(serverID, "ssh-host-fingerprints", false); err == nil { - PublicKeys := strings.Split(string(*value), "\n") - for i := range PublicKeys { - if fingerprint, err := utils.SSHGetFingerprint([]byte(PublicKeys[i])); err == nil { - ret = append(ret, fingerprint) - } - } - } - return ret -} diff --git a/vendor/github.com/scaleway/scaleway-cli/pkg/api/logger.go b/vendor/github.com/scaleway/scaleway-cli/pkg/api/logger.go deleted file mode 100644 index 58ad93716..000000000 --- a/vendor/github.com/scaleway/scaleway-cli/pkg/api/logger.go +++ /dev/null @@ -1,77 +0,0 @@ -// Copyright (C) 2015 Scaleway. All rights reserved. -// Use of this source code is governed by a MIT-style -// license that can be found in the LICENSE.md file. - -package api - -import ( - "fmt" - "log" - "net/http" - "os" -) - -// Logger handles logging concerns for the Scaleway API SDK -type Logger interface { - LogHTTP(*http.Request) - Fatalf(format string, v ...interface{}) - Debugf(format string, v ...interface{}) - Infof(format string, v ...interface{}) - Warnf(format string, v ...interface{}) -} - -// NewDefaultLogger returns a logger which is configured for stdout -func NewDefaultLogger() Logger { - return &defaultLogger{ - Logger: log.New(os.Stdout, "", log.LstdFlags), - } -} - -type defaultLogger struct { - *log.Logger -} - -func (l *defaultLogger) LogHTTP(r *http.Request) { - l.Printf("%s %s\n", r.Method, r.URL.RawPath) -} - -func (l *defaultLogger) Fatalf(format string, v ...interface{}) { - l.Printf("[FATAL] %s\n", fmt.Sprintf(format, v)) - os.Exit(1) -} - -func (l *defaultLogger) Debugf(format string, v ...interface{}) { - l.Printf("[DEBUG] %s\n", fmt.Sprintf(format, v)) -} - -func (l *defaultLogger) Infof(format string, v ...interface{}) { - l.Printf("[INFO ] %s\n", fmt.Sprintf(format, v)) -} - -func (l *defaultLogger) Warnf(format string, v ...interface{}) { - l.Printf("[WARN ] %s\n", fmt.Sprintf(format, v)) -} - -type disableLogger struct { -} - -// NewDisableLogger returns a logger which is configured to do nothing -func NewDisableLogger() Logger { - return &disableLogger{} -} - -func (d *disableLogger) LogHTTP(r *http.Request) { -} - -func (d *disableLogger) Fatalf(format string, v ...interface{}) { - panic(fmt.Sprintf(format, v)) -} - -func (d *disableLogger) Debugf(format string, v ...interface{}) { -} - -func (d *disableLogger) Infof(format string, v ...interface{}) { -} - -func (d *disableLogger) Warnf(format string, v ...interface{}) { -} diff --git a/vendor/github.com/scaleway/scaleway-cli/pkg/sshcommand/sshcommand.go b/vendor/github.com/scaleway/scaleway-cli/pkg/sshcommand/sshcommand.go deleted file mode 100644 index 7852ca0be..000000000 --- a/vendor/github.com/scaleway/scaleway-cli/pkg/sshcommand/sshcommand.go +++ /dev/null @@ -1,122 +0,0 @@ -package sshcommand - -import ( - "fmt" - "runtime" - "strings" -) - -// Command contains settings to build a ssh command -type Command struct { - Host string - User string - Port int - SSHOptions []string - Gateway *Command - Command []string - Debug bool - NoEscapeCommand bool - SkipHostKeyChecking bool - Quiet bool - AllocateTTY bool - EnableSSHKeyForwarding bool - - isGateway bool -} - -// New returns a minimal Command -func New(host string) *Command { - return &Command{ - Host: host, - } -} - -func (c *Command) applyDefaults() { - if strings.Contains(c.Host, "@") { - parts := strings.Split(c.Host, "@") - c.User = parts[0] - c.Host = parts[1] - } - - if c.Port == 0 { - c.Port = 22 - } - - if c.isGateway { - c.SSHOptions = []string{"-W", "%h:%p"} - } -} - -// Slice returns an execve compatible slice of arguments -func (c *Command) Slice() []string { - c.applyDefaults() - - slice := []string{"ssh"} - - if c.EnableSSHKeyForwarding { - slice = append(slice, "-A") - } - - if c.Quiet { - slice = append(slice, "-q") - } - - if c.SkipHostKeyChecking { - slice = append(slice, "-o", "UserKnownHostsFile=/dev/null", "-o", "StrictHostKeyChecking=no") - } - - if len(c.SSHOptions) > 0 { - slice = append(slice, c.SSHOptions...) - } - - if c.Gateway != nil { - c.Gateway.isGateway = true - slice = append(slice, "-o", "ProxyCommand="+c.Gateway.String()) - } - - if c.User != "" { - slice = append(slice, "-l", c.User) - } - - slice = append(slice, c.Host) - - if c.AllocateTTY { - slice = append(slice, "-t", "-t") - } - - slice = append(slice, "-p", fmt.Sprintf("%d", c.Port)) - if len(c.Command) > 0 { - slice = append(slice, "--", "/bin/sh", "-e") - if c.Debug { - slice = append(slice, "-x") - } - slice = append(slice, "-c") - - var escapedCommand []string - if c.NoEscapeCommand { - escapedCommand = c.Command - } else { - escapedCommand = []string{} - for _, part := range c.Command { - escapedCommand = append(escapedCommand, fmt.Sprintf("%q", part)) - } - } - slice = append(slice, fmt.Sprintf("%q", strings.Join(escapedCommand, " "))) - } - if runtime.GOOS == "windows" { - slice[len(slice)-1] = slice[len(slice)-1] + " " // Why ? - } - return slice -} - -// String returns a copy-pasteable command, useful for debugging -func (c *Command) String() string { - slice := c.Slice() - for i := range slice { - quoted := fmt.Sprintf("%q", slice[i]) - if strings.Contains(slice[i], " ") || len(quoted) != len(slice[i])+2 { - slice[i] = quoted - } - } - return strings.Join(slice, " ") -} diff --git a/vendor/github.com/scaleway/scaleway-cli/pkg/utils/quiet.go b/vendor/github.com/scaleway/scaleway-cli/pkg/utils/quiet.go deleted file mode 100644 index 775918d8d..000000000 --- a/vendor/github.com/scaleway/scaleway-cli/pkg/utils/quiet.go +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright (C) 2015 Scaleway. All rights reserved. -// Use of this source code is governed by a MIT-style -// license that can be found in the LICENSE.md file. - -// Package utils contains logquiet -package utils - -import ( - "fmt" - "os" -) - -// LogQuietStruct is a struct to store information about quiet state -type LogQuietStruct struct { - quiet bool -} - -var instanceQuiet LogQuietStruct - -// Quiet enable or disable quiet -func Quiet(option bool) { - instanceQuiet.quiet = option -} - -// LogQuiet Displays info if quiet is activated -func LogQuiet(str string) { - if !instanceQuiet.quiet { - fmt.Fprintf(os.Stderr, "%s", str) - } -} diff --git a/vendor/github.com/scaleway/scaleway-cli/pkg/utils/utils.go b/vendor/github.com/scaleway/scaleway-cli/pkg/utils/utils.go deleted file mode 100644 index 126d9c834..000000000 --- a/vendor/github.com/scaleway/scaleway-cli/pkg/utils/utils.go +++ /dev/null @@ -1,253 +0,0 @@ -// Copyright (C) 2015 Scaleway. All rights reserved. -// Use of this source code is governed by a MIT-style -// license that can be found in the LICENSE.md file. - -// scw helpers - -// Package utils contains helpers -package utils - -import ( - "crypto/md5" - "errors" - "fmt" - "io" - "net" - "os" - "os/exec" - "path" - "path/filepath" - "reflect" - "regexp" - "strings" - "time" - - "golang.org/x/crypto/ssh" - - "github.com/mattn/go-isatty" - "github.com/moul/gotty-client" - "github.com/scaleway/scaleway-cli/pkg/sshcommand" - "github.com/sirupsen/logrus" - log "github.com/sirupsen/logrus" -) - -// SpawnRedirection is used to redirects the fluxes -type SpawnRedirection struct { - Stdin io.Reader - Stdout io.Writer - Stderr io.Writer -} - -// SSHExec executes a command over SSH and redirects file-descriptors -func SSHExec(publicIPAddress, privateIPAddress, user string, port int, command []string, checkConnection bool, gateway string, enableSSHKeyForwarding bool) error { - gatewayUser := "root" - gatewayIPAddress := gateway - if strings.Contains(gateway, "@") { - parts := strings.Split(gatewayIPAddress, "@") - if len(parts) != 2 { - return fmt.Errorf("gateway: must be like root@IP") - } - gatewayUser = parts[0] - gatewayIPAddress = parts[1] - gateway = gatewayUser + "@" + gatewayIPAddress - } - - if publicIPAddress == "" && gatewayIPAddress == "" { - return errors.New("server does not have public IP") - } - if privateIPAddress == "" && gatewayIPAddress != "" { - return errors.New("server does not have private IP") - } - - if checkConnection { - useGateway := gatewayIPAddress != "" - if useGateway && !IsTCPPortOpen(fmt.Sprintf("%s:22", gatewayIPAddress)) { - return errors.New("gateway is not available, try again later") - } - if !useGateway && !IsTCPPortOpen(fmt.Sprintf("%s:%d", publicIPAddress, port)) { - return errors.New("server is not ready, try again later") - } - } - - sshCommand := NewSSHExecCmd(publicIPAddress, privateIPAddress, user, port, isatty.IsTerminal(os.Stdin.Fd()), command, gateway, enableSSHKeyForwarding) - - log.Debugf("Executing: %s", sshCommand) - - spawn := exec.Command("ssh", sshCommand.Slice()[1:]...) - spawn.Stdout = os.Stdout - spawn.Stdin = os.Stdin - spawn.Stderr = os.Stderr - return spawn.Run() -} - -// NewSSHExecCmd computes execve compatible arguments to run a command via ssh -func NewSSHExecCmd(publicIPAddress, privateIPAddress, user string, port int, allocateTTY bool, command []string, gatewayIPAddress string, enableSSHKeyForwarding bool) *sshcommand.Command { - quiet := os.Getenv("DEBUG") != "1" - secureExec := os.Getenv("SCW_SECURE_EXEC") == "1" - sshCommand := &sshcommand.Command{ - AllocateTTY: allocateTTY, - Command: command, - Host: publicIPAddress, - Quiet: quiet, - SkipHostKeyChecking: !secureExec, - User: user, - NoEscapeCommand: true, - Port: port, - EnableSSHKeyForwarding: enableSSHKeyForwarding, - } - if gatewayIPAddress != "" { - sshCommand.Host = privateIPAddress - sshCommand.Gateway = &sshcommand.Command{ - Host: gatewayIPAddress, - SkipHostKeyChecking: !secureExec, - AllocateTTY: allocateTTY, - Quiet: quiet, - User: user, - Port: port, - } - } - - return sshCommand -} - -// GeneratingAnSSHKey generates an SSH key -func GeneratingAnSSHKey(cfg SpawnRedirection, path string, name string) (string, error) { - args := []string{ - "-t", - "rsa", - "-b", - "4096", - "-f", - filepath.Join(path, name), - "-N", - "", - "-C", - "", - } - log.Infof("Executing commands %v", args) - spawn := exec.Command("ssh-keygen", args...) - spawn.Stdout = cfg.Stdout - spawn.Stdin = cfg.Stdin - spawn.Stderr = cfg.Stderr - return args[5], spawn.Run() -} - -// WaitForTCPPortOpen calls IsTCPPortOpen in a loop -func WaitForTCPPortOpen(dest string) error { - for { - if IsTCPPortOpen(dest) { - break - } - time.Sleep(1 * time.Second) - } - return nil -} - -// IsTCPPortOpen returns true if a TCP communication with "host:port" can be initialized -func IsTCPPortOpen(dest string) bool { - conn, err := net.DialTimeout("tcp", dest, time.Duration(2000)*time.Millisecond) - if err == nil { - defer conn.Close() - } - return err == nil -} - -// TruncIf ensures the input string does not exceed max size if cond is met -func TruncIf(str string, max int, cond bool) string { - if cond && len(str) > max { - return str[:max] - } - return str -} - -// Wordify convert complex name to a single word without special shell characters -func Wordify(str string) string { - str = regexp.MustCompile(`[^a-zA-Z0-9-]`).ReplaceAllString(str, "_") - str = regexp.MustCompile(`__+`).ReplaceAllString(str, "_") - str = strings.Trim(str, "_") - return str -} - -// PathToTARPathparts returns the two parts of a unix path -func PathToTARPathparts(fullPath string) (string, string) { - fullPath = strings.TrimRight(fullPath, "/") - return path.Dir(fullPath), path.Base(fullPath) -} - -// RemoveDuplicates transforms an array into a unique array -func RemoveDuplicates(elements []string) []string { - encountered := map[string]bool{} - - // Create a map of all unique elements. - for v := range elements { - encountered[elements[v]] = true - } - - // Place all keys from the map into a slice. - result := []string{} - for key := range encountered { - result = append(result, key) - } - return result -} - -// AttachToSerial tries to connect to server serial using 'gotty-client' and fallback with a help message -func AttachToSerial(serverID, apiToken, url string) (*gottyclient.Client, chan bool, error) { - gottyURL := os.Getenv("SCW_GOTTY_URL") - if gottyURL == "" { - gottyURL = url - } - URL := fmt.Sprintf("%s?arg=%s&arg=%s", gottyURL, apiToken, serverID) - - logrus.Debug("Connection to ", URL) - gottycli, err := gottyclient.NewClient(URL) - if err != nil { - return nil, nil, err - } - - if os.Getenv("SCW_TLSVERIFY") == "0" { - gottycli.SkipTLSVerify = true - } - - gottycli.UseProxyFromEnv = true - - if err = gottycli.Connect(); err != nil { - return nil, nil, err - } - done := make(chan bool) - - fmt.Println("You are connected, type 'Ctrl+q' to quit.") - go func() { - gottycli.Loop() - gottycli.Close() - done <- true - }() - return gottycli, done, nil -} - -func rfc4716hex(data []byte) string { - fingerprint := "" - - for i := 0; i < len(data); i++ { - fingerprint = fmt.Sprintf("%s%0.2x", fingerprint, data[i]) - if i != len(data)-1 { - fingerprint = fingerprint + ":" - } - } - return fingerprint -} - -// SSHGetFingerprint returns the fingerprint of an SSH key -func SSHGetFingerprint(key []byte) (string, error) { - publicKey, comment, _, _, err := ssh.ParseAuthorizedKey(key) - if err != nil { - return "", err - } - switch reflect.TypeOf(publicKey).String() { - case "*ssh.rsaPublicKey", "*ssh.dsaPublicKey", "*ssh.ecdsaPublicKey": - md5sum := md5.Sum(publicKey.Marshal()) - return publicKey.Type() + " " + rfc4716hex(md5sum[:]) + " " + comment, nil - default: - return "", errors.New("Can't handle this key") - } -} diff --git a/vendor/github.com/docker/docker/LICENSE b/vendor/github.com/scaleway/scaleway-sdk-go/LICENSE similarity index 99% rename from vendor/github.com/docker/docker/LICENSE rename to vendor/github.com/scaleway/scaleway-sdk-go/LICENSE index 9c8e20ab8..2712cc51a 100644 --- a/vendor/github.com/docker/docker/LICENSE +++ b/vendor/github.com/scaleway/scaleway-sdk-go/LICENSE @@ -176,7 +176,7 @@ END OF TERMS AND CONDITIONS - Copyright 2013-2017 Docker, Inc. + 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. 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..e9d4a90f7 --- /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) (*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, + }) + + 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..1f5919880 --- /dev/null +++ b/vendor/github.com/scaleway/scaleway-sdk-go/api/instance/v1/instance_metadata_sdk.go @@ -0,0 +1,273 @@ +package instance + +import ( + "bytes" + "encoding/json" + "io/ioutil" + "math/rand" + "net" + "net/http" + "strconv" + "time" + + "github.com/scaleway/scaleway-sdk-go/internal/errors" +) + +var ( + metadataURL = "http://169.254.42.42" + metadataRetryBindPort = 200 +) + +// MetadataAPI metadata API +type MetadataAPI struct { +} + +// NewMetadataAPI returns a MetadataAPI object from a Scaleway client. +func NewMetadataAPI() *MetadataAPI { + return &MetadataAPI{} +} + +// GetMetadata returns the metadata available from the server +func (*MetadataAPI) GetMetadata() (m *Metadata, err error) { + resp, err := http.Get(metadataURL + "/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 +} + +// 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 struct { + Dynamic bool `json:"dynamic,omitempty"` + ID string `json:"id,omitempty"` + Address string `json:"address,omitempty"` + } `json:"public_ip,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"` + } +} + +// ListUserData returns the metadata available from the server +func (*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(metadataURL + "/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 (*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(metadataURL + "/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 (*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", metadataURL+"/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 (*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", metadataURL+"/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..b97dd6850 --- /dev/null +++ b/vendor/github.com/scaleway/scaleway-sdk-go/api/instance/v1/instance_sdk.go @@ -0,0 +1,4707 @@ +// 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 +) + +// API: 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, + } +} + +type Arch string + +const ( + // ArchX86_64 is [insert doc]. + ArchX86_64 = Arch("x86_64") + // ArchArm is [insert doc]. + ArchArm = Arch("arm") +) + +func (enum Arch) String() string { + if enum == "" { + // return default value if empty + return "x86_64" + } + 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 BootType string + +const ( + // BootTypeLocal is [insert doc]. + BootTypeLocal = BootType("local") + // BootTypeBootscript is [insert doc]. + BootTypeBootscript = BootType("bootscript") + // BootTypeRescue is [insert doc]. + 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 ImageState string + +const ( + // ImageStateAvailable is [insert doc]. + ImageStateAvailable = ImageState("available") + // ImageStateCreating is [insert doc]. + ImageStateCreating = ImageState("creating") + // ImageStateError is [insert doc]. + 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 PlacementGroupPolicyMode string + +const ( + // PlacementGroupPolicyModeOptional is [insert doc]. + PlacementGroupPolicyModeOptional = PlacementGroupPolicyMode("optional") + // PlacementGroupPolicyModeEnforced is [insert doc]. + 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 is [insert doc]. + PlacementGroupPolicyTypeMaxAvailability = PlacementGroupPolicyType("max_availability") + // PlacementGroupPolicyTypeLowLatency is [insert doc]. + 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 SecurityGroupPolicy string + +const ( + // SecurityGroupPolicyAccept is [insert doc]. + SecurityGroupPolicyAccept = SecurityGroupPolicy("accept") + // SecurityGroupPolicyDrop is [insert doc]. + SecurityGroupPolicyDrop = SecurityGroupPolicy("drop") +) + +func (enum SecurityGroupPolicy) String() string { + if enum == "" { + // return default value if empty + return "accept" + } + 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 ( + // SecurityGroupRuleActionAccept is [insert doc]. + SecurityGroupRuleActionAccept = SecurityGroupRuleAction("accept") + // SecurityGroupRuleActionDrop is [insert doc]. + SecurityGroupRuleActionDrop = SecurityGroupRuleAction("drop") +) + +func (enum SecurityGroupRuleAction) String() string { + if enum == "" { + // return default value if empty + return "accept" + } + 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 ( + // SecurityGroupRuleDirectionInbound is [insert doc]. + SecurityGroupRuleDirectionInbound = SecurityGroupRuleDirection("inbound") + // SecurityGroupRuleDirectionOutbound is [insert doc]. + SecurityGroupRuleDirectionOutbound = SecurityGroupRuleDirection("outbound") +) + +func (enum SecurityGroupRuleDirection) String() string { + if enum == "" { + // return default value if empty + return "inbound" + } + 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 ( + // SecurityGroupRuleProtocolTCP is [insert doc]. + SecurityGroupRuleProtocolTCP = SecurityGroupRuleProtocol("TCP") + // SecurityGroupRuleProtocolUDP is [insert doc]. + SecurityGroupRuleProtocolUDP = SecurityGroupRuleProtocol("UDP") + // SecurityGroupRuleProtocolICMP is [insert doc]. + SecurityGroupRuleProtocolICMP = SecurityGroupRuleProtocol("ICMP") + // SecurityGroupRuleProtocolANY is [insert doc]. + SecurityGroupRuleProtocolANY = SecurityGroupRuleProtocol("ANY") +) + +func (enum SecurityGroupRuleProtocol) String() string { + if enum == "" { + // return default value if empty + return "TCP" + } + 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 ServerAction string + +const ( + // ServerActionPoweron is [insert doc]. + ServerActionPoweron = ServerAction("poweron") + // ServerActionBackup is [insert doc]. + ServerActionBackup = ServerAction("backup") + // ServerActionStopInPlace is [insert doc]. + ServerActionStopInPlace = ServerAction("stop_in_place") + // ServerActionPoweroff is [insert doc]. + ServerActionPoweroff = ServerAction("poweroff") + // ServerActionTerminate is [insert doc]. + ServerActionTerminate = ServerAction("terminate") + // ServerActionReboot is [insert doc]. + ServerActionReboot = ServerAction("reboot") +) + +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 ServerState string + +const ( + // ServerStateRunning is [insert doc]. + ServerStateRunning = ServerState("running") + // ServerStateStopped is [insert doc]. + ServerStateStopped = ServerState("stopped") + // ServerStateStoppedInPlace is [insert doc]. + ServerStateStoppedInPlace = ServerState("stopped in place") + // ServerStateStarting is [insert doc]. + ServerStateStarting = ServerState("starting") + // ServerStateStopping is [insert doc]. + ServerStateStopping = ServerState("stopping") + // ServerStateLocked is [insert doc]. + 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 is [insert doc]. + ServerTypesAvailabilityAvailable = ServerTypesAvailability("available") + // ServerTypesAvailabilityScarce is [insert doc]. + ServerTypesAvailabilityScarce = ServerTypesAvailability("scarce") + // ServerTypesAvailabilityShortage is [insert doc]. + 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 is [insert doc]. + SnapshotStateAvailable = SnapshotState("available") + // SnapshotStateSnapshotting is [insert doc]. + SnapshotStateSnapshotting = SnapshotState("snapshotting") + // SnapshotStateError is [insert doc]. + SnapshotStateError = SnapshotState("error") +) + +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 TaskStatus string + +const ( + // TaskStatusPending is [insert doc]. + TaskStatusPending = TaskStatus("pending") + // TaskStatusStarted is [insert doc]. + TaskStatusStarted = TaskStatus("started") + // TaskStatusSuccess is [insert doc]. + TaskStatusSuccess = TaskStatus("success") + // TaskStatusFailure is [insert doc]. + TaskStatusFailure = TaskStatus("failure") + // TaskStatusRetry is [insert doc]. + 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 VolumeState string + +const ( + // VolumeStateAvailable is [insert doc]. + VolumeStateAvailable = VolumeState("available") + // VolumeStateSnapshotting is [insert doc]. + VolumeStateSnapshotting = VolumeState("snapshotting") + // VolumeStateError is [insert doc]. + 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 is [insert doc]. + VolumeVolumeTypeLSSD = VolumeVolumeType("l_ssd") + // VolumeVolumeTypeBSSD is [insert doc]. + VolumeVolumeTypeBSSD = VolumeVolumeType("b_ssd") +) + +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 +} + +// Bootscript: bootscript +type Bootscript struct { + // Bootcmdargs: the bootscript arguments + Bootcmdargs string `json:"bootcmdargs"` + // Default: dispmay 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: the bootscript ID + ID string `json:"id"` + // Initrd: the initrd (initial ramdisk) configuration + Initrd string `json:"initrd"` + // Kernel: the server kernel version + Kernel string `json:"kernel"` + // Organization: the bootscript organization + Organization string `json:"organization"` + // Project: the bootscript project ID + Project string `json:"project"` + // Public: provide information if the bootscript is public + Public bool `json:"public"` + // Title: the bootscript title + Title string `json:"title"` + // Arch: the bootscript arch + // + // Default value: x86_64 + Arch Arch `json:"arch"` + // Zone: the zone in which is the bootscript + Zone scw.Zone `json:"zone"` +} + +type CreateIPResponse struct { + IP *IP `json:"ip"` + + Location string `json:"Location"` +} + +type CreateImageResponse struct { + Image *Image `json:"image"` + + Location string `json:"Location"` +} + +type CreatePlacementGroupResponse struct { + PlacementGroup *PlacementGroup `json:"placement_group"` +} + +type CreatePrivateNICResponse struct { + PrivateNic *PrivateNIC `json:"private_nic"` +} + +type CreateSecurityGroupResponse struct { + SecurityGroup *SecurityGroup `json:"security_group"` +} + +type CreateSecurityGroupRuleResponse struct { + Rule *SecurityGroupRule `json:"rule"` +} + +type CreateServerResponse struct { + Server *Server `json:"server"` +} + +type CreateSnapshotResponse struct { + Snapshot *Snapshot `json:"snapshot"` +} + +type CreateVolumeResponse struct { + Volume *Volume `json:"volume"` + + Location string `json:"Location"` +} + +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"` +} + +type GetBootscriptResponse struct { + Bootscript *Bootscript `json:"bootscript"` +} + +type GetDashboardResponse struct { + Dashboard *Dashboard `json:"dashboard"` +} + +type GetIPResponse struct { + IP *IP `json:"ip"` +} + +type GetImageResponse struct { + Image *Image `json:"image"` +} + +type GetPlacementGroupResponse struct { + PlacementGroup *PlacementGroup `json:"placement_group"` +} + +type GetPlacementGroupServersResponse struct { + Servers []*PlacementGroupServer `json:"servers"` +} + +type GetPrivateNICResponse struct { + PrivateNic *PrivateNIC `json:"private_nic"` +} + +type GetSecurityGroupResponse struct { + SecurityGroup *SecurityGroup `json:"security_group"` +} + +type GetSecurityGroupRuleResponse struct { + Rule *SecurityGroupRule `json:"rule"` +} + +type GetServerResponse struct { + Server *Server `json:"server"` +} + +type GetServerTypesAvailabilityResponse struct { + Servers map[string]*GetServerTypesAvailabilityResponseAvailability `json:"servers"` +} + +type GetServerTypesAvailabilityResponseAvailability struct { + // Availability: + // + // Default value: available + Availability ServerTypesAvailability `json:"availability"` +} + +type GetSnapshotResponse struct { + Snapshot *Snapshot `json:"snapshot"` +} + +type GetVolumeResponse struct { + Volume *Volume `json:"volume"` +} + +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"` + + Zone scw.Zone `json:"zone"` +} + +type Image struct { + ID string `json:"id"` + + Name string `json:"name"` + // Arch: + // + // Default value: x86_64 + Arch Arch `json:"arch"` + + CreationDate *time.Time `json:"creation_date"` + + ModificationDate *time.Time `json:"modification_date"` + + 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"` + + Zone scw.Zone `json:"zone"` +} + +type ListBootscriptsResponse struct { + Bootscripts []*Bootscript `json:"bootscripts"` + + TotalCount uint32 `json:"total_count"` +} + +type ListIPsResponse struct { + IPs []*IP `json:"ips"` + + TotalCount uint32 `json:"total_count"` +} + +type ListImagesResponse struct { + Images []*Image `json:"images"` + + TotalCount uint32 `json:"total_count"` +} + +type ListPlacementGroupsResponse struct { + PlacementGroups []*PlacementGroup `json:"placement_groups"` + + TotalCount uint32 `json:"total_count"` +} + +type ListPrivateNICsResponse struct { + PrivateNics []*PrivateNIC `json:"private_nics"` +} + +type ListSecurityGroupRulesResponse struct { + Rules []*SecurityGroupRule `json:"rules"` + + TotalCount uint32 `json:"total_count"` +} + +type ListSecurityGroupsResponse struct { + SecurityGroups []*SecurityGroup `json:"security_groups"` + + TotalCount uint32 `json:"total_count"` +} + +type ListServerActionsResponse struct { + Actions []ServerAction `json:"actions"` +} + +type ListServerUserDataResponse struct { + UserData []string `json:"user_data"` +} + +type ListServersResponse struct { + Servers []*Server `json:"servers"` + + TotalCount uint32 `json:"total_count"` +} + +type ListServersTypesResponse struct { + Servers map[string]*ServerType `json:"servers"` + + TotalCount uint32 `json:"total_count"` +} + +type ListSnapshotsResponse struct { + Snapshots []*Snapshot `json:"snapshots"` + + TotalCount uint32 `json:"total_count"` +} + +type ListVolumesResponse struct { + Volumes []*Volume `json:"volumes"` + + TotalCount uint32 `json:"total_count"` +} + +type ListVolumesTypesResponse struct { + Volumes map[string]*VolumeType `json:"volumes"` + + TotalCount uint32 `json:"total_count"` +} + +type NullableStringValue struct { + Null bool `json:"null,omitempty"` + + Value string `json:"value,omitempty"` +} + +// PlacementGroup: placement group +type PlacementGroup struct { + // ID: the placement group unique ID + ID string `json:"id"` + // Name: the placement group name + Name string `json:"name"` + // Organization: the placement group organization + Organization string `json:"organization"` + // Project: the placement group project ID + Project string `json:"project"` + // PolicyMode: select the failling 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: the zone in which is the placement group + Zone scw.Zone `json:"zone"` +} + +type PlacementGroupServer struct { + ID string `json:"id"` + + Name string `json:"name"` + + PolicyRespected bool `json:"policy_respected"` +} + +// PrivateNIC: private nic +type PrivateNIC struct { + // ID: the private NIC unique ID + ID string `json:"id,omitempty"` + // ServerID: the server the private NIC is attached to + ServerID string `json:"server_id,omitempty"` + // PrivateNetworkID: the private network where the private NIC is attached + PrivateNetworkID string `json:"private_network_id,omitempty"` + // MacAddress: the private NIC MAC address + MacAddress string `json:"mac_address,omitempty"` +} + +// SecurityGroup: security group +type SecurityGroup struct { + // ID: the security groups' unique ID + ID string `json:"id"` + // Name: the security groups name + Name string `json:"name"` + // Description: the security groups description + Description string `json:"description"` + // EnableDefaultSecurity: true if SMTP is blocked on IPv4 and IPv6 + EnableDefaultSecurity bool `json:"enable_default_security"` + // InboundDefaultPolicy: the default inbound policy + // + // Default value: accept + InboundDefaultPolicy SecurityGroupPolicy `json:"inbound_default_policy"` + // OutboundDefaultPolicy: the default outbound policy + // + // Default value: accept + OutboundDefaultPolicy SecurityGroupPolicy `json:"outbound_default_policy"` + // Organization: the security groups organization ID + Organization string `json:"organization"` + // Project: the project ID of the security group + Project string `json:"project"` + // OrganizationDefault: true if it is your default security group for this organization + OrganizationDefault bool `json:"organization_default"` + // ProjectDefault: true if it is your default security group for this project id + ProjectDefault bool `json:"project_default"` + // CreationDate: the security group creation date + CreationDate *time.Time `json:"creation_date"` + // ModificationDate: the security group modification date + ModificationDate *time.Time `json:"modification_date"` + // Servers: list of servers attached to this security group + Servers []*ServerSummary `json:"servers"` + // Stateful: true if the security group is stateful + Stateful bool `json:"stateful"` + // Zone: the zone in which is the security group + Zone scw.Zone `json:"zone"` +} + +type SecurityGroupRule struct { + ID string `json:"id"` + // Protocol: + // + // Default value: TCP + Protocol SecurityGroupRuleProtocol `json:"protocol"` + // Direction: + // + // Default value: inbound + Direction SecurityGroupRuleDirection `json:"direction"` + // Action: + // + // Default value: accept + 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 scw.Zone `json:"zone"` +} + +type SecurityGroupSummary struct { + ID string `json:"id"` + + Name string `json:"name"` +} + +type SecurityGroupTemplate struct { + ID string `json:"id,omitempty"` + + Name string `json:"name,omitempty"` +} + +// Server: server +type Server struct { + // ID: the server unique ID + ID string `json:"id"` + // Name: the server name + Name string `json:"name"` + // Organization: the server organization + Organization string `json:"organization"` + // Project: the server project ID + Project string `json:"project"` + // AllowedActions: provide as list of allowed actions on the server + AllowedActions []ServerAction `json:"allowed_actions"` + // Tags: the server associated tags + Tags []string `json:"tags"` + // CommercialType: the server commercial type (eg. GP1-M) + CommercialType string `json:"commercial_type"` + // CreationDate: the server creation date + CreationDate *time.Time `json:"creation_date"` + // DynamicIPRequired: true if a dynamic IP is required + DynamicIPRequired bool `json:"dynamic_ip_required"` + // EnableIPv6: true if IPv6 is enabled + EnableIPv6 bool `json:"enable_ipv6"` + // Hostname: the server host name + Hostname string `json:"hostname"` + // Image: provide information on the server image + Image *Image `json:"image"` + // Protected: the server protection option is activated + Protected bool `json:"protected"` + // PrivateIP: the server private IP address + PrivateIP *string `json:"private_ip"` + // PublicIP: information about the public IP + PublicIP *ServerIP `json:"public_ip"` + // ModificationDate: the server modification date + ModificationDate *time.Time `json:"modification_date"` + // State: the server state + // + // Default value: running + State ServerState `json:"state"` + // Location: the server location + Location *ServerLocation `json:"location"` + // IPv6: the server IPv6 address + IPv6 *ServerIPv6 `json:"ipv6"` + // Bootscript: the server bootscript + Bootscript *Bootscript `json:"bootscript"` + // BootType: the server boot type + // + // Default value: local + BootType BootType `json:"boot_type"` + // Volumes: the server volumes + Volumes map[string]*Volume `json:"volumes"` + // SecurityGroup: the server security group + SecurityGroup *SecurityGroupSummary `json:"security_group"` + // Maintenances: the server planned maintenances + Maintenances []*ServerMaintenance `json:"maintenances"` + // StateDetail: the server state_detail + StateDetail string `json:"state_detail"` + // Arch: the server arch + // + // Default value: x86_64 + Arch Arch `json:"arch"` + // PlacementGroup: the server placement group + PlacementGroup *PlacementGroup `json:"placement_group"` + // PrivateNics: the server private NICs + PrivateNics []*PrivateNIC `json:"private_nics"` + // Zone: the zone in which is the server + Zone scw.Zone `json:"zone"` +} + +type ServerActionResponse struct { + Task *Task `json:"task"` +} + +// ServerIP: server. ip +type ServerIP struct { + // ID: the unique ID of the IP address + ID string `json:"id"` + // Address: the server public IPv4 IP-Address + Address net.IP `json:"address"` + // Dynamic: true if the IP address is dynamic + Dynamic bool `json:"dynamic"` +} + +// ServerIPv6: server. ipv6 +type ServerIPv6 struct { + // Address: the server IPv6 IP-Address + Address net.IP `json:"address"` + // Gateway: the IPv6 IP-addresses gateway + Gateway net.IP `json:"gateway"` + // Netmask: the IPv6 IP-addresses CIDR netmask + Netmask string `json:"netmask"` +} + +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"` +} + +type ServerMaintenance struct { +} + +type ServerSummary struct { + ID string `json:"id"` + + Name string `json:"name"` +} + +type ServerType struct { + MonthlyPrice float32 `json:"monthly_price"` + + HourlyPrice float32 `json:"hourly_price"` + + AltNames []string `json:"alt_names"` + + PerVolumeConstraint *ServerTypeVolumeConstraintsByType `json:"per_volume_constraint"` + + VolumesConstraint *ServerTypeVolumeConstraintSizes `json:"volumes_constraint"` + + Ncpus uint32 `json:"ncpus"` + + Gpu *uint64 `json:"gpu"` + + RAM uint64 `json:"ram"` + // Arch: + // + // Default value: x86_64 + Arch Arch `json:"arch"` + + Baremetal bool `json:"baremetal"` + + Network *ServerTypeNetwork `json:"network"` +} + +type ServerTypeNetwork struct { + Interfaces []*ServerTypeNetworkInterface `json:"interfaces"` + + SumInternalBandwidth *uint64 `json:"sum_internal_bandwidth"` + + SumInternetBandwidth *uint64 `json:"sum_internet_bandwidth"` + + IPv6Support bool `json:"ipv6_support"` +} + +type ServerTypeNetworkInterface struct { + InternalBandwidth *uint64 `json:"internal_bandwidth"` + + InternetBandwidth *uint64 `json:"internet_bandwidth"` +} + +type ServerTypeVolumeConstraintSizes struct { + MinSize scw.Size `json:"min_size"` + + MaxSize scw.Size `json:"max_size"` +} + +type ServerTypeVolumeConstraintsByType struct { + LSSD *ServerTypeVolumeConstraintSizes `json:"l_ssd"` +} + +type SetPlacementGroupResponse struct { + PlacementGroup *PlacementGroup `json:"placement_group"` +} + +type SetPlacementGroupServersResponse struct { + Servers []*PlacementGroupServer `json:"servers"` +} + +type Snapshot struct { + 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"` + + CreationDate *time.Time `json:"creation_date"` + + ModificationDate *time.Time `json:"modification_date"` + + Project string `json:"project"` + + Zone scw.Zone `json:"zone"` +} + +type SnapshotBaseVolume struct { + ID string `json:"id"` + + Name string `json:"name"` +} + +// Task: task +type Task struct { + // ID: the unique ID of the task + ID string `json:"id"` + // Description: the description of the task + Description string `json:"description"` + // Progress: the progress of the task in percent + Progress int32 `json:"progress"` + // StartedAt: the task start date + StartedAt *time.Time `json:"started_at"` + // TerminatedAt: the task end date + TerminatedAt *time.Time `json:"terminated_at"` + // Status: the task status + // + // Default value: pending + Status TaskStatus `json:"status"` + + HrefFrom string `json:"href_from"` + + HrefResult string `json:"href_result"` + // Zone: the zone in which is the task + Zone scw.Zone `json:"zone"` +} + +type UpdateIPResponse struct { + IP *IP `json:"ip"` +} + +type UpdatePlacementGroupResponse struct { + PlacementGroup *PlacementGroup `json:"placement_group"` +} + +type UpdatePlacementGroupServersResponse struct { + Servers []*PlacementGroupServer `json:"servers"` +} + +type UpdateServerResponse struct { + Server *Server `json:"server"` +} + +type UpdateVolumeResponse struct { + Volume *Volume `json:"volume"` +} + +// Volume: volume +type Volume struct { + // ID: the volumes unique ID + ID string `json:"id"` + // Name: the volumes names + Name string `json:"name"` + // ExportURI: show the volumes NBD export URI + ExportURI string `json:"export_uri"` + // Size: the volumes disk size + Size scw.Size `json:"size"` + // VolumeType: the volumes type + // + // Default value: l_ssd + VolumeType VolumeVolumeType `json:"volume_type"` + // CreationDate: the volumes creation date + CreationDate *time.Time `json:"creation_date"` + // ModificationDate: the volumes modification date + ModificationDate *time.Time `json:"modification_date"` + // Organization: the volumes organization + Organization string `json:"organization"` + // Project: the volumes project ID + Project string `json:"project"` + // Server: the server attached to the volume + Server *ServerSummary `json:"server"` + // State: the volumes state + // + // Default value: available + State VolumeState `json:"state"` + // Zone: the zone in which is the volume + Zone scw.Zone `json:"zone"` +} + +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"` +} + +// 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 + Size scw.Size `json:"size,omitempty"` + // VolumeType: type of the volume + // + // Default value: l_ssd + VolumeType VolumeVolumeType `json:"volume_type,omitempty"` + // Organization: organization ID of the volume + Organization string `json:"organization,omitempty"` + // Project: project ID of the volume + Project string `json:"project,omitempty"` +} + +type VolumeType struct { + DisplayName string `json:"display_name"` + + Capabilities *VolumeTypeCapabilities `json:"capabilities"` + + Constraints *VolumeTypeConstraints `json:"constraints"` +} + +type VolumeTypeCapabilities struct { + Snapshot bool `json:"snapshot"` +} + +type VolumeTypeConstraints struct { + Min scw.Size `json:"min"` + + Max scw.Size `json:"max"` +} + +// setImageResponse: set image response +type setImageResponse struct { + Image *Image `json:"image"` +} + +// setSecurityGroupResponse: set security group response +type setSecurityGroupResponse struct { + SecurityGroup *SecurityGroup `json:"security_group"` +} + +// setSecurityGroupRuleResponse: set security group rule response +type setSecurityGroupRuleResponse struct { + Rule *SecurityGroupRule `json:"rule"` +} + +// setServerResponse: set server response +type setServerResponse struct { + Server *Server `json:"server"` +} + +// setSnapshotResponse: set snapshot response +type setSnapshotResponse struct { + Snapshot *Snapshot `json:"snapshot"` +} + +// Service API + +type GetServerTypesAvailabilityRequest struct { + Zone scw.Zone `json:"-"` + + PerPage *uint32 `json:"-"` + + Page *int32 `json:"-"` +} + +// GetServerTypesAvailability: get availability +// +// Get availibility for all server 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 + } + + defaultPerPage, exist := s.client.GetDefaultPageSize() + if (req.PerPage == nil || *req.PerPage == 0) && exist { + req.PerPage = &defaultPerPage + } + + 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, + Headers: http.Header{}, + } + + var resp GetServerTypesAvailabilityResponse + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +type ListServersTypesRequest struct { + Zone scw.Zone `json:"-"` + + PerPage *uint32 `json:"-"` + + Page *int32 `json:"-"` +} + +// ListServersTypes: list server types +// +// Get server types 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 + } + + defaultPerPage, exist := s.client.GetDefaultPageSize() + if (req.PerPage == nil || *req.PerPage == 0) && exist { + req.PerPage = &defaultPerPage + } + + 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, + Headers: http.Header{}, + } + + var resp ListServersTypesResponse + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +type ListVolumesTypesRequest struct { + Zone scw.Zone `json:"-"` + + PerPage *uint32 `json:"-"` + + Page *int32 `json:"-"` +} + +// ListVolumesTypes: list volumes types +// +// Get volumes 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 + } + + defaultPerPage, exist := s.client.GetDefaultPageSize() + if (req.PerPage == nil || *req.PerPage == 0) && exist { + req.PerPage = &defaultPerPage + } + + 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, + Headers: http.Header{}, + } + + var resp ListVolumesTypesResponse + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +type ListServersRequest struct { + Zone scw.Zone `json:"-"` + // PerPage: a positive integer lower or equal to 100 to select the number of items to return + // + // Default value: 50 + PerPage *uint32 `json:"-"` + // Page: a positive integer to choose the page to return + Page *int32 `json:"-"` + // Organization: list only servers of this organization + Organization *string `json:"-"` + // Project: list only servers of this project ID + Project *string `json:"-"` + // Name: filter servers by name (for eg. "server1" will return "server100" and "server1" but not "foo") + Name *string `json:"-"` + // PrivateIP: list servers by private_ip + PrivateIP *net.IP `json:"-"` + // WithoutIP: list servers that are not attached to a public IP + WithoutIP *bool `json:"-"` + // CommercialType: list servers of this commercial type + CommercialType *string `json:"-"` + // State: list servers in this state + // + // Default value: running + State *ServerState `json:"-"` + // Tags: list servers with these exact tags + Tags []string `json:"-"` +} + +// ListServers: list all servers +func (s *API) ListServers(req *ListServersRequest, opts ...scw.RequestOption) (*ListServersResponse, error) { + var err error + + if req.Zone == "" { + defaultZone, _ := s.client.GetDefaultZone() + req.Zone = defaultZone + } + + defaultPerPage, exist := s.client.GetDefaultPageSize() + if (req.PerPage == nil || *req.PerPage == 0) && exist { + req.PerPage = &defaultPerPage + } + + 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, ",")) + } + + 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, + Headers: http.Header{}, + } + + var resp ListServersResponse + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +// 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 +} + +type CreateServerRequest struct { + Zone scw.Zone `json:"-"` + // Name: the server name + Name string `json:"name,omitempty"` + // DynamicIPRequired: define if a dynamic IP is required for the instance + DynamicIPRequired *bool `json:"dynamic_ip_required,omitempty"` + // CommercialType: define the server commercial type (i.e. GP1-S) + CommercialType string `json:"commercial_type,omitempty"` + // Image: the server image ID or label + Image string `json:"image,omitempty"` + // Volumes: the volumes attached to the server + Volumes map[string]*VolumeTemplate `json:"volumes,omitempty"` + // EnableIPv6: true if IPv6 is enabled on the server + EnableIPv6 bool `json:"enable_ipv6,omitempty"` + // PublicIP: the ID of the reserved IP to attach to the server + PublicIP *string `json:"public_ip,omitempty"` + // BootType: the boot type to use + // + // Default value: local + BootType *BootType `json:"boot_type,omitempty"` + // Bootscript: the bootscript ID to use when `boot_type` is set to `bootscript` + Bootscript *string `json:"bootscript,omitempty"` + // Organization: the server organization ID + // Precisely one of Organization, Project must be set. + Organization *string `json:"organization,omitempty"` + // Project: the server project ID + // Precisely one of Organization, Project must be set. + Project *string `json:"project,omitempty"` + // Tags: the server tags + Tags []string `json:"tags,omitempty"` + // SecurityGroup: the security group ID + SecurityGroup *string `json:"security_group,omitempty"` + // PlacementGroup: placement group ID if server must be part of a placement group + PlacementGroup *string `json:"placement_group,omitempty"` + // PrivateNetwork: private Network IDs if the server need to be part of one or more Private Networks + PrivateNetwork []string `json:"private_network,omitempty"` +} + +// createServer: create a server +func (s *API) createServer(req *CreateServerRequest, opts ...scw.RequestOption) (*CreateServerResponse, error) { + var err error + + defaultProject, exist := s.client.GetDefaultProjectID() + if exist && req.Organization == nil && req.Project == nil { + req.Project = &defaultProject + } + + defaultOrganization, exist := s.client.GetDefaultOrganizationID() + if exist && req.Organization == nil && req.Project == nil { + req.Organization = &defaultOrganization + } + + if req.Zone == "" { + defaultZone, _ := s.client.GetDefaultZone() + req.Zone = defaultZone + } + + 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", + Headers: http.Header{}, + } + + 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 +} + +type DeleteServerRequest struct { + Zone scw.Zone `json:"-"` + + ServerID string `json:"-"` +} + +// DeleteServer: delete a server +// +// Delete a server with the given 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) + "", + Headers: http.Header{}, + } + + err = s.client.Do(scwReq, nil, opts...) + if err != nil { + return err + } + return nil +} + +type GetServerRequest struct { + Zone scw.Zone `json:"-"` + // ServerID: UUID of the server you want to get + ServerID string `json:"-"` +} + +// GetServer: get a server +// +// Get the details of a specified Server. +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) + "", + Headers: http.Header{}, + } + + var resp GetServerResponse + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +type setServerRequest struct { + Zone scw.Zone `json:"-"` + // ID: the server unique ID + ID string `json:"-"` + // Name: the server name + Name string `json:"name"` + // Organization: the server organization + Organization string `json:"organization"` + // Project: the server project ID + Project string `json:"project"` + // AllowedActions: provide as list of allowed actions on the server + AllowedActions []ServerAction `json:"allowed_actions"` + // Tags: the server associated tags + Tags []string `json:"tags"` + // CommercialType: the server commercial type (eg. GP1-M) + CommercialType string `json:"commercial_type"` + // CreationDate: the server creation date + CreationDate *time.Time `json:"creation_date"` + // DynamicIPRequired: true if a dynamic IP is required + DynamicIPRequired bool `json:"dynamic_ip_required"` + // EnableIPv6: true if IPv6 is enabled + EnableIPv6 bool `json:"enable_ipv6"` + // Hostname: the server host name + Hostname string `json:"hostname"` + // Image: provide information on the server image + Image *Image `json:"image"` + // Protected: the server protection option is activated + Protected bool `json:"protected"` + // PrivateIP: the server private IP address + PrivateIP *string `json:"private_ip"` + // PublicIP: information about the public IP + PublicIP *ServerIP `json:"public_ip"` + // ModificationDate: the server modification date + ModificationDate *time.Time `json:"modification_date"` + // State: the server state + // + // Default value: running + State ServerState `json:"state"` + // Location: the server location + Location *ServerLocation `json:"location"` + // IPv6: the server IPv6 address + IPv6 *ServerIPv6 `json:"ipv6"` + // Bootscript: the server bootscript + Bootscript *Bootscript `json:"bootscript"` + // BootType: the server boot type + // + // Default value: local + BootType BootType `json:"boot_type"` + // Volumes: the server volumes + Volumes map[string]*Volume `json:"volumes"` + // SecurityGroup: the server security group + SecurityGroup *SecurityGroupSummary `json:"security_group"` + // Maintenances: the server planned maintenances + Maintenances []*ServerMaintenance `json:"maintenances"` + // StateDetail: the server state_detail + StateDetail string `json:"state_detail"` + // Arch: the server arch + // + // Default value: x86_64 + Arch Arch `json:"arch"` + // PlacementGroup: the server placement group + PlacementGroup *PlacementGroup `json:"placement_group"` + // PrivateNics: the server private NICs + PrivateNics []*PrivateNIC `json:"private_nics"` +} + +func (s *API) setServer(req *setServerRequest, opts ...scw.RequestOption) (*setServerResponse, error) { + var err error + + if req.Project == "" { + defaultProject, _ := s.client.GetDefaultProjectID() + req.Project = defaultProject + } + + if req.Organization == "" { + defaultOrganization, _ := s.client.GetDefaultOrganizationID() + req.Organization = defaultOrganization + } + + 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.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) + "", + Headers: http.Header{}, + } + + 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 +} + +type UpdateServerRequest struct { + Zone scw.Zone `json:"-"` + // ServerID: UUID of the server + ServerID string `json:"-"` + // Name: name of the server + Name *string `json:"name,omitempty"` + // BootType: + // + // Default value: local + BootType *BootType `json:"boot_type,omitempty"` + // Tags: tags of the server + Tags *[]string `json:"tags,omitempty"` + + Volumes *map[string]*VolumeTemplate `json:"volumes,omitempty"` + + Bootscript *string `json:"bootscript,omitempty"` + + DynamicIPRequired *bool `json:"dynamic_ip_required,omitempty"` + + EnableIPv6 *bool `json:"enable_ipv6,omitempty"` + + Protected *bool `json:"protected,omitempty"` + + SecurityGroup *SecurityGroupTemplate `json:"security_group,omitempty"` + // PlacementGroup: placement group ID if server must be part of a placement group + PlacementGroup *NullableStringValue `json:"placement_group,omitempty"` + // PrivateNics: the server private NICs + PrivateNics []*PrivateNIC `json:"private_nics,omitempty"` +} + +// updateServer: update a server +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) + "", + Headers: http.Header{}, + } + + 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 +} + +type ListServerActionsRequest struct { + Zone scw.Zone `json:"-"` + + ServerID string `json:"-"` +} + +// ListServerActions: list server actions +// +// Liste all actions that can currently be performed on a server. +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", + Headers: http.Header{}, + } + + var resp ListServerActionsResponse + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +type ServerActionRequest struct { + Zone scw.Zone `json:"-"` + // ServerID: UUID of the server + ServerID string `json:"-"` + // Action: the action to perform on the server + // + // Default value: poweron + Action ServerAction `json:"action"` + // Name: the name of the backup you want to create + // + // The name of the backup you want to create. + // This field should only be specified when performing a backup action. + // + Name *string `json:"name,omitempty"` +} + +// ServerAction: perform action +// +// Perform power related actions on a server. Be wary that when terminating a server, all the attached volumes (local *and* block storage) are deleted. So, if you want to keep your local volumes, you must use the `archive` action instead of `terminate`. And if you want to keep block-storage volumes, **you must** detach it beforehand you issue the `terminate` call. For more information, read the [Volumes](#volumes-7e8a39) 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", + Headers: http.Header{}, + } + + 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 +} + +type ListServerUserDataRequest struct { + Zone scw.Zone `json:"-"` + // ServerID: UUID of the server + ServerID string `json:"-"` +} + +// ListServerUserData: list user data +// +// List all user data keys registered on a given server. +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", + Headers: http.Header{}, + } + + var resp ListServerUserDataResponse + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +type DeleteServerUserDataRequest struct { + Zone scw.Zone `json:"-"` + // ServerID: UUID of the server + ServerID string `json:"-"` + // Key: key of the user data to delete + Key string `json:"-"` +} + +// DeleteServerUserData: delete user data +// +// Delete the given key from a server 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) + "", + Headers: http.Header{}, + } + + err = s.client.Do(scwReq, nil, opts...) + if err != nil { + return err + } + return nil +} + +type ListImagesRequest struct { + 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:"-"` +} + +// ListImages: list instance images +// +// List all images available in an account. +func (s *API) ListImages(req *ListImagesRequest, opts ...scw.RequestOption) (*ListImagesResponse, error) { + var err error + + if req.Zone == "" { + defaultZone, _ := s.client.GetDefaultZone() + req.Zone = defaultZone + } + + defaultPerPage, exist := s.client.GetDefaultPageSize() + if (req.PerPage == nil || *req.PerPage == 0) && exist { + req.PerPage = &defaultPerPage + } + + 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) + + 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, + Headers: http.Header{}, + } + + var resp ListImagesResponse + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +// 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 +} + +type GetImageRequest struct { + Zone scw.Zone `json:"-"` + // ImageID: UUID of the image you want to get + ImageID string `json:"-"` +} + +// GetImage: get an instance image +// +// Get details of an image with the given 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) + "", + Headers: http.Header{}, + } + + var resp GetImageResponse + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +type CreateImageRequest struct { + 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: x86_64 + Arch Arch `json:"arch"` + // 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"` + // Organization: organization ID of the image + // Precisely one of Organization, Project must be set. + Organization *string `json:"organization,omitempty"` + // Project: project ID of the image + // Precisely one of Organization, Project must be set. + Project *string `json:"project,omitempty"` + // Public: true to create a public image + Public bool `json:"public,omitempty"` +} + +// CreateImage: create an instance image +func (s *API) CreateImage(req *CreateImageRequest, opts ...scw.RequestOption) (*CreateImageResponse, error) { + var err error + + defaultProject, exist := s.client.GetDefaultProjectID() + if exist && req.Organization == nil && req.Project == nil { + req.Project = &defaultProject + } + + defaultOrganization, exist := s.client.GetDefaultOrganizationID() + if exist && req.Organization == nil && req.Project == nil { + req.Organization = &defaultOrganization + } + + if req.Zone == "" { + defaultZone, _ := s.client.GetDefaultZone() + req.Zone = defaultZone + } + + 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", + Headers: http.Header{}, + } + + 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 +} + +type SetImageRequest struct { + Zone scw.Zone `json:"-"` + + ID string `json:"-"` + + Name string `json:"name"` + // Arch: + // + // Default value: x86_64 + Arch Arch `json:"arch"` + + CreationDate *time.Time `json:"creation_date"` + + ModificationDate *time.Time `json:"modification_date"` + + 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"` +} + +// setImage: update image +// +// Replace all image properties with an image message. +func (s *API) setImage(req *SetImageRequest, opts ...scw.RequestOption) (*setImageResponse, error) { + var err error + + if req.Project == "" { + defaultProject, _ := s.client.GetDefaultProjectID() + req.Project = defaultProject + } + + if req.Organization == "" { + defaultOrganization, _ := s.client.GetDefaultOrganizationID() + req.Organization = defaultOrganization + } + + 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.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) + "", + Headers: http.Header{}, + } + + 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 +} + +type DeleteImageRequest struct { + Zone scw.Zone `json:"-"` + // ImageID: UUID of the image you want to delete + ImageID string `json:"-"` +} + +// DeleteImage: delete an instance image +// +// Delete the image with the given 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) + "", + Headers: http.Header{}, + } + + err = s.client.Do(scwReq, nil, opts...) + if err != nil { + return err + } + return nil +} + +type ListSnapshotsRequest struct { + Zone scw.Zone `json:"-"` + + Organization *string `json:"-"` + + PerPage *uint32 `json:"-"` + + Page *int32 `json:"-"` + + Name *string `json:"-"` + + Project *string `json:"-"` +} + +// ListSnapshots: list snapshots +func (s *API) ListSnapshots(req *ListSnapshotsRequest, opts ...scw.RequestOption) (*ListSnapshotsResponse, error) { + var err error + + if req.Zone == "" { + defaultZone, _ := s.client.GetDefaultZone() + req.Zone = defaultZone + } + + defaultPerPage, exist := s.client.GetDefaultPageSize() + if (req.PerPage == nil || *req.PerPage == 0) && exist { + req.PerPage = &defaultPerPage + } + + 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, "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) + "/snapshots", + Query: query, + Headers: http.Header{}, + } + + var resp ListSnapshotsResponse + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +// 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 +} + +type CreateSnapshotRequest struct { + 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"` + + // Precisely one of Organization, Project must be set. + Organization *string `json:"organization,omitempty"` + + // Precisely one of Organization, Project must be set. + Project *string `json:"project,omitempty"` +} + +// CreateSnapshot: create a snapshot from a given volume +func (s *API) CreateSnapshot(req *CreateSnapshotRequest, opts ...scw.RequestOption) (*CreateSnapshotResponse, error) { + var err error + + defaultProject, exist := s.client.GetDefaultProjectID() + if exist && req.Organization == nil && req.Project == nil { + req.Project = &defaultProject + } + + defaultOrganization, exist := s.client.GetDefaultOrganizationID() + if exist && req.Organization == nil && req.Project == nil { + req.Organization = &defaultOrganization + } + + if req.Zone == "" { + defaultZone, _ := s.client.GetDefaultZone() + req.Zone = defaultZone + } + + 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", + Headers: http.Header{}, + } + + 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 +} + +type GetSnapshotRequest struct { + Zone scw.Zone `json:"-"` + // SnapshotID: UUID of the snapshot you want to get + SnapshotID string `json:"-"` +} + +// GetSnapshot: get a snapshot +// +// Get details of a snapshot with the given 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) + "", + Headers: http.Header{}, + } + + var resp GetSnapshotResponse + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +type SetSnapshotRequest struct { + Zone scw.Zone `json:"-"` + + ID string `json:"-"` + + 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"` + + CreationDate *time.Time `json:"creation_date"` + + ModificationDate *time.Time `json:"modification_date"` + + Project string `json:"project"` +} + +// setSnapshot: update snapshot +// +// Replace all snapshot properties with a snapshot message. +func (s *API) setSnapshot(req *SetSnapshotRequest, opts ...scw.RequestOption) (*setSnapshotResponse, error) { + var err error + + if req.Project == "" { + defaultProject, _ := s.client.GetDefaultProjectID() + req.Project = defaultProject + } + + if req.Organization == "" { + defaultOrganization, _ := s.client.GetDefaultOrganizationID() + req.Organization = defaultOrganization + } + + 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.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) + "/snapshots/" + fmt.Sprint(req.ID) + "", + Headers: http.Header{}, + } + + 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 +} + +type DeleteSnapshotRequest struct { + Zone scw.Zone `json:"-"` + // SnapshotID: UUID of the snapshot you want to delete + SnapshotID string `json:"-"` +} + +// DeleteSnapshot: delete a snapshot +// +// Delete the snapshot with the given 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) + "", + Headers: http.Header{}, + } + + err = s.client.Do(scwReq, nil, opts...) + if err != nil { + return err + } + return nil +} + +type ListVolumesRequest struct { + 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 + // + // Default value: 50 + PerPage *uint32 `json:"-"` + // Page: a positive integer to choose the page to return + Page *int32 `json:"-"` + // Organization: filter volume by organization + Organization *string `json:"-"` + // Project: filter volume by project ID + Project *string `json:"-"` + // Name: filter volume by name (for eg. "vol" will return "myvolume" but not "data") + Name *string `json:"-"` +} + +// ListVolumes: list volumes +func (s *API) ListVolumes(req *ListVolumesRequest, opts ...scw.RequestOption) (*ListVolumesResponse, error) { + var err error + + if req.Zone == "" { + defaultZone, _ := s.client.GetDefaultZone() + req.Zone = defaultZone + } + + defaultPerPage, exist := s.client.GetDefaultPageSize() + if (req.PerPage == nil || *req.PerPage == 0) && exist { + req.PerPage = &defaultPerPage + } + + 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) + 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, + Headers: http.Header{}, + } + + var resp ListVolumesResponse + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +// 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 +} + +type CreateVolumeRequest struct { + Zone scw.Zone `json:"-"` + + Name string `json:"name,omitempty"` + + // Precisely one of Organization, Project must be set. + Organization *string `json:"organization,omitempty"` + // VolumeType: + // + // Default value: l_ssd + VolumeType VolumeVolumeType `json:"volume_type"` + + // Precisely one of BaseSnapshot, BaseVolume, Size must be set. + Size *scw.Size `json:"size,omitempty"` + + // Precisely one of BaseSnapshot, BaseVolume, Size must be set. + BaseVolume *string `json:"base_volume,omitempty"` + + // Precisely one of BaseSnapshot, BaseVolume, Size must be set. + BaseSnapshot *string `json:"base_snapshot,omitempty"` + + // Precisely one of Organization, Project must be set. + Project *string `json:"project,omitempty"` +} + +// CreateVolume: create a volume +func (s *API) CreateVolume(req *CreateVolumeRequest, opts ...scw.RequestOption) (*CreateVolumeResponse, error) { + var err error + + defaultProject, exist := s.client.GetDefaultProjectID() + if exist && req.Organization == nil && req.Project == nil { + req.Project = &defaultProject + } + + defaultOrganization, exist := s.client.GetDefaultOrganizationID() + if exist && req.Organization == nil && req.Project == nil { + req.Organization = &defaultOrganization + } + + 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) + "/volumes", + Headers: http.Header{}, + } + + 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 +} + +type GetVolumeRequest struct { + Zone scw.Zone `json:"-"` + // VolumeID: UUID of the volume you want to get + VolumeID string `json:"-"` +} + +// GetVolume: get a volume +// +// Get details of a volume with the given 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) + "", + Headers: http.Header{}, + } + + var resp GetVolumeResponse + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +type UpdateVolumeRequest struct { + Zone scw.Zone `json:"-"` + // VolumeID: UUID of the volume + VolumeID string `json:"-"` + // Name: the volume name + Name *string `json:"name,omitempty"` + // Size: the volume disk size + Size *scw.Size `json:"size,omitempty"` +} + +// UpdateVolume: update a volume +// +// Replace name and/or size properties of given ID volume with the given value(s). Any volume name can be changed while, for now, only `b_ssd` volume growing is supported. +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) + "", + Headers: http.Header{}, + } + + 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 +} + +type DeleteVolumeRequest struct { + Zone scw.Zone `json:"-"` + // VolumeID: UUID of the volume you want to delete + VolumeID string `json:"-"` +} + +// DeleteVolume: delete a volume +// +// Delete the volume with the given 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) + "", + Headers: http.Header{}, + } + + err = s.client.Do(scwReq, nil, opts...) + if err != nil { + return err + } + return nil +} + +type ListSecurityGroupsRequest struct { + Zone scw.Zone `json:"-"` + // Name: name of the security group + Name *string `json:"-"` + // Organization: the security group organization ID + Organization *string `json:"-"` + // Project: the security group project ID + Project *string `json:"-"` + // PerPage: a positive integer lower or equal to 100 to select the number of items to return + // + // Default value: 50 + PerPage *uint32 `json:"-"` + // Page: a positive integer to choose the page to return + Page *int32 `json:"-"` +} + +// ListSecurityGroups: list security groups +// +// List all security groups available in an account. +func (s *API) ListSecurityGroups(req *ListSecurityGroupsRequest, opts ...scw.RequestOption) (*ListSecurityGroupsResponse, error) { + var err error + + if req.Zone == "" { + defaultZone, _ := s.client.GetDefaultZone() + req.Zone = defaultZone + } + + defaultPerPage, exist := s.client.GetDefaultPageSize() + if (req.PerPage == nil || *req.PerPage == 0) && exist { + req.PerPage = &defaultPerPage + } + + query := url.Values{} + parameter.AddToQuery(query, "name", req.Name) + 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) + + 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, + Headers: http.Header{}, + } + + var resp ListSecurityGroupsResponse + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +// 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 +} + +type CreateSecurityGroupRequest struct { + 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"` + // Organization: organization the security group belongs to + // Precisely one of Organization, Project must be set. + Organization *string `json:"organization,omitempty"` + // Project: project ID the security group belong to + // Precisely one of Organization, Project must be set. + Project *string `json:"project,omitempty"` + // OrganizationDefault: whether this security group becomes the default security group for new instances + // + // Default value: false + // 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 + // + // Default value: false + // Precisely one of OrganizationDefault, ProjectDefault must be set. + ProjectDefault *bool `json:"project_default,omitempty"` + // Stateful: whether the security group is stateful or not + // + // Default value: false + Stateful bool `json:"stateful,omitempty"` + // InboundDefaultPolicy: default policy for inbound rules + // + // Default value: accept + InboundDefaultPolicy SecurityGroupPolicy `json:"inbound_default_policy"` + // OutboundDefaultPolicy: default policy for outbound rules + // + // Default value: accept + OutboundDefaultPolicy SecurityGroupPolicy `json:"outbound_default_policy"` +} + +// CreateSecurityGroup: create a security group +func (s *API) CreateSecurityGroup(req *CreateSecurityGroupRequest, opts ...scw.RequestOption) (*CreateSecurityGroupResponse, error) { + var err error + + defaultProject, exist := s.client.GetDefaultProjectID() + if exist && req.Organization == nil && req.Project == nil { + req.Project = &defaultProject + } + + defaultOrganization, exist := s.client.GetDefaultOrganizationID() + if exist && req.Organization == nil && req.Project == nil { + req.Organization = &defaultOrganization + } + + if req.Zone == "" { + defaultZone, _ := s.client.GetDefaultZone() + req.Zone = defaultZone + } + + 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", + Headers: http.Header{}, + } + + 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 +} + +type GetSecurityGroupRequest struct { + Zone scw.Zone `json:"-"` + // SecurityGroupID: UUID of the security group you want to get + SecurityGroupID string `json:"-"` +} + +// GetSecurityGroup: get a security group +// +// Get the details of a Security Group with the given 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) + "", + Headers: http.Header{}, + } + + var resp GetSecurityGroupResponse + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +type DeleteSecurityGroupRequest struct { + Zone scw.Zone `json:"-"` + // SecurityGroupID: UUID of the security group you want to delete + SecurityGroupID string `json:"-"` +} + +// DeleteSecurityGroup: delete a security group +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) + "", + Headers: http.Header{}, + } + + err = s.client.Do(scwReq, nil, opts...) + if err != nil { + return err + } + return nil +} + +type setSecurityGroupRequest struct { + Zone scw.Zone `json:"-"` + + ID string `json:"-"` + + Name string `json:"name"` + + CreationDate *time.Time `json:"creation_date"` + + ModificationDate *time.Time `json:"modification_date"` + + Description string `json:"description"` + + EnableDefaultSecurity bool `json:"enable_default_security"` + // InboundDefaultPolicy: + // + // Default value: accept + InboundDefaultPolicy SecurityGroupPolicy `json:"inbound_default_policy"` + + Organization string `json:"organization"` + + OrganizationDefault bool `json:"organization_default"` + // OutboundDefaultPolicy: + // + // Default value: accept + OutboundDefaultPolicy SecurityGroupPolicy `json:"outbound_default_policy"` + + Servers []*ServerSummary `json:"servers"` + + Stateful bool `json:"stateful"` + + Project string `json:"project"` + + ProjectDefault bool `json:"project_default"` +} + +// setSecurityGroup: update a security group +// +// 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.Project == "" { + defaultProject, _ := s.client.GetDefaultProjectID() + req.Project = defaultProject + } + + if req.Organization == "" { + defaultOrganization, _ := s.client.GetDefaultOrganizationID() + req.Organization = defaultOrganization + } + + 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.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) + "", + Headers: http.Header{}, + } + + 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 +} + +type ListSecurityGroupRulesRequest struct { + 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 + // + // Default value: 50 + PerPage *uint32 `json:"-"` + // Page: a positive integer to choose the page to return + Page *int32 `json:"-"` +} + +// ListSecurityGroupRules: list rules +func (s *API) ListSecurityGroupRules(req *ListSecurityGroupRulesRequest, opts ...scw.RequestOption) (*ListSecurityGroupRulesResponse, error) { + var err error + + if req.Zone == "" { + defaultZone, _ := s.client.GetDefaultZone() + req.Zone = defaultZone + } + + defaultPerPage, exist := s.client.GetDefaultPageSize() + if (req.PerPage == nil || *req.PerPage == 0) && exist { + req.PerPage = &defaultPerPage + } + + 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, + Headers: http.Header{}, + } + + var resp ListSecurityGroupRulesResponse + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +// 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 +} + +type CreateSecurityGroupRuleRequest struct { + Zone scw.Zone `json:"-"` + // SecurityGroupID: UUID of the security group + SecurityGroupID string `json:"-"` + // Protocol: + // + // Default value: TCP + Protocol SecurityGroupRuleProtocol `json:"protocol"` + // Direction: + // + // Default value: inbound + Direction SecurityGroupRuleDirection `json:"direction"` + // Action: + // + // Default value: accept + Action SecurityGroupRuleAction `json:"action"` + + IPRange scw.IPNet `json:"ip_range,omitempty"` + + DestPortFrom *uint32 `json:"dest_port_from,omitempty"` + + DestPortTo *uint32 `json:"dest_port_to,omitempty"` + + Position uint32 `json:"position,omitempty"` + + Editable bool `json:"editable,omitempty"` +} + +// CreateSecurityGroupRule: create rule +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", + Headers: http.Header{}, + } + + 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 +} + +type DeleteSecurityGroupRuleRequest struct { + Zone scw.Zone `json:"-"` + + SecurityGroupID string `json:"-"` + + SecurityGroupRuleID string `json:"-"` +} + +// DeleteSecurityGroupRule: delete rule +// +// Delete a security group rule with the given 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) + "", + Headers: http.Header{}, + } + + err = s.client.Do(scwReq, nil, opts...) + if err != nil { + return err + } + return nil +} + +type GetSecurityGroupRuleRequest struct { + Zone scw.Zone `json:"-"` + + SecurityGroupID string `json:"-"` + + SecurityGroupRuleID string `json:"-"` +} + +// GetSecurityGroupRule: get rule +// +// Get details of a security group rule with the given 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) + "", + Headers: http.Header{}, + } + + var resp GetSecurityGroupRuleResponse + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +type setSecurityGroupRuleRequest struct { + Zone scw.Zone `json:"-"` + + SecurityGroupID string `json:"-"` + + SecurityGroupRuleID string `json:"-"` + + ID string `json:"id"` + // Protocol: + // + // Default value: TCP + Protocol SecurityGroupRuleProtocol `json:"protocol"` + // Direction: + // + // Default value: inbound + Direction SecurityGroupRuleDirection `json:"direction"` + // Action: + // + // Default value: accept + 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"` +} + +// setSecurityGroupRule: update security group rule +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) + "", + Headers: http.Header{}, + } + + 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 +} + +type ListPlacementGroupsRequest struct { + Zone scw.Zone `json:"-"` + // PerPage: a positive integer lower or equal to 100 to select the number of items to return + // + // Default value: 50 + PerPage *uint32 `json:"-"` + // Page: a positive integer to choose the page to return + Page *int32 `json:"-"` + // Organization: list only placement groups of this organization + Organization *string `json:"-"` + // Project: list only placement groups of this project ID + Project *string `json:"-"` + // Name: filter placement groups by name (for eg. "cluster1" will return "cluster100" and "cluster1" but not "foo") + Name *string `json:"-"` +} + +// ListPlacementGroups: list placement groups +// +// List all placement groups. +func (s *API) ListPlacementGroups(req *ListPlacementGroupsRequest, opts ...scw.RequestOption) (*ListPlacementGroupsResponse, error) { + var err error + + if req.Zone == "" { + defaultZone, _ := s.client.GetDefaultZone() + req.Zone = defaultZone + } + + defaultPerPage, exist := s.client.GetDefaultPageSize() + if (req.PerPage == nil || *req.PerPage == 0) && exist { + req.PerPage = &defaultPerPage + } + + 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) + + 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, + Headers: http.Header{}, + } + + var resp ListPlacementGroupsResponse + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +// 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 +} + +type CreatePlacementGroupRequest struct { + Zone scw.Zone `json:"-"` + // Name: name of the placement group + Name string `json:"name,omitempty"` + + // Precisely one of Organization, Project must be set. + Organization *string `json:"organization,omitempty"` + + // Precisely one of Organization, Project must be set. + Project *string `json:"project,omitempty"` + // PolicyMode: + // + // Default value: optional + PolicyMode PlacementGroupPolicyMode `json:"policy_mode"` + // PolicyType: + // + // Default value: max_availability + PolicyType PlacementGroupPolicyType `json:"policy_type"` +} + +// CreatePlacementGroup: create a placement group +// +// Create a new placement group. +func (s *API) CreatePlacementGroup(req *CreatePlacementGroupRequest, opts ...scw.RequestOption) (*CreatePlacementGroupResponse, error) { + var err error + + defaultProject, exist := s.client.GetDefaultProjectID() + if exist && req.Organization == nil && req.Project == nil { + req.Project = &defaultProject + } + + defaultOrganization, exist := s.client.GetDefaultOrganizationID() + if exist && req.Organization == nil && req.Project == nil { + req.Organization = &defaultOrganization + } + + if req.Zone == "" { + defaultZone, _ := s.client.GetDefaultZone() + req.Zone = defaultZone + } + + 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", + Headers: http.Header{}, + } + + 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 +} + +type GetPlacementGroupRequest struct { + Zone scw.Zone `json:"-"` + // PlacementGroupID: UUID of the placement group you want to get + PlacementGroupID string `json:"-"` +} + +// GetPlacementGroup: get a placement group +// +// Get the given 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) + "", + Headers: http.Header{}, + } + + var resp GetPlacementGroupResponse + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +type SetPlacementGroupRequest struct { + 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"` +} + +// SetPlacementGroup: set placement group +// +// Set all parameters of the given placement group. +func (s *API) SetPlacementGroup(req *SetPlacementGroupRequest, opts ...scw.RequestOption) (*SetPlacementGroupResponse, error) { + var err error + + if req.Project == "" { + defaultProject, _ := s.client.GetDefaultProjectID() + req.Project = defaultProject + } + + if req.Organization == "" { + defaultOrganization, _ := s.client.GetDefaultOrganizationID() + req.Organization = defaultOrganization + } + + 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) + "", + Headers: http.Header{}, + } + + 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 +} + +type UpdatePlacementGroupRequest struct { + Zone scw.Zone `json:"-"` + // PlacementGroupID: UUID of the placement group + PlacementGroupID string `json:"-"` + // Name: name of the placement group + Name *string `json:"name,omitempty"` + // PolicyMode: + // + // Default value: optional + PolicyMode PlacementGroupPolicyMode `json:"policy_mode"` + // PolicyType: + // + // Default value: max_availability + PolicyType PlacementGroupPolicyType `json:"policy_type"` +} + +// UpdatePlacementGroup: update a placement group +// +// Update one or more parameter of the given 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) + "", + Headers: http.Header{}, + } + + 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 +} + +type DeletePlacementGroupRequest struct { + Zone scw.Zone `json:"-"` + // PlacementGroupID: UUID of the placement group you want to delete + PlacementGroupID string `json:"-"` +} + +// DeletePlacementGroup: delete the given placement group +// +// Delete the given 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) + "", + Headers: http.Header{}, + } + + err = s.client.Do(scwReq, nil, opts...) + if err != nil { + return err + } + return nil +} + +type GetPlacementGroupServersRequest struct { + Zone scw.Zone `json:"-"` + + PlacementGroupID string `json:"-"` +} + +// GetPlacementGroupServers: get placement group servers +// +// Get all servers belonging to the given 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", + Headers: http.Header{}, + } + + var resp GetPlacementGroupServersResponse + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +type SetPlacementGroupServersRequest struct { + Zone scw.Zone `json:"-"` + + PlacementGroupID string `json:"-"` + + Servers []string `json:"servers"` +} + +// SetPlacementGroupServers: set placement group servers +// +// Set all servers belonging to the given 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", + Headers: http.Header{}, + } + + 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 +} + +type UpdatePlacementGroupServersRequest struct { + Zone scw.Zone `json:"-"` + // PlacementGroupID: UUID of the placement group + PlacementGroupID string `json:"-"` + + Servers []string `json:"servers,omitempty"` +} + +// UpdatePlacementGroupServers: update placement group servers +// +// Update all servers belonging to the given 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", + Headers: http.Header{}, + } + + 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 +} + +type ListIPsRequest struct { + Zone scw.Zone `json:"-"` + // Project: the project ID the IPs are reserved in + Project *string `json:"-"` + // Organization: the organization ID the IPs are reserved in + Organization *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 + // + // Default value: 50 + PerPage *uint32 `json:"-"` + // Page: a positive integer to choose the page to return + Page *int32 `json:"-"` +} + +// ListIPs: list all flexible IPs +func (s *API) ListIPs(req *ListIPsRequest, opts ...scw.RequestOption) (*ListIPsResponse, error) { + var err error + + if req.Zone == "" { + defaultZone, _ := s.client.GetDefaultZone() + req.Zone = defaultZone + } + + defaultPerPage, exist := s.client.GetDefaultPageSize() + if (req.PerPage == nil || *req.PerPage == 0) && exist { + req.PerPage = &defaultPerPage + } + + query := url.Values{} + parameter.AddToQuery(query, "project", req.Project) + parameter.AddToQuery(query, "organization", req.Organization) + parameter.AddToQuery(query, "name", req.Name) + 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) + "/ips", + Query: query, + Headers: http.Header{}, + } + + var resp ListIPsResponse + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +// 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 +} + +type CreateIPRequest struct { + Zone scw.Zone `json:"-"` + // Organization: the organization ID the IP is reserved in + // Precisely one of Organization, Project must be set. + Organization *string `json:"organization,omitempty"` + // Project: the project ID the IP is reserved in + // Precisely one of Organization, Project must be set. + Project *string `json:"project,omitempty"` + // Server: UUID of the server you want to attach the IP to + Server *string `json:"server,omitempty"` + // Tags: an array of keywords you want to tag this IP with + Tags []string `json:"tags,omitempty"` +} + +// CreateIP: reserve a flexible IP +func (s *API) CreateIP(req *CreateIPRequest, opts ...scw.RequestOption) (*CreateIPResponse, error) { + var err error + + defaultProject, exist := s.client.GetDefaultProjectID() + if exist && req.Organization == nil && req.Project == nil { + req.Project = &defaultProject + } + + defaultOrganization, exist := s.client.GetDefaultOrganizationID() + if exist && req.Organization == nil && req.Project == nil { + req.Organization = &defaultOrganization + } + + 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) + "/ips", + Headers: http.Header{}, + } + + 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 +} + +type GetIPRequest struct { + Zone scw.Zone `json:"-"` + // IP: the IP ID or address to get + IP string `json:"-"` +} + +// GetIP: get a flexible IP +// +// Get details of an IP with the given 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) + "", + Headers: http.Header{}, + } + + var resp GetIPResponse + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +type UpdateIPRequest struct { + Zone scw.Zone `json:"-"` + // IP: IP ID or IP address + IP string `json:"-"` + // Reverse: reverse domain name + Reverse *NullableStringValue `json:"reverse,omitempty"` + // Tags: an array of keywords you want to tag this IP with + Tags *[]string `json:"tags,omitempty"` + + Server *NullableStringValue `json:"server,omitempty"` +} + +// UpdateIP: update a flexible IP +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) + "", + Headers: http.Header{}, + } + + 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 +} + +type DeleteIPRequest struct { + Zone scw.Zone `json:"-"` + // IP: the ID or the address of the IP to delete + IP string `json:"-"` +} + +// DeleteIP: delete a flexible IP +// +// Delete the IP with the given 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) + "", + Headers: http.Header{}, + } + + err = s.client.Do(scwReq, nil, opts...) + if err != nil { + return err + } + return nil +} + +type ListPrivateNICsRequest struct { + Zone scw.Zone `json:"-"` + + ServerID string `json:"-"` +} + +// ListPrivateNICs: list all private NICs +// +// List all private NICs of a given server. +func (s *API) ListPrivateNICs(req *ListPrivateNICsRequest, opts ...scw.RequestOption) (*ListPrivateNICsResponse, 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) + "/private_nics", + Headers: http.Header{}, + } + + var resp ListPrivateNICsResponse + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +type CreatePrivateNICRequest struct { + Zone scw.Zone `json:"-"` + + ServerID string `json:"-"` + + PrivateNetworkID string `json:"private_network_id,omitempty"` +} + +// CreatePrivateNIC: create a private NIC connecting a server to a private network +// +// Create a private NIC connecting a server 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", + Headers: http.Header{}, + } + + 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 +} + +type GetPrivateNICRequest struct { + Zone scw.Zone `json:"-"` + + ServerID string `json:"-"` + + PrivateNicID string `json:"-"` +} + +// GetPrivateNIC: get a private NIC +// +// 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) + "", + Headers: http.Header{}, + } + + var resp GetPrivateNICResponse + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +type DeletePrivateNICRequest struct { + Zone scw.Zone `json:"-"` + + ServerID string `json:"-"` + + PrivateNicID string `json:"-"` +} + +// DeletePrivateNIC: delete a private NIC +// +// 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) + "", + Headers: http.Header{}, + } + + err = s.client.Do(scwReq, nil, opts...) + if err != nil { + return err + } + return nil +} + +type ListBootscriptsRequest struct { + Zone scw.Zone `json:"-"` + + Arch *string `json:"-"` + + Title *string `json:"-"` + + Default *bool `json:"-"` + + Public *bool `json:"-"` + + PerPage *uint32 `json:"-"` + + Page *int32 `json:"-"` +} + +// 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 + } + + defaultPerPage, exist := s.client.GetDefaultPageSize() + if (req.PerPage == nil || *req.PerPage == 0) && exist { + req.PerPage = &defaultPerPage + } + + 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, + Headers: http.Header{}, + } + + var resp ListBootscriptsResponse + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +// 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 +} + +type GetBootscriptRequest struct { + Zone scw.Zone `json:"-"` + + BootscriptID string `json:"-"` +} + +// GetBootscript: get bootscripts +// +// Get details of a bootscript with the given 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) + "", + Headers: http.Header{}, + } + + var resp GetBootscriptResponse + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +type GetDashboardRequest struct { + Zone scw.Zone `json:"-"` + + Organization *string `json:"-"` + + Project *string `json:"-"` +} + +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, + Headers: http.Header{}, + } + + var resp GetDashboardResponse + + 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/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..5fa431e92 --- /dev/null +++ b/vendor/github.com/scaleway/scaleway-sdk-go/api/instance/v1/instance_utils.go @@ -0,0 +1,329 @@ +package instance + +import ( + "encoding/json" + "fmt" + "sync" + + "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]*Volume) map[string]*VolumeTemplate { + volumeTemplates := map[string]*VolumeTemplate{} + for key, volume := range volumes { + volumeTemplates[key] = &VolumeTemplate{ID: volume.ID, Name: volume.Name} + } + 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() + // 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] = &VolumeTemplate{ + ID: req.VolumeID, + // name is ignored on this PATCH + 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:"-"` +} + +// 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) { + // get volume + getVolumeResponse, err := s.GetVolume(&GetVolumeRequest{ + Zone: req.Zone, + VolumeID: req.VolumeID, + }) + if err != nil { + return nil, err + } + if getVolumeResponse.Volume == nil { + return nil, errors.New("expected volume to have value in response") + } + if getVolumeResponse.Volume.Server == nil { + return nil, errors.New("volume should be attached to a server") + } + serverID := getVolumeResponse.Volume.Server.ID + + defer lockServer(req.Zone, serverID).Unlock() + // get server with volumes + getServerResponse, err := s.GetServer(&GetServerRequest{ + Zone: req.Zone, + ServerID: 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: 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) +} + +// 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, len(results.Servers)) + } + + for name, serverType := range results.Servers { + r.Servers[name] = serverType + } + + r.TotalCount += uint32(len(results.Servers)) + return uint32(len(results.Servers)), nil +} + +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) +} diff --git a/vendor/github.com/scaleway/scaleway-sdk-go/api/instance/v1/security_group_utils.go b/vendor/github.com/scaleway/scaleway-sdk-go/api/instance/v1/security_group_utils.go new file mode 100644 index 000000000..1645c54ec --- /dev/null +++ b/vendor/github.com/scaleway/scaleway-sdk-go/api/instance/v1/security_group_utils.go @@ -0,0 +1,214 @@ +package instance + +import ( + "fmt" + + "github.com/scaleway/scaleway-sdk-go/internal/errors" + "github.com/scaleway/scaleway-sdk-go/scw" +) + +// UpdateSecurityGroupRequest contains the parameters to update a security group +type UpdateSecurityGroupRequest struct { + Zone scw.Zone `json:"-"` + SecurityGroupID string `json:"-"` + + Name *string `json:"name,omitempty"` + Description *string `json:"description,omitempty"` + InboundDefaultPolicy *SecurityGroupPolicy `json:"inbound_default_policy,omitempty"` + OutboundDefaultPolicy *SecurityGroupPolicy `json:"outbound_default_policy,omitempty"` + Stateful *bool `json:"stateful,omitempty"` + OrganizationDefault *bool `json:"organization_default,omitempty"` + ProjectDefault *bool `json:"project_default,omitempty"` +} + +type UpdateSecurityGroupResponse struct { + SecurityGroup *SecurityGroup +} + +// UpdateSecurityGroup updates a 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") + } + + getSGResponse, err := s.GetSecurityGroup(&GetSecurityGroupRequest{ + Zone: req.Zone, + SecurityGroupID: req.SecurityGroupID, + }, opts...) + if err != nil { + return nil, err + } + + setRequest := &setSecurityGroupRequest{ + ID: getSGResponse.SecurityGroup.ID, + Name: getSGResponse.SecurityGroup.Name, + Description: getSGResponse.SecurityGroup.Description, + Organization: getSGResponse.SecurityGroup.Organization, + Project: getSGResponse.SecurityGroup.Project, + OrganizationDefault: getSGResponse.SecurityGroup.OrganizationDefault, + ProjectDefault: getSGResponse.SecurityGroup.ProjectDefault, + OutboundDefaultPolicy: getSGResponse.SecurityGroup.OutboundDefaultPolicy, + InboundDefaultPolicy: getSGResponse.SecurityGroup.InboundDefaultPolicy, + Stateful: getSGResponse.SecurityGroup.Stateful, + Zone: req.Zone, + EnableDefaultSecurity: getSGResponse.SecurityGroup.EnableDefaultSecurity, + CreationDate: getSGResponse.SecurityGroup.CreationDate, + ModificationDate: getSGResponse.SecurityGroup.ModificationDate, + Servers: getSGResponse.SecurityGroup.Servers, + } + + // Override the values that need to be updated + if req.Name != nil { + setRequest.Name = *req.Name + } + if req.Description != nil { + setRequest.Description = *req.Description + } + if req.InboundDefaultPolicy != nil { + setRequest.InboundDefaultPolicy = *req.InboundDefaultPolicy + } + if req.OutboundDefaultPolicy != nil { + setRequest.OutboundDefaultPolicy = *req.OutboundDefaultPolicy + } + if req.Stateful != nil { + setRequest.Stateful = *req.Stateful + } + if req.OrganizationDefault != nil { + setRequest.OrganizationDefault = *req.OrganizationDefault + } + if req.ProjectDefault != nil { + setRequest.ProjectDefault = *req.ProjectDefault + } + + setRes, err := s.setSecurityGroup(setRequest, opts...) + if err != nil { + return nil, err + } + + return &UpdateSecurityGroupResponse{ + SecurityGroup: setRes.SecurityGroup, + }, nil +} + +// UpdateSecurityGroupRuleRequest contains the parameters to update a security group rule +type UpdateSecurityGroupRuleRequest struct { + Zone scw.Zone `json:"-"` + SecurityGroupID string `json:"-"` + SecurityGroupRuleID string `json:"-"` + + Protocol *SecurityGroupRuleProtocol `json:"protocol"` + Direction *SecurityGroupRuleDirection `json:"direction"` + Action *SecurityGroupRuleAction `json:"action"` + IPRange *scw.IPNet `json:"ip_range"` + Position *uint32 `json:"position"` + + // If set to 0, DestPortFrom will be removed. + // See SecurityGroupRule.DestPortFrom for more information + DestPortFrom *uint32 `json:"dest_port_from"` + + // If set to 0, DestPortTo will be removed. + // See SecurityGroupRule.DestPortTo for more information + DestPortTo *uint32 `json:"dest_port_to"` +} + +type UpdateSecurityGroupRuleResponse struct { + Rule *SecurityGroupRule `json:"security_rule"` +} + +// UpdateSecurityGroupRule updates a security group. +func (s *API) UpdateSecurityGroupRule(req *UpdateSecurityGroupRuleRequest, opts ...scw.RequestOption) (*UpdateSecurityGroupRuleResponse, error) { + var err error + + if fmt.Sprint(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") + } + + res, err := s.GetSecurityGroupRule(&GetSecurityGroupRuleRequest{ + SecurityGroupRuleID: req.SecurityGroupRuleID, + SecurityGroupID: req.SecurityGroupID, + Zone: req.Zone, + }) + if err != nil { + return nil, err + } + + setRequest := &setSecurityGroupRuleRequest{ + Zone: req.Zone, + SecurityGroupID: req.SecurityGroupID, + SecurityGroupRuleID: req.SecurityGroupRuleID, + ID: req.SecurityGroupRuleID, + Direction: res.Rule.Direction, + Protocol: res.Rule.Protocol, + DestPortFrom: res.Rule.DestPortFrom, + DestPortTo: res.Rule.DestPortTo, + IPRange: res.Rule.IPRange, + Action: res.Rule.Action, + Position: res.Rule.Position, + Editable: res.Rule.Editable, + } + + // Override the values that need to be updated + if req.Action != nil { + setRequest.Action = *req.Action + } + if req.IPRange != nil { + setRequest.IPRange = *req.IPRange + } + if req.DestPortTo != nil { + if *req.DestPortTo > 0 { + setRequest.DestPortTo = req.DestPortTo + } else { + setRequest.DestPortTo = nil + } + } + if req.DestPortFrom != nil { + if *req.DestPortFrom > 0 { + setRequest.DestPortFrom = req.DestPortFrom + } else { + setRequest.DestPortFrom = nil + } + } + if req.DestPortFrom != nil && req.DestPortTo != nil && *req.DestPortFrom == *req.DestPortTo { + setRequest.DestPortTo = nil + } + if req.Protocol != nil { + setRequest.Protocol = *req.Protocol + } + if req.Direction != nil { + setRequest.Direction = *req.Direction + } + if req.Position != nil { + setRequest.Position = *req.Position + } + + // When we use ICMP protocol portFrom and portTo should be set to nil + if req.Protocol != nil && *req.Protocol == SecurityGroupRuleProtocolICMP { + setRequest.DestPortFrom = nil + setRequest.DestPortTo = nil + } + + resp, err := s.setSecurityGroupRule(setRequest) + if err != nil { + return nil, err + } + + return &UpdateSecurityGroupRuleResponse{ + Rule: resp.Rule, + }, nil +} 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..88f958351 --- /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) (*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, + }) + + 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) 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, + }) + if err != nil { + return err + } + + finalServer, err := s.WaitForServer(&WaitForServerRequest{ + Zone: req.Zone, + ServerID: req.ServerID, + Timeout: &timeout, + RetryInterval: &retryInterval, + }) + 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. +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..683b0056a --- /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) (*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, + }) + + 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..1f9b7ad60 --- /dev/null +++ b/vendor/github.com/scaleway/scaleway-sdk-go/api/instance/v1/volume_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 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) (*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, + }) + + 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 +} 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..e82f62702 --- /dev/null +++ b/vendor/github.com/scaleway/scaleway-sdk-go/api/marketplace/v1/marketplace_sdk.go @@ -0,0 +1,304 @@ +// 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 +) + +// API: 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, + } +} + +type GetImageResponse struct { + Image *Image `json:"image"` +} + +type GetServiceInfoResponse struct { + API string `json:"api"` + + Description string `json:"description"` + + Version string `json:"version"` +} + +type GetVersionResponse struct { + Version *Version `json:"version"` +} + +// 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: label of this image + 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"` +} + +type ListImagesResponse struct { + Images []*Image `json:"images"` + + TotalCount uint32 `json:"total_count"` +} + +type ListVersionsResponse struct { + Versions []*Version `json:"versions"` + + TotalCount uint32 `json:"total_count"` +} + +// LocalImage: local image +type LocalImage struct { + // ID: UUID of this local image + 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"` +} + +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"` +} + +// Service API + +type GetServiceInfoRequest struct { +} + +func (s *API) GetServiceInfo(req *GetServiceInfoRequest, opts ...scw.RequestOption) (*GetServiceInfoResponse, error) { + var err error + + scwReq := &scw.ScalewayRequest{ + Method: "GET", + Path: "/marketplace/v1", + Headers: http.Header{}, + } + + var resp GetServiceInfoResponse + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +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:"-"` +} + +// ListImages: list marketplace images +func (s *API) ListImages(req *ListImagesRequest, opts ...scw.RequestOption) (*ListImagesResponse, error) { + var err error + + defaultPerPage, exist := s.client.GetDefaultPageSize() + if (req.PerPage == nil || *req.PerPage == 0) && exist { + req.PerPage = &defaultPerPage + } + + 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, + Headers: http.Header{}, + } + + var resp ListImagesResponse + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +// 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 +} + +type GetImageRequest struct { + // ImageID: display the image name + ImageID string `json:"-"` +} + +// 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) + "", + Headers: http.Header{}, + } + + var resp GetImageResponse + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +type ListVersionsRequest struct { + ImageID string `json:"-"` +} + +func (s *API) ListVersions(req *ListVersionsRequest, opts ...scw.RequestOption) (*ListVersionsResponse, 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) + "/versions", + Headers: http.Header{}, + } + + var resp ListVersionsResponse + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +type GetVersionRequest struct { + ImageID string `json:"-"` + + VersionID string `json:"-"` +} + +func (s *API) GetVersion(req *GetVersionRequest, opts ...scw.RequestOption) (*GetVersionResponse, error) { + var err error + + if fmt.Sprint(req.ImageID) == "" { + return nil, errors.New("field ImageID cannot be empty in request") + } + + if fmt.Sprint(req.VersionID) == "" { + return nil, errors.New("field VersionID cannot be empty in request") + } + + scwReq := &scw.ScalewayRequest{ + Method: "GET", + Path: "/marketplace/v1/images/" + fmt.Sprint(req.ImageID) + "/versions/" + fmt.Sprint(req.VersionID) + "", + Headers: http.Header{}, + } + + var resp GetVersionResponse + + 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..2d78bb266 --- /dev/null +++ b/vendor/github.com/scaleway/scaleway-sdk-go/api/marketplace/v1/marketplace_utils.go @@ -0,0 +1,98 @@ +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/latests 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) (string, error) { + if req.Zone == "" { + defaultZone, _ := s.client.GetDefaultZone() + req.Zone = defaultZone + } + + listImageRequest := &ListImagesRequest{} + listImageResponse, err := s.ListImages(listImageRequest, scw.WithAllPages()) + 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) +} + +// UnsafeSetTotalCount should not be used +// Internal usage only +func (r *ListVersionsResponse) 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/auth.go b/vendor/github.com/scaleway/scaleway-sdk-go/internal/auth/auth.go new file mode 100644 index 000000000..f7ee31fcb --- /dev/null +++ b/vendor/github.com/scaleway/scaleway-sdk-go/internal/auth/auth.go @@ -0,0 +1,14 @@ +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 +} 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..6a0902755 --- /dev/null +++ b/vendor/github.com/scaleway/scaleway-sdk-go/internal/auth/token.go @@ -0,0 +1,45 @@ +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 +} + +// AnonymizedHeaders returns an anonymized version of Headers() +// This method could be use for logging purpose. +func (t *Token) AnonymizedHeaders() http.Header { + headers := http.Header{} + headers.Set(XAuthTokenHeader, HideSecretKey(t.SecretKey)) + return 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/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..bbb5d1123 --- /dev/null +++ b/vendor/github.com/scaleway/scaleway-sdk-go/internal/parameter/query.go @@ -0,0 +1,33 @@ +package parameter + +import ( + "fmt" + "net/url" + "reflect" + "time" +) + +// 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.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..04316d8e5 --- /dev/null +++ b/vendor/github.com/scaleway/scaleway-sdk-go/logger/default_logger.go @@ -0,0 +1,111 @@ +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 +} + +// 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("SCW_DEBUG") { + 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..834f765ea --- /dev/null +++ b/vendor/github.com/scaleway/scaleway-sdk-go/logger/logger.go @@ -0,0 +1,50 @@ +package logger + +import "os" + +type LogLevel int + +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/docker/docker/pkg/namesgenerator/names-generator.go b/vendor/github.com/scaleway/scaleway-sdk-go/namegenerator/name_generator.go similarity index 66% rename from vendor/github.com/docker/docker/pkg/namesgenerator/names-generator.go rename to vendor/github.com/scaleway/scaleway-sdk-go/namegenerator/name_generator.go index d1da8539b..11fda4532 100644 --- a/vendor/github.com/docker/docker/pkg/namesgenerator/names-generator.go +++ b/vendor/github.com/scaleway/scaleway-sdk-go/namegenerator/name_generator.go @@ -1,10 +1,20 @@ -package namesgenerator // import "github.com/docker/docker/pkg/namesgenerator" +// Source: github.com/docker/docker/pkg/namesgenerator + +package namegenerator import ( - "fmt" "math/rand" + "strings" + "time" ) +var r *rand.Rand + +func init() { + source := rand.NewSource(time.Now().UnixNano()) + r = rand.New(source) +} + var ( left = [...]string{ "admiring", @@ -14,16 +24,22 @@ var ( "amazing", "angry", "awesome", + "beautiful", "blissful", + "bold", "boring", "brave", + "busy", + "charming", "clever", "cocky", + "cool", "compassionate", "competent", "condescending", "confident", "cranky", + "crazy", "dazzling", "determined", "distracted", @@ -35,16 +51,19 @@ var ( "elegant", "eloquent", "epic", + "exciting", "fervent", "festive", "flamboyant", "focused", "friendly", "frosty", + "funny", "gallant", "gifted", "goofy", "gracious", + "great", "happy", "hardcore", "heuristic", @@ -52,6 +71,8 @@ var ( "hungry", "infallible", "inspiring", + "interesting", + "intelligent", "jolly", "jovial", "keen", @@ -59,11 +80,13 @@ var ( "laughing", "loving", "lucid", + "magical", "mystifying", "modest", "musing", "naughty", "nervous", + "nice", "nifty", "nostalgic", "objective", @@ -75,6 +98,7 @@ var ( "priceless", "quirky", "quizzical", + "recursing", "relaxed", "reverent", "romantic", @@ -84,8 +108,10 @@ var ( "silly", "sleepy", "stoic", + "strange", "stupefied", "suspicious", + "sweet", "tender", "thirsty", "trusting", @@ -114,6 +140,9 @@ var ( // 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", @@ -135,6 +164,9 @@ var ( // 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", @@ -159,6 +191,12 @@ var ( // 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", @@ -174,6 +212,9 @@ var ( // 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", @@ -186,23 +227,44 @@ var ( // 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 + // 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", - // 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 + // É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 mathematitian. He is known fo his works on probability, statistics, mechanics, analytical geometry and number theory https://en.wikipedia.org/wiki/Pafnuty_Chebyshev + // 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", - //Claude Shannon - The father of information theory and founder of digital circuit design theory. (https://en.wikipedia.org/wiki/Claude_Shannon) - "shannon", + // 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", @@ -230,9 +292,24 @@ var ( // 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", @@ -245,11 +322,17 @@ var ( // 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", - // 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", + // 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", @@ -260,6 +343,12 @@ var ( // 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", @@ -272,12 +361,27 @@ var ( // 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", @@ -293,27 +397,51 @@ var ( // 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", @@ -326,6 +454,9 @@ var ( // 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", @@ -365,6 +496,9 @@ var ( // 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", @@ -392,10 +526,16 @@ var ( // 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 + // 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", - //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 + // 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 @@ -413,6 +553,15 @@ var ( // 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", @@ -422,12 +571,18 @@ var ( // 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", @@ -437,6 +592,9 @@ var ( // 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", @@ -446,18 +604,33 @@ var ( // 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", @@ -479,6 +652,9 @@ var ( // 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", @@ -518,18 +694,39 @@ var ( // 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", @@ -539,24 +736,30 @@ var ( // 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", - // 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", - // 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", @@ -566,18 +769,27 @@ var ( // Bertha Swirles was a theoretical physicist who made a number of contributions to early quantum theory. https://en.wikipedia.org/wiki/Bertha_Swirles "swirles", - // Valentina Tereshkova is a russian engineer, cosmonaut and politician. She was the first woman flying 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 + // 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", @@ -599,12 +811,18 @@ var ( // 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", @@ -617,6 +835,9 @@ var ( // 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", @@ -629,17 +850,14 @@ var ( ) // GetRandomName generates a random name from the list of adjectives and surnames in this package -// formatted as "adjective_surname". For example 'focused_turing'. If retry is non-zero, a random -// integer between 0 and 10 will be added to the end of the name, e.g `focused_turing3` -func GetRandomName(retry int) string { +// formatted as "scw-adjective-surname". For example 'scw-focused-turing'. +func GetRandomName(prefixes ...string) string { begin: - name := fmt.Sprintf("%s_%s", left[rand.Intn(len(left))], right[rand.Intn(len(right))]) - if name == "boring_wozniak" /* Steve Wozniak is not boring */ { + 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 } - if retry > 0 { - name = fmt.Sprintf("%s%d", name, rand.Intn(10)) - } 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..0ab7eae24 --- /dev/null +++ b/vendor/github.com/scaleway/scaleway-sdk-go/scw/README.md @@ -0,0 +1,55 @@ +# Scaleway config + +## TL;DR + +Recommended config file: + +```yaml +# get your credentials on https://console.scaleway.com/account/credentials +access_key: SCWXXXXXXXXXXXXXXXXX +secret_key: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx +default_organization_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` +3. Windows home directory: `%USERPROFILE%/.config/scw/config.yaml` + +## V1 config (DEPRECATED) + +The V1 config (AKA legacy config) `.scwrc` is deprecated. +To migrate the V1 config to the new format use the function [`MigrateLegacyConfig`](https://godoc.org/github.com/scaleway/scaleway-sdk-go/scw#MigrateLegacyConfig), this will create a [proper config file](#tl-dr) the new [config file path](#config-file-path). + +## 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/account/credentials)) | `$SCALEWAY_ACCESS_KEY` (used by terraform) | +| `$SCW_SECRET_KEY` | Secret key of a token ([get yours](https://console.scaleway.com/account/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, if you don't have one use your organization ID ([get yours](https://console.scaleway.com/account/credentials)) | `$SCW_ORGANIZATION` (used by cli),`$SCALEWAY_ORGANIZATION` (used by terraform) | +| `$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..0bdb13bca --- /dev/null +++ b/vendor/github.com/scaleway/scaleway-sdk-go/scw/client.go @@ -0,0 +1,376 @@ +package scw + +import ( + "crypto/tls" + "encoding/json" + "io" + "math" + "net" + "net/http" + "net/http/httputil" + "reflect" + "strconv" + "sync/atomic" + "time" + + "github.com/scaleway/scaleway-sdk-go/internal/auth" + "github.com/scaleway/scaleway-sdk-go/internal/errors" + "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) + } + + 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 + } + 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.allPages { + return c.doListAll(req, res) + } + + return c.do(req, res) +} + +// requestNumber auto increments on each do(). +// This allows easy distinguishing of concurrently performed requests in log. +var requestNumber uint32 + +// do performs a single HTTP request based on the ScalewayRequest object. +func (c *Client) do(req *ScalewayRequest, res interface{}) (sdkErr error) { + currentRequestNumber := atomic.AddUint32(&requestNumber, 1) + + 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) + } + + if logger.ShouldLog(logger.LogLevelDebug) { + // Keep original headers (before anonymization) + originalHeaders := httpRequest.Header + + // Get anonymized headers + httpRequest.Header = req.getAllHeaders(req.auth, c.userAgent, true) + + dump, err := httputil.DumpRequestOut(httpRequest, 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 + httpRequest.Header = originalHeaders + } + + // 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") + } + }() + if logger.ShouldLog(logger.LogLevelDebug) { + dump, err := httputil.DumpResponse(httpResponse, 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) + } + } + + sdkErr = hasResponseError(httpResponse) + if sdkErr != nil { + return sdkErr + } + + if res != nil { + contentType := httpResponse.Header.Get("Content-Type") + + switch contentType { + case "application/json": + err = json.NewDecoder(httpResponse.Body).Decode(&res) + if err != nil { + return errors.Wrap(err, "could not parse %s response body", contentType) + } + default: + 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.Atoi(xTotalCountStr) + if err != nil { + return errors.Wrap(err, "could not parse X-Total-Count header") + } + legacyLister.UnsafeSetTotalCount(xTotalCount) + } + } + + return nil +} + +type lister interface { + UnsafeGetTotalCount() uint32 + UnsafeAppend(interface{}) (uint32, error) +} + +type legacyLister interface { + UnsafeSetTotalCount(totalCount int) +} + +const maxPageCount uint32 = 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 response, isLister := res.(lister); isLister { + pageCount := maxPageCount + for page := uint32(1); page <= pageCount; page++ { + // set current page + req.Query.Set("page", strconv.FormatUint(uint64(page), 10)) + + // request the next page + nextPage := newVariableFromType(response) + err := c.do(req, nextPage) + if err != nil { + return err + } + + // append results + pageSize, err := response.UnsafeAppend(nextPage) + if err != nil { + return err + } + + if pageSize == 0 { + return nil + } + + // set total count on first request + if pageCount == maxPageCount { + totalCount := nextPage.(lister).UnsafeGetTotalCount() + pageCount = (totalCount + pageSize - 1) / pageSize + } + } + return nil + } + + return errors.New("%T does not support pagination", res) +} + +// 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.Transport{ + DialContext: (&net.Dialer{Timeout: 5 * time.Second}).DialContext, + TLSHandshakeTimeout: 5 * time.Second, + ResponseHeaderTimeout: 30 * time.Second, + MaxIdleConnsPerHost: 20, + }, + } +} + +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 +} 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..f01b37400 --- /dev/null +++ b/vendor/github.com/scaleway/scaleway-sdk-go/scw/client_option.go @@ -0,0 +1,265 @@ +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) + } +} + +// 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 + } + + 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 + } + } +} + +// WithProfile 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) + } + + // 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..eb423162f --- /dev/null +++ b/vendor/github.com/scaleway/scaleway-sdk-go/scw/config.go @@ -0,0 +1,343 @@ +package scw + +import ( + "bytes" + "io/ioutil" + "os" + "path/filepath" + "text/template" + + "github.com/scaleway/scaleway-sdk-go/internal/auth" + "github.com/scaleway/scaleway-sdk-go/internal/errors" + "github.com/scaleway/scaleway-sdk-go/logger" + "gopkg.in/yaml.v2" +) + +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/account/credentials + +# 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) { + return LoadConfigFromPath(GetConfigPath()) +} + +// 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") + } + + _, err = unmarshalConfV1(file) + if err == nil { + // reject V1 config + return nil, errors.New("found legacy config in %s: legacy config is not allowed, please switch to the new config file format: %s", path, documentationLink) + } + + 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, + } + + 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 + } + } + + return np +} diff --git a/vendor/github.com/scaleway/scaleway-sdk-go/scw/config_legacy.go b/vendor/github.com/scaleway/scaleway-sdk-go/scw/config_legacy.go new file mode 100644 index 000000000..9a81df685 --- /dev/null +++ b/vendor/github.com/scaleway/scaleway-sdk-go/scw/config_legacy.go @@ -0,0 +1,92 @@ +package scw + +import ( + "encoding/json" + "io/ioutil" + "os" + "path/filepath" + + "github.com/scaleway/scaleway-sdk-go/internal/errors" + "github.com/scaleway/scaleway-sdk-go/logger" + "gopkg.in/yaml.v2" +) + +// configV1 is a Scaleway CLI configuration file +type configV1 struct { + // Organization is the identifier of the Scaleway organization + Organization string `json:"organization"` + + // Token is the authentication token for the Scaleway organization + Token string `json:"token"` + + // Version is the actual version of scw CLI + Version string `json:"version"` +} + +func unmarshalConfV1(content []byte) (*configV1, error) { + var config configV1 + err := json.Unmarshal(content, &config) + if err != nil { + return nil, err + } + return &config, err +} + +func (v1 *configV1) toV2() *Config { + return &Config{ + Profile: Profile{ + DefaultOrganizationID: &v1.Organization, + DefaultProjectID: &v1.Organization, // v1 config is not aware of project, so default project is set to organization ID + SecretKey: &v1.Token, + // ignore v1 version + }, + } +} + +// MigrateLegacyConfig will migrate the legacy config to the V2 when none exist yet. +// Returns a boolean set to true when the migration happened. +// TODO: get accesskey from account? +func MigrateLegacyConfig() (bool, error) { + // STEP 1: try to load config file V2 + v2Path, v2PathOk := getConfigV2FilePath() + if !v2PathOk || fileExist(v2Path) { + return false, nil + } + + // STEP 2: try to load config file V1 + v1Path, v1PathOk := getConfigV1FilePath() + if !v1PathOk { + return false, nil + } + file, err := ioutil.ReadFile(v1Path) + if err != nil { + return false, nil + } + confV1, err := unmarshalConfV1(file) + if err != nil { + return false, errors.Wrap(err, "content of config file %s is invalid json", v1Path) + } + + // STEP 3: create dir + err = os.MkdirAll(filepath.Dir(v2Path), 0700) + if err != nil { + return false, errors.Wrap(err, "mkdir did not work on %s", filepath.Dir(v2Path)) + } + + // STEP 4: marshal yaml config + newConfig := confV1.toV2() + file, err = yaml.Marshal(newConfig) + if err != nil { + return false, err + } + + // STEP 5: save config + err = ioutil.WriteFile(v2Path, file, defaultConfigPermission) + if err != nil { + return false, errors.Wrap(err, "cannot write file %s", v2Path) + } + + // STEP 6: log success + logger.Warningf("migrated existing config to %s", v2Path) + return true, nil +} 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..7b671dcf5 --- /dev/null +++ b/vendor/github.com/scaleway/scaleway-sdk-go/scw/convert.go @@ -0,0 +1,177 @@ +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. +// Deprecated: the use of time.Duration in request is deprecated and only available in lb API +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..75da2b159 --- /dev/null +++ b/vendor/github.com/scaleway/scaleway-sdk-go/scw/custom_types.go @@ -0,0 +1,339 @@ +package scw + +import ( + "bytes" + "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) 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" + } + + _, value, err := net.ParseCIDR(str) + if err != nil { + return err + } + 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/1e9) + 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 +} + +// 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 +} 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..8672d8351 --- /dev/null +++ b/vendor/github.com/scaleway/scaleway-sdk-go/scw/env.go @@ -0,0 +1,143 @@ +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" + + // 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..9c0785a9e --- /dev/null +++ b/vendor/github.com/scaleway/scaleway-sdk-go/scw/errors.go @@ -0,0 +1,484 @@ +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. + if res.Header.Get("Content-Type") != "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} + 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.Current) + } + + 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() {} 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..9cf72987b --- /dev/null +++ b/vendor/github.com/scaleway/scaleway-sdk-go/scw/locality.go @@ -0,0 +1,198 @@ +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") + // ZoneNlAms1 represents the nl-ams-1 zone + ZoneNlAms1 = Zone("nl-ams-1") +) + +var ( + // AllZones is an array that list all zones + AllZones = []Zone{ + ZoneFrPar1, + ZoneFrPar2, + ZoneNlAms1, + } +) + +// 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") +) + +var ( + // AllRegions is an array that list all regions + AllRegions = []Region{ + RegionFrPar, + RegionNlAms, + } +) + +// 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} + case RegionNlAms: + return []Zone{ZoneNlAms1} + 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..0c90adace --- /dev/null +++ b/vendor/github.com/scaleway/scaleway-sdk-go/scw/path.go @@ -0,0 +1,98 @@ +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 +} + +// getConfigV1FilePath returns the path to the v1 config file +func getConfigV1FilePath() (string, bool) { + path, err := os.UserHomeDir() + if err != nil { + return "", false + } + return filepath.Clean(filepath.Join(path, ".scwrc")), 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..19f0dd4d5 --- /dev/null +++ b/vendor/github.com/scaleway/scaleway-sdk-go/scw/request.go @@ -0,0 +1,104 @@ +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 +} + +// 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 +} + +// 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 +} 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..a5ff37663 --- /dev/null +++ b/vendor/github.com/scaleway/scaleway-sdk-go/scw/request_option.go @@ -0,0 +1,32 @@ +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) + } +} 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..22bdfa2fc --- /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.6+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/modules.txt b/vendor/modules.txt index 7c7461859..7f70ccdd3 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -173,8 +173,6 @@ github.com/c2h5oh/datasize github.com/cheggaaa/pb # github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e github.com/chzyer/readline -# github.com/creack/goselect v0.1.0 -github.com/creack/goselect # github.com/davecgh/go-spew v1.1.1 github.com/davecgh/go-spew/spew # github.com/dgrijalva/jwt-go v3.2.0+incompatible @@ -189,10 +187,6 @@ github.com/digitalocean/go-qemu/qmp github.com/digitalocean/godo # github.com/dimchansky/utfbom v1.1.0 github.com/dimchansky/utfbom -# github.com/docker/docker v0.0.0-20180422163414-57142e89befe -github.com/docker/docker/pkg/namesgenerator -# github.com/dustin/go-humanize v1.0.0 -github.com/dustin/go-humanize # github.com/dylanmei/iso8601 v0.1.0 github.com/dylanmei/iso8601 # github.com/dylanmei/winrmtest v0.0.0-20170819153634-c2fbb09e6c08 @@ -291,8 +285,6 @@ github.com/gophercloud/gophercloud/pagination github.com/gophercloud/utils/env github.com/gophercloud/utils/openstack/clientconfig github.com/gophercloud/utils/openstack/compute/v2/flavors -# github.com/gorilla/websocket v0.0.0-20170319172727-a91eba7f9777 -github.com/gorilla/websocket # github.com/grpc-ecosystem/go-grpc-middleware v1.1.0 github.com/grpc-ecosystem/go-grpc-middleware # github.com/hako/durafmt v0.0.0-20200710122514-c0fb7b4da026 @@ -468,10 +460,6 @@ github.com/mitchellh/reflectwalk github.com/modern-go/concurrent # github.com/modern-go/reflect2 v1.0.1 github.com/modern-go/reflect2 -# github.com/moul/anonuuid v0.0.0-20160222162117-609b752a95ef -github.com/moul/anonuuid -# github.com/moul/gotty-client v0.0.0-20180327180212-b26a57ebc215 -github.com/moul/gotty-client # github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d github.com/nu7hatch/gouuid # github.com/olekukonko/tablewriter v0.0.0-20180105111133-96aac992fc8b @@ -500,16 +488,22 @@ github.com/posener/complete/cmd github.com/posener/complete/cmd/install # github.com/profitbricks/profitbricks-sdk-go v4.0.2+incompatible github.com/profitbricks/profitbricks-sdk-go -# github.com/renstrom/fuzzysearch v0.0.0-20160331204855-2d205ac6ec17 -github.com/renstrom/fuzzysearch/fuzzy # github.com/ryanuber/go-glob v1.0.0 github.com/ryanuber/go-glob # github.com/satori/go.uuid v1.2.0 github.com/satori/go.uuid -# github.com/scaleway/scaleway-cli v0.0.0-20180921094345-7b12c9699d70 -github.com/scaleway/scaleway-cli/pkg/api -github.com/scaleway/scaleway-cli/pkg/sshcommand -github.com/scaleway/scaleway-cli/pkg/utils +# github.com/scaleway/scaleway-sdk-go v1.0.0-beta.6.0.20200903143645-c0ce17a0443d +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/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/shirou/gopsutil v2.18.12+incompatible github.com/shirou/gopsutil/cpu github.com/shirou/gopsutil/host diff --git a/website/pages/partials/builder/scaleway/Config-not-required.mdx b/website/pages/partials/builder/scaleway/Config-not-required.mdx index 20a417b83..47c8b3e7b 100644 --- a/website/pages/partials/builder/scaleway/Config-not-required.mdx +++ b/website/pages/partials/builder/scaleway/Config-not-required.mdx @@ -1,5 +1,8 @@ +- `api_url` (string) - The Scaleway API URL to use + It can also be specified via the environment variable SCW_API_URL + - `snapshot_name` (string) - The name of the resulting snapshot that will appear in your account. Default packer-TIMESTAMP @@ -16,3 +19,22 @@ bootscript, Default bootscript - `remove_volume` (bool) - Remove Volume + +- `api_token` (string) - The token to use to authenticate with your account. + It can also be specified via environment variable SCALEWAY_API_TOKEN. You + can see and generate tokens in the "Credentials" + section of the control panel. + Deprecated, use SecretKey instead + +- `organization_id` (string) - The organization id to use to identify your + organization. It can also be specified via environment variable + SCALEWAY_ORGANIZATION. Your organization id is available in the + "Account" section of the + control panel. + Previously named: api_access_key with environment variable: SCALEWAY_API_ACCESS_KEY + Deprecated, use ProjectID instead + +- `region` (string) - The name of the region to launch the server in (par1 + or ams1). Consequently, this is the region where the snapshot will be + available. + Deprecated, use Zone instead diff --git a/website/pages/partials/builder/scaleway/Config-required.mdx b/website/pages/partials/builder/scaleway/Config-required.mdx index cafb63def..f3a641d37 100644 --- a/website/pages/partials/builder/scaleway/Config-required.mdx +++ b/website/pages/partials/builder/scaleway/Config-required.mdx @@ -1,20 +1,16 @@ -- `api_token` (string) - The token to use to authenticate with your account. - It can also be specified via environment variable SCALEWAY_API_TOKEN. You - can see and generate tokens in the "Credentials" - section of the control panel. +- `access_key` (string) - The AccessKey corresponding to the secret key. + It can also be specified via the environment variable SCW_ACCESS_KEY. -- `organization_id` (string) - The organization id to use to identify your - organization. It can also be specified via environment variable - SCALEWAY_ORGANIZATION. Your organization id is available in the - "Account" section of the - control panel. - Previously named: api_access_key with environment variable: SCALEWAY_API_ACCESS_KEY +- `secret_key` (string) - The SecretKey to authenticate against the Scaleway API. + It can also be specified via the environment variable SCW_SECRET_KEY. -- `region` (string) - The name of the region to launch the server in (par1 - or ams1). Consequently, this is the region where the snapshot will be - available. +- `project_id` (string) - The Project ID in which the instances, volumes and snapshots will be created. + It can also be specified via the environment variable SCW_DEFAULT_PROJECT_ID. + +- `zone` (string) - The Zone in which the instances, volumes and snapshots will be created. + It can also be specified via the environment variable SCW_DEFAULT_ZONE - `image` (string) - The UUID of the base image to use. This is the image that will be used to launch a new server and provision it. See