diff --git a/README.md b/README.md index b62944929..d307540f1 100644 --- a/README.md +++ b/README.md @@ -78,6 +78,7 @@ Currently supported platforms are: - [Google Cloud](docs/platform-gcp.md) `[x86_64]` - [Microsoft Azure](docs/platform-azure.md) `[x86_64]` - [OpenStack](docs/platform-openstack.md) `[x86_64]` + - [Scaleway](docs/platform-scaleway.md) `[x86_64]` - Baremetal: - [packet.net](docs/platform-packet.md) `[x86_64, arm64]` - [Raspberry Pi Model 3b](docs/platform-rpi3.md) `[arm64]` diff --git a/docs/platform-scaleway.md b/docs/platform-scaleway.md index cc8454b44..1c3a7d946 100644 --- a/docs/platform-scaleway.md +++ b/docs/platform-scaleway.md @@ -3,14 +3,11 @@ This is a quick guide to run LinuxKit on Scaleway (only VPS x86_64 for now) ## Setup -Before you proceed it's recommanded that you set up the [Scaleway CLI](https://github.com/scaleway/scaleway-cli/) -and perform an `scw login`. This will create a `$HOME/.scwrc` file containing the required API token. -You can also use the `SCW_TOKEN` environment variable to set a Scaleway token. -The `-token` flag of the `linuxkit push scaleway` and `linuxkit run scaleway` can also be used. - -The environment variable `SCW_TARGET_REGION` is used to set the region (there is also the `-region` flag) +You must create a Scaleway Secret Key (available ine the [Scaleway Console](https://console.scaleway.com/account/credentials)) first. +Then you can use it either with the `SCW_SECRET_KEY` environment variable or with the `-secret-key` flag of the `linuxkit push scaleway` and `linuxkit run scaleway` commands. +The environment variable `SCW_DEFAULT_ZONE` is used to set the zone (there is also the `-zone` flag) ## Build an image @@ -33,13 +30,13 @@ It can be overidden with the `-img-name` flag or the `SCW_IMAGE_NAME` environmen **Note 1:** If an image (and snapshot) of the same name exists, it will be replaced. -**Note 2:** The image is region specific: if you create an image in `par1` you can't use is in `ams1`. +**Note 2:** The image is zone specific: if you create an image in `par1` you can't use is in `ams1`. ### Push process Building a Scaleway image have a special process. Basically: -* Create an `image-builder` instance with an additional volume, based on Ubuntu Xenial (only x86_64 for now) +* Create an `image-builder` instance with an additional volume, based on Ubuntu Bionic (only x86_64 for now) * Copy the ISO image on this instance * Use `dd` to write the image on the additional volume (`/dev/vdb` by default) * Terminate the instance, create a snapshot, and create an image from the snapshot diff --git a/src/cmd/linuxkit/push_scaleway.go b/src/cmd/linuxkit/push_scaleway.go index 886aec116..4a9c11192 100644 --- a/src/cmd/linuxkit/push_scaleway.go +++ b/src/cmd/linuxkit/push_scaleway.go @@ -20,11 +20,12 @@ func pushScaleway(args []string) { flags.PrintDefaults() } nameFlag := flags.String("img-name", "", "Overrides the name used to identify the image name in Scaleway's images. Defaults to the base of 'path' with the '.iso' suffix removed") - tokenFlag := flags.String("token", "", "Token to connet to Scaleway API") + secretKeyFlag := flags.String("secret-key", "", "Secret Key to connet to Scaleway API") sshKeyFlag := flags.String("ssh-key", os.Getenv("HOME")+"/.ssh/id_rsa", "SSH key file") instanceIDFlag := flags.String("instance-id", "", "Instance ID of a running Scaleway instance, with a second volume.") deviceNameFlag := flags.String("device-name", "/dev/vdb", "Device name on which the image will be copied") - regionFlag := flags.String("region", defaultScalewayRegion, "Select scaleway region") + zoneFlag := flags.String("zone", defaultScalewayZone, "Select Scaleway zone") + projectIDFlag := flags.String("project-id", "", "Select Scaleway's project ID") noCleanFlag := flags.Bool("no-clean", false, "Do not remove temporary instance and volumes") if err := flags.Parse(args); err != nil { @@ -40,11 +41,12 @@ func pushScaleway(args []string) { path := remArgs[0] name := getStringValue(scalewayNameVar, *nameFlag, "") - token := getStringValue(tokenVar, *tokenFlag, "") + secretKey := getStringValue(secretKeyVar, *secretKeyFlag, "") sshKeyFile := getStringValue(sshKeyVar, *sshKeyFlag, "") instanceID := getStringValue(instanceIDVar, *instanceIDFlag, "") deviceName := getStringValue(deviceNameVar, *deviceNameFlag, "") - region := getStringValue(regionVar, *regionFlag, defaultScalewayRegion) + zone := getStringValue(zoneVar, *zoneFlag, defaultScalewayZone) + projectID := getStringValue(projectIDVar, *projectIDFlag, "") const suffix = ".iso" if name == "" { @@ -52,7 +54,7 @@ func pushScaleway(args []string) { name = filepath.Base(name) } - client, err := NewScalewayClient(token, region) + client, err := NewScalewayClient(secretKey, zone, projectID) if err != nil { log.Fatalf("Unable to connect to Scaleway: %v", err) } diff --git a/src/cmd/linuxkit/run_scaleway.go b/src/cmd/linuxkit/run_scaleway.go index 637a38c02..5c1a45b1e 100644 --- a/src/cmd/linuxkit/run_scaleway.go +++ b/src/cmd/linuxkit/run_scaleway.go @@ -10,15 +10,16 @@ import ( ) const ( - defaultScalewayInstanceType = "VC1S" - defaultScalewayRegion = "par1" + defaultScalewayInstanceType = "DEV1-S" + defaultScalewayZone = "par1" scalewayNameVar = "SCW_IMAGE_NAME" // non-standard - tokenVar = "SCW_TOKEN" // non-standard + secretKeyVar = "SCW_SECRET_KEY" // non-standard sshKeyVar = "SCW_SSH_KEY_FILE" // non-standard instanceIDVar = "SCW_INSTANCE_ID" // non-standard deviceNameVar = "SCW_DEVICE_NAME" // non-standard - regionVar = "SCW_TARGET_REGION" + scwZoneVar = "SCW_DEFAULT_ZONE" + projectIDVar = "SCW_DEFAULT_PROJECT_ID" instanceTypeVar = "SCW_RUN_TYPE" // non-standard ) @@ -35,8 +36,9 @@ func runScaleway(args []string) { } instanceTypeFlag := flags.String("instance-type", defaultScalewayInstanceType, "Scaleway instance type") instanceNameFlag := flags.String("instance-name", "linuxkit", "Name of the create instance, default to the image name") - tokenFlag := flags.String("token", "", "Token to connect to Scaleway API") - regionFlag := flags.String("region", defaultScalewayRegion, "Select Scaleway region") + secretKeyFlag := flags.String("secret-key", "", "Secret Key to connect to Scaleway API") + zoneFlag := flags.String("zone", defaultScalewayZone, "Select Scaleway zone") + projectIDFlag := flags.String("project-id", "", "Select Scaleway's project ID") cleanFlag := flags.Bool("clean", false, "Remove instance") noAttachFlag := flags.Bool("no-attach", false, "Don't attach to serial port, you will have to connect to instance manually") @@ -54,10 +56,11 @@ func runScaleway(args []string) { instanceType := getStringValue(instanceTypeVar, *instanceTypeFlag, defaultScalewayInstanceType) instanceName := getStringValue("", *instanceNameFlag, name) - token := getStringValue(tokenVar, *tokenFlag, "") - region := getStringValue(regionVar, *regionFlag, defaultScalewayRegion) + secretKey := getStringValue(secretKeyVar, *secretKeyFlag, "") + zone := getStringValue(scwZoneVar, *zoneFlag, defaultScalewayZone) + projectID := getStringValue(projectIDVar, *projectIDFlag, "") - client, err := NewScalewayClient(token, region) + client, err := NewScalewayClient(secretKey, zone, projectID) if err != nil { log.Fatalf("Unable to connect to Scaleway: %v", err) } diff --git a/src/cmd/linuxkit/scaleway.go b/src/cmd/linuxkit/scaleway.go index 7841d9c77..f3604d0b4 100644 --- a/src/cmd/linuxkit/scaleway.go +++ b/src/cmd/linuxkit/scaleway.go @@ -2,7 +2,6 @@ package main import ( "bytes" - "encoding/json" "errors" "fmt" "io" @@ -11,143 +10,189 @@ import ( "os" "path/filepath" "strings" + "syscall" "time" + "github.com/ScaleFT/sshkeys" gotty "github.com/moul/gotty-client" - scw "github.com/scaleway/go-scaleway" - "github.com/scaleway/go-scaleway/logger" - "github.com/scaleway/go-scaleway/types" + "github.com/scaleway/scaleway-sdk-go/api/instance/v1" + "github.com/scaleway/scaleway-sdk-go/api/marketplace/v1" + "github.com/scaleway/scaleway-sdk-go/scw" + "github.com/scaleway/scaleway-sdk-go/scwconfig" + "github.com/scaleway/scaleway-sdk-go/utils" log "github.com/sirupsen/logrus" "golang.org/x/crypto/ssh" + "golang.org/x/crypto/ssh/terminal" +) + +var ( + defaultScalewayCommercialType = "DEV1-S" + defaultScalewayImageName = "Ubuntu Bionic" + defaultScalewayImageArch = "x86_64" + defaultVolumeSize = uint64(10000000000) ) // ScalewayClient contains state required for communication with Scaleway as well as the instance type ScalewayClient struct { - api *scw.ScalewayAPI - fileName string - region string - sshConfig *ssh.ClientConfig -} - -// ScalewayConfig contains required field to read scaleway config file -type ScalewayConfig struct { - Organization string `json:"organization"` - Token string `json:"token"` - Version string `json:"version"` + instanceAPI *instance.API + marketplaceAPI *marketplace.API + fileName string + zone string + sshConfig *ssh.ClientConfig + secretKey string } // NewScalewayClient creates a new scaleway client -func NewScalewayClient(token, region string) (*ScalewayClient, error) { +func NewScalewayClient(secretKey, zone, projectID string) (*ScalewayClient, error) { + var scwClient *scw.Client log.Debugf("Connecting to Scaleway") - organization := "" - if token == "" { - log.Debugf("Using .scwrc file to get token") - homeDir := os.Getenv("HOME") - if homeDir == "" { - homeDir = os.Getenv("USERPROFILE") // Windows support - } - if homeDir == "" { - return nil, fmt.Errorf("Home directory not found") - } - swrcPath := filepath.Join(homeDir, ".scwrc") - - file, err := ioutil.ReadFile(swrcPath) - if err != nil { - return nil, fmt.Errorf("Error reading Scaleway config file: %v", err) - } - - var scalewayConfig ScalewayConfig - err = json.Unmarshal(file, &scalewayConfig) - if err != nil { - return nil, fmt.Errorf("Error during unmarshal of Scaleway config file: %v", err) - } - - token = scalewayConfig.Token - organization = scalewayConfig.Organization - } - - api, err := scw.NewScalewayAPI(organization, token, "", region) - if err != nil { - return nil, err - } - - l := logger.NewDisableLogger() - api.Logger = l - - if organization == "" { - organisations, err := api.GetOrganization() + if secretKey == "" { + config, err := scwconfig.Load() + if err != nil { + return nil, err + } + + scwClient, err = scw.NewClient( + scw.WithConfig(config), + ) + if err != nil { + return nil, err + } + } else { + scwZone, err := utils.ParseZone(zone) + if err != nil { + return nil, err + } + + scwClient, err = scw.NewClient( + scw.WithAuth("", secretKey), + scw.WithDefaultZone(scwZone), + scw.WithDefaultProjectID(projectID), + ) if err != nil { return nil, err } - api.Organization = organisations.Organizations[0].ID } + instanceAPI := instance.NewAPI(scwClient) + marketplaceAPI := marketplace.NewAPI(scwClient) + client := &ScalewayClient{ - api: api, - fileName: "", - region: region, + instanceAPI: instanceAPI, + marketplaceAPI: marketplaceAPI, + zone: zone, + fileName: "", + secretKey: secretKey, } return client, nil } +func (s *ScalewayClient) getImageID(imageName, commercialType, arch string) (string, error) { + imagesResp, err := s.marketplaceAPI.ListImages(&marketplace.ListImagesRequest{}) + if err != nil { + return "", err + } + for _, image := range imagesResp.Images { + if image.Name == imageName { + for _, version := range image.Versions { + for _, localImage := range version.LocalImages { + if localImage.Arch == arch { + for _, compatibleCommercialType := range localImage.CompatibleCommercialTypes { + if compatibleCommercialType == commercialType { + return localImage.ID, nil + } + } + } + } + } + } + } + return "", errors.New("No image matching given requests") +} + // CreateInstance create an instance with one additional volume func (s *ScalewayClient) CreateInstance() (string, error) { // get the Ubuntu Xenial image id - image, err := s.api.GetImageID("Ubuntu Xenial", "x86_64") // TODO fix arch and use from args + imageID, err := s.getImageID(defaultScalewayImageName, defaultScalewayCommercialType, defaultScalewayImageArch) if err != nil { return "", err } - imageID := image.Identifier - var serverDefinition types.ScalewayServerDefinition - - serverDefinition.Name = "linuxkit-builder" - serverDefinition.Image = &imageID - serverDefinition.CommercialType = "VC1M" // TODO use args? - - // creation of second volume - var volumeDefinition types.ScalewayVolumeDefinition - volumeDefinition.Name = "linuxkit-builder-volume" - volumeDefinition.Size = 50000000000 // FIX remove hardcoded value - volumeDefinition.Type = "l_ssd" + createVolumeRequest := &instance.CreateVolumeRequest{ + Name: "linuxkit-builder-volume", + VolumeType: "l_ssd", + Size: &defaultVolumeSize, + } log.Debugf("Creating volume on Scaleway") - volumeID, err := s.api.PostVolume(volumeDefinition) + volumeResp, err := s.instanceAPI.CreateVolume(createVolumeRequest) if err != nil { return "", err } - serverDefinition.Volumes = make(map[string]string) - serverDefinition.Volumes["1"] = volumeID + volumeMap := make(map[string]*instance.VolumeTemplate) + volumeTemplate := &instance.VolumeTemplate{ + Size: defaultVolumeSize, + } + volumeMap["0"] = volumeTemplate - serverID, err := s.api.PostServer(serverDefinition) + createServerRequest := &instance.CreateServerRequest{ + Name: "linuxkit-builder", + CommercialType: defaultScalewayCommercialType, + DynamicIPRequired: true, + Image: imageID, + EnableIPv6: false, + BootType: instance.ServerBootTypeLocal, + Volumes: volumeMap, + } + + serverResp, err := s.instanceAPI.CreateServer(createServerRequest) if err != nil { return "", err } - log.Debugf("Created server %s on Scaleway", serverID) - return serverID, nil + attachVolumeRequest := &instance.AttachVolumeRequest{ + ServerID: serverResp.Server.ID, + VolumeID: volumeResp.Volume.ID, + } + + _, err = s.instanceAPI.AttachVolume(attachVolumeRequest) + if err != nil { + return "", nil + } + + log.Debugf("Created server %s on Scaleway", serverResp.Server.ID) + return serverResp.Server.ID, nil } // GetSecondVolumeID returns the ID of the second volume of the server func (s *ScalewayClient) GetSecondVolumeID(instanceID string) (string, error) { - server, err := s.api.GetServer(instanceID) + getServerRequest := &instance.GetServerRequest{ + ServerID: instanceID, + } + + serverResp, err := s.instanceAPI.GetServer(getServerRequest) if err != nil { return "", err } - secondVolume, ok := server.Volumes["1"] + secondVolume, ok := serverResp.Server.Volumes["1"] if !ok { return "", errors.New("No second volume found") } - return secondVolume.Identifier, nil + return secondVolume.ID, nil } // BootInstanceAndWait boots and wait for instance to be booted func (s *ScalewayClient) BootInstanceAndWait(instanceID string) error { - err := s.api.PostServerAction(instanceID, "poweron") + serverActionRequest := &instance.ServerActionRequest{ + ServerID: instanceID, + Action: instance.ServerActionPoweron, + } + + _, err := s.instanceAPI.ServerAction(serverActionRequest) if err != nil { return err } @@ -156,14 +201,17 @@ func (s *ScalewayClient) BootInstanceAndWait(instanceID string) error { // code taken from scaleway-cli, could need some changes promise := make(chan bool) - var server *types.ScalewayServer - var currentState string + var server *instance.Server + var currentState instance.ServerState go func() { defer close(promise) for { - server, err = s.api.GetServer(instanceID) + serverResp, err := s.instanceAPI.GetServer(&instance.GetServerRequest{ + ServerID: instanceID, + }) + server = serverResp.Server if err != nil { promise <- false return @@ -173,20 +221,17 @@ func (s *ScalewayClient) BootInstanceAndWait(instanceID string) error { currentState = server.State } - if server.State == "running" { + if server.State == instance.ServerStateRunning { break } - if server.State == "stopped" { + if server.State == instance.ServerStateStopped { promise <- false return } time.Sleep(1 * time.Second) } - ip := server.PublicAddress.IP - if ip == "" && server.EnableIPV6 { - ip = fmt.Sprintf("[%s]", server.IPV6.Address) - } + ip := server.PublicIP.Address.String() dest := fmt.Sprintf("%s:22", ip) for { conn, err := net.Dial("tcp", dest) @@ -232,7 +277,16 @@ func getSSHAuth(sshKeyPath string) (ssh.Signer, error) { } signer, err := ssh.ParsePrivateKey(buf) if err != nil { - return nil, err + fmt.Print("Enter ssh key passphrase: ") + bytePassword, err := terminal.ReadPassword(int(syscall.Stdin)) + if err != nil { + return nil, err + } + signer, err := sshkeys.ParseEncryptedPrivateKey(buf, bytePassword) + if err != nil { + return nil, err + } + return signer, nil } return signer, err } @@ -242,10 +296,13 @@ func (s *ScalewayClient) CopyImageToInstance(instanceID, path, sshKeyPath string _, base := filepath.Split(path) s.fileName = base - server, err := s.api.GetServer(instanceID) + serverResp, err := s.instanceAPI.GetServer(&instance.GetServerRequest{ + ServerID: instanceID, + }) if err != nil { return err } + server := serverResp.Server signer, err := getSSHAuth(sshKeyPath) if err != nil { @@ -260,7 +317,7 @@ func (s *ScalewayClient) CopyImageToInstance(instanceID, path, sshKeyPath string HostKeyCallback: ssh.InsecureIgnoreHostKey(), // TODO validate server before? } - client, err := ssh.Dial("tcp", server.PublicAddress.IP+":22", s.sshConfig) // TODO remove hardocoded port? + client, err := ssh.Dial("tcp", server.PublicIP.Address.String()+":22", s.sshConfig) // TODO remove hardocoded port? if err != nil { return err } @@ -303,12 +360,16 @@ func (s *ScalewayClient) CopyImageToInstance(instanceID, path, sshKeyPath string // WriteImageToVolume does a dd command on the remote instance via ssh func (s *ScalewayClient) WriteImageToVolume(instanceID, deviceName string) error { - server, err := s.api.GetServer(instanceID) + serverResp, err := s.instanceAPI.GetServer(&instance.GetServerRequest{ + ServerID: instanceID, + }) if err != nil { return err } - client, err := ssh.Dial("tcp", server.PublicAddress.IP+":22", s.sshConfig) // TODO remove hardcoded port + use the same dial as before? + server := serverResp.Server + + client, err := ssh.Dial("tcp", server.PublicIP.Address.String()+":22", s.sshConfig) // TODO remove hardcoded port + use the same dial as before? if err != nil { return err } @@ -350,14 +411,12 @@ func (s *ScalewayClient) WriteImageToVolume(instanceID, deviceName string) error // TerminateInstance terminates the instance and wait for termination func (s *ScalewayClient) TerminateInstance(instanceID string) error { - server, err := s.api.GetServer(instanceID) - if err != nil { - return err - } - log.Debugf("Shutting down server %s", instanceID) - err = s.api.PostServerAction(server.Identifier, "poweroff") + _, err := s.instanceAPI.ServerAction(&instance.ServerActionRequest{ + ServerID: instanceID, + Action: instance.ServerActionPoweroff, + }) if err != nil { return err } @@ -365,19 +424,24 @@ func (s *ScalewayClient) TerminateInstance(instanceID string) error { // code taken from scaleway-cli time.Sleep(10 * time.Second) - var currentState string + var currentState instance.ServerState log.Debugf("Waiting for server to shutdown") for { - server, err = s.api.GetServer(instanceID) + serverResp, err := s.instanceAPI.GetServer(&instance.GetServerRequest{ + ServerID: instanceID, + }) if err != nil { return err } + + server := serverResp.Server + if currentState != server.State { currentState = server.State } - if server.State == "stopped" { + if server.State == instance.ServerStateStopped { break } time.Sleep(1 * time.Second) @@ -387,51 +451,76 @@ func (s *ScalewayClient) TerminateInstance(instanceID string) error { // CreateScalewayImage creates the image and delete old image and snapshot if same name func (s *ScalewayClient) CreateScalewayImage(instanceID, volumeID, name string) error { - oldImage, err := s.api.GetImageID(name, "x86_64") + oldImageID, err := s.getImageID(name, defaultScalewayCommercialType, defaultArch) if err == nil { - err = s.api.DeleteImage(oldImage.Identifier) + log.Debugf("deleting image %s", oldImageID) + err = s.instanceAPI.DeleteImage(&instance.DeleteImageRequest{ + ImageID: oldImageID, + }) if err != nil { return err } } - oldSnapshot, err := s.api.GetSnapshotID(name) + oldSnapshotsResp, err := s.instanceAPI.ListSnapshots(&instance.ListSnapshotsRequest{ + Name: &name, + }, scw.WithAllPages()) if err == nil { - err := s.api.DeleteSnapshot(oldSnapshot) - if err != nil { - return err + for _, oldSnapshot := range oldSnapshotsResp.Snapshots { + log.Debugf("deleting snapshot %s", oldSnapshot.ID) + err = s.instanceAPI.DeleteSnapshot(&instance.DeleteSnapshotRequest{ + SnapshotID: oldSnapshot.ID, + }) + if err != nil { + return err + } } } - snapshotID, err := s.api.PostSnapshot(volumeID, name) + log.Debugf("creating snapshot %s with volume %s", name, volumeID) + snapshotResp, err := s.instanceAPI.CreateSnapshot(&instance.CreateSnapshotRequest{ + VolumeID: volumeID, + Name: name, + }) if err != nil { return err } - imageID, err := s.api.PostImage(snapshotID, name, "", "x86_64") // TODO remove hardcoded arch + log.Debugf("creating image %s with snapshot %s", name, snapshotResp.Snapshot.ID) + imageResp, err := s.instanceAPI.CreateImage(&instance.CreateImageRequest{ + Name: name, + Arch: instance.Arch(defaultArch), + RootVolume: snapshotResp.Snapshot.ID, + }) if err != nil { return err } - log.Infof("Image %s with ID %s created", name, imageID) + log.Infof("Image %s with ID %s created", name, imageResp.Image.ID) return nil } // DeleteInstanceAndVolumes deletes the instance and the volumes attached func (s *ScalewayClient) DeleteInstanceAndVolumes(instanceID string) error { - server, err := s.api.GetServer(instanceID) + serverResp, err := s.instanceAPI.GetServer(&instance.GetServerRequest{ + ServerID: instanceID, + }) if err != nil { return err } - err = s.api.DeleteServer(instanceID) + err = s.instanceAPI.DeleteServer(&instance.DeleteServerRequest{ + ServerID: instanceID, + }) if err != nil { return err } - for _, volume := range server.Volumes { - err = s.api.DeleteVolume(volume.Identifier) + for _, volume := range serverResp.Server.Volumes { + err = s.instanceAPI.DeleteVolume(&instance.DeleteVolumeRequest{ + VolumeID: volume.ID, + }) if err != nil { return err } @@ -445,33 +534,38 @@ func (s *ScalewayClient) DeleteInstanceAndVolumes(instanceID string) error { // CreateLinuxkitInstance creates an instance with the given linuxkit image func (s *ScalewayClient) CreateLinuxkitInstance(instanceName, imageName, instanceType string) (string, error) { // get the image ID - image, err := s.api.GetImageID(imageName, "x86_64") // TODO fix arch and use from args + imageResp, err := s.instanceAPI.ListImages(&instance.ListImagesRequest{ + Name: &imageName, + }) if err != nil { return "", err } - imageID := image.Identifier + if len(imageResp.Images) != 1 { + return "", fmt.Errorf("Image %s not found or found multiple times", imageName) + } + imageID := imageResp.Images[0].ID - var serverDefinition types.ScalewayServerDefinition - - serverDefinition.Name = instanceName - serverDefinition.Image = &imageID - serverDefinition.CommercialType = instanceType - serverDefinition.BootType = "local" - - log.Debugf("Creating volume on Scaleway") - - log.Debugf("Creating server %s on Scaleway", serverDefinition.Name) - serverID, err := s.api.PostServer(serverDefinition) + log.Debugf("Creating server %s on Scaleway", instanceName) + serverResp, err := s.instanceAPI.CreateServer(&instance.CreateServerRequest{ + Name: instanceName, + DynamicIPRequired: true, + CommercialType: instanceType, + Image: imageID, + BootType: instance.ServerBootTypeLocal, + }) if err != nil { return "", err } - return serverID, nil + return serverResp.Server.ID, nil } // BootInstance boots the specified instance, and don't wait func (s *ScalewayClient) BootInstance(instanceID string) error { - err := s.api.PostServerAction(instanceID, "poweron") + _, err := s.instanceAPI.ServerAction(&instance.ServerActionRequest{ + ServerID: instanceID, + Action: instance.ServerActionPoweron, + }) if err != nil { return err } @@ -481,7 +575,7 @@ func (s *ScalewayClient) BootInstance(instanceID string) error { // ConnectSerialPort connects to the serial port of the instance func (s *ScalewayClient) ConnectSerialPort(instanceID string) error { var gottyURL string - switch s.region { + switch s.zone { case "par1": gottyURL = "https://tty-par1.scaleway.com/v2/" case "ams1": @@ -490,7 +584,7 @@ func (s *ScalewayClient) ConnectSerialPort(instanceID string) error { return errors.New("Instance have no region") } - fullURL := fmt.Sprintf("%s?arg=%s&arg=%s", gottyURL, s.api.Token, instanceID) + fullURL := fmt.Sprintf("%s?arg=%s&arg=%s", gottyURL, s.secretKey, instanceID) log.Debugf("Connection to %s", fullURL) gottyClient, err := gotty.NewClient(fullURL) diff --git a/src/cmd/linuxkit/vendor.conf b/src/cmd/linuxkit/vendor.conf index 27eb18312..16cfc2e84 100644 --- a/src/cmd/linuxkit/vendor.conf +++ b/src/cmd/linuxkit/vendor.conf @@ -10,6 +10,7 @@ github.com/containerd/containerd v1.1.2 github.com/containerd/continuity d8fb8589b0e8e85b8c8bbaa8840226d0dfeb7371 github.com/creack/goselect 58854f77ee8d858ce751b0a9bcc5533fef7bfa9e github.com/davecgh/go-spew v1.1.0 +github.com/dchest/bcrypt_pbkdf 83f37f9c154a678179d11e218bff73ebe5717f99 github.com/dgrijalva/jwt-go 6c8dedd55f8a2e41f605de6d5d66e51ed1f299fc github.com/docker/cli v18.06.0-ce github.com/docker/distribution 83389a148052d74ac602f5f1d62f86ff2f3c4aa5 @@ -54,7 +55,8 @@ github.com/radu-matei/azure-sdk-for-go 3b12823551999669c9a325a32472508e0af7978e github.com/radu-matei/azure-vhd-utils e52754d5569d2a643a7775f72ff2a6cf524f4c25 github.com/renstrom/fuzzysearch 7a8f9a1c4bed53899ecd512daeaf8207cc454156 github.com/rn/iso9660wrap baf8d62ad3155152b488d5ff9d4f2b9bb0d6986a -github.com/scaleway/go-scaleway f8c3b31c65867b4cb96b3bd41e574c33fd1d69ae +github.com/ScaleFT/sshkeys 82451a80368171b074c7129d43b47fc2773f6e9f +github.com/scaleway/scaleway-sdk-go 20b731586975c078d9c2d7dd0002127e9e9cdef2 github.com/sirupsen/logrus v1.0.3 github.com/spf13/cobra v0.0.3 github.com/spf13/pflag v1.0.1 diff --git a/src/cmd/linuxkit/vendor/github.com/ScaleFT/sshkeys/LICENSE b/src/cmd/linuxkit/vendor/github.com/ScaleFT/sshkeys/LICENSE new file mode 100644 index 000000000..d64569567 --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/ScaleFT/sshkeys/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/src/cmd/linuxkit/vendor/github.com/ScaleFT/sshkeys/NOTICE b/src/cmd/linuxkit/vendor/github.com/ScaleFT/sshkeys/NOTICE new file mode 100644 index 000000000..21302db95 --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/ScaleFT/sshkeys/NOTICE @@ -0,0 +1,10 @@ +sshkeys +Copyright 2017 ScaleFT, Inc + +This product includes software developed at ScaleFT, Inc. +(https://www.scaleft.com/). + +Portions of this software are derived from +https://github.com/golang/crypto/blob/master/ssh/keys.go + +Copyright (c) 2009 The Go Authors. All rights reserved. diff --git a/src/cmd/linuxkit/vendor/github.com/ScaleFT/sshkeys/README.md b/src/cmd/linuxkit/vendor/github.com/ScaleFT/sshkeys/README.md new file mode 100644 index 000000000..89d8c21db --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/ScaleFT/sshkeys/README.md @@ -0,0 +1,14 @@ +# sshkeys + +[![GoDoc](https://godoc.org/github.com/ScaleFT/sshkeys?status.svg)](https://godoc.org/github.com/ScaleFT/sshkeys) +[![Build Status](https://travis-ci.org/ScaleFT/sshkeys.svg?branch=master)](https://travis-ci.org/ScaleFT/sshkeys) + +`sshkeys` provides utilities for parsing and marshalling cryptographic keys used for SSH, in both cleartext and encrypted formats. + +[ssh.ParseRawPrivateKey](https://godoc.org/golang.org/x/crypto/ssh#ParseRawPrivateKey) only supports parsing a subset of the formats `sshkeys` supports, does not support parsing encrypted private keys, and does not support marshalling. + +## Supported Formats + +* OpenSSH's [PROTOCOL.key](https://github.com/openssh/openssh-portable/blob/master/PROTOCOL.key) for RSA and ED25519 keys. +* OpenSSH version >= 7.6 using aes256-ctr encryption +* "Classic" PEM containing RSA (PKCS#1), DSA (OpenSSL), and ECDSA private keys. diff --git a/src/cmd/linuxkit/vendor/github.com/ScaleFT/sshkeys/marshal.go b/src/cmd/linuxkit/vendor/github.com/ScaleFT/sshkeys/marshal.go new file mode 100644 index 000000000..5fdf209c7 --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/ScaleFT/sshkeys/marshal.go @@ -0,0 +1,275 @@ +package sshkeys + +import ( + "crypto/aes" + "crypto/cipher" + "crypto/dsa" + "crypto/ecdsa" + "crypto/rand" + "crypto/rsa" + "crypto/x509" + "encoding/asn1" + "encoding/pem" + "fmt" + "math/big" + mrand "math/rand" + + "github.com/dchest/bcrypt_pbkdf" + "golang.org/x/crypto/ed25519" + "golang.org/x/crypto/ssh" +) + +// Format of private key to use when Marshaling. +type Format int + +const ( + // FormatOpenSSHv1 encodes a private key using OpenSSH's PROTOCOL.key format: https://github.com/openssh/openssh-portable/blob/master/PROTOCOL.key + FormatOpenSSHv1 Format = iota + // FormatClassicPEM encodes private keys in PEM, with a key-specific encoding, as used by OpenSSH. + FormatClassicPEM +) + +// MarshalOptions provides the Marshal function format and encryption options. +type MarshalOptions struct { + // Passphrase to encrypt private key with, if nil, the key will not be encrypted. + Passphrase []byte + // Format to encode the private key in. + Format Format +} + +// Marshal converts a private key into an optionally encrypted format. +func Marshal(pk interface{}, opts *MarshalOptions) ([]byte, error) { + switch opts.Format { + case FormatOpenSSHv1: + return marshalOpenssh(pk, opts) + case FormatClassicPEM: + return marshalPem(pk, opts) + default: + return nil, fmt.Errorf("sshkeys: invalid format %d", opts.Format) + } +} + +func marshalPem(pk interface{}, opts *MarshalOptions) ([]byte, error) { + var err error + var plain []byte + var pemType string + + switch key := pk.(type) { + case *rsa.PrivateKey: + pemType = "RSA PRIVATE KEY" + plain = x509.MarshalPKCS1PrivateKey(key) + case *ecdsa.PrivateKey: + pemType = "EC PRIVATE KEY" + plain, err = x509.MarshalECPrivateKey(key) + if err != nil { + return nil, err + } + case *dsa.PrivateKey: + pemType = "DSA PRIVATE KEY" + plain, err = marshalDSAPrivateKey(key) + if err != nil { + return nil, err + } + case *ed25519.PrivateKey: + return nil, fmt.Errorf("sshkeys: ed25519 keys must be marshaled with FormatOpenSSHv1") + default: + return nil, fmt.Errorf("sshkeys: unsupported key type %T", pk) + } + + if len(opts.Passphrase) > 0 { + block, err := x509.EncryptPEMBlock(rand.Reader, pemType, plain, opts.Passphrase, x509.PEMCipherAES128) + if err != nil { + return nil, err + } + return pem.EncodeToMemory(block), nil + } + + return pem.EncodeToMemory(&pem.Block{ + Type: pemType, + Bytes: plain, + }), nil +} + +type dsaOpenssl struct { + Version int + P *big.Int + Q *big.Int + G *big.Int + Pub *big.Int + Priv *big.Int +} + +// https://github.com/golang/crypto/blob/master/ssh/keys.go#L793-L804 +func marshalDSAPrivateKey(pk *dsa.PrivateKey) ([]byte, error) { + k := dsaOpenssl{ + Version: 0, + P: pk.P, + Q: pk.Q, + G: pk.G, + Pub: pk.Y, + Priv: pk.X, + } + + return asn1.Marshal(k) +} + +const opensshv1Magic = "openssh-key-v1" + +type opensshHeader struct { + CipherName string + KdfName string + KdfOpts string + NumKeys uint32 + PubKey string + PrivKeyBlock string +} + +type opensshKey struct { + Check1 uint32 + Check2 uint32 + Keytype string + Rest []byte `ssh:"rest"` +} + +type opensshRsa struct { + N *big.Int + E *big.Int + D *big.Int + Iqmp *big.Int + P *big.Int + Q *big.Int + Comment string + Pad []byte `ssh:"rest"` +} + +type opensshED25519 struct { + Pub []byte + Priv []byte + Comment string + Pad []byte `ssh:"rest"` +} + +func padBytes(data []byte, blocksize int) []byte { + if blocksize != 0 { + var i byte + for i = byte(1); len(data)%blocksize != 0; i++ { + data = append(data, i&0xFF) + } + } + return data +} + +func marshalOpenssh(pk interface{}, opts *MarshalOptions) ([]byte, error) { + var blocksize int + var keylen int + + out := opensshHeader{ + CipherName: "none", + KdfName: "none", + KdfOpts: "", + NumKeys: 1, + PubKey: "", + } + + if len(opts.Passphrase) > 0 { + out.CipherName = "aes256-cbc" + out.KdfName = "bcrypt" + keylen = keySizeAES256 + blocksize = aes.BlockSize + } + + check := mrand.Uint32() + pk1 := opensshKey{ + Check1: check, + Check2: check, + } + + switch key := pk.(type) { + case *rsa.PrivateKey: + k := &opensshRsa{ + N: key.N, + E: big.NewInt(int64(key.E)), + D: key.D, + Iqmp: key.Precomputed.Qinv, + P: key.Primes[0], + Q: key.Primes[1], + Comment: "", + } + + data := ssh.Marshal(k) + pk1.Keytype = ssh.KeyAlgoRSA + pk1.Rest = data + publicKey, err := ssh.NewPublicKey(&key.PublicKey) + if err != nil { + return nil, err + } + out.PubKey = string(publicKey.Marshal()) + + case ed25519.PrivateKey: + k := opensshED25519{ + Pub: key.Public().(ed25519.PublicKey), + Priv: key, + } + data := ssh.Marshal(k) + pk1.Keytype = ssh.KeyAlgoED25519 + pk1.Rest = data + + publicKey, err := ssh.NewPublicKey(key.Public()) + if err != nil { + return nil, err + } + out.PubKey = string(publicKey.Marshal()) + default: + return nil, fmt.Errorf("sshkeys: unsupported key type %T", pk) + } + + if len(opts.Passphrase) > 0 { + rounds := 16 + ivlen := blocksize + salt := make([]byte, blocksize) + _, err := rand.Read(salt) + if err != nil { + return nil, err + } + + kdfdata, err := bcrypt_pbkdf.Key(opts.Passphrase, salt, rounds, keylen+ivlen) + if err != nil { + return nil, err + } + iv := kdfdata[keylen : ivlen+keylen] + aeskey := kdfdata[0:keylen] + + block, err := aes.NewCipher(aeskey) + if err != nil { + return nil, err + } + + pkblock := padBytes(ssh.Marshal(pk1), blocksize) + + cbc := cipher.NewCBCEncrypter(block, iv) + cbc.CryptBlocks(pkblock, pkblock) + + out.PrivKeyBlock = string(pkblock) + + var opts struct { + Salt []byte + Rounds uint32 + } + + opts.Salt = salt + opts.Rounds = uint32(rounds) + + out.KdfOpts = string(ssh.Marshal(&opts)) + } else { + out.PrivKeyBlock = string(ssh.Marshal(pk1)) + } + + outBytes := []byte(opensshv1Magic) + outBytes = append(outBytes, 0) + outBytes = append(outBytes, ssh.Marshal(out)...) + block := &pem.Block{ + Type: "OPENSSH PRIVATE KEY", + Bytes: outBytes, + } + return pem.EncodeToMemory(block), nil +} diff --git a/src/cmd/linuxkit/vendor/github.com/ScaleFT/sshkeys/parse.go b/src/cmd/linuxkit/vendor/github.com/ScaleFT/sshkeys/parse.go new file mode 100644 index 000000000..7ccc2f8f3 --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/ScaleFT/sshkeys/parse.go @@ -0,0 +1,244 @@ +// Portions of this file are based on https://github.com/golang/crypto/blob/master/ssh/keys.go +// +// Copyright 2012 The Go 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 sshkeys + +import ( + "bytes" + "crypto/aes" + "crypto/cipher" + "crypto/rsa" + "crypto/x509" + "encoding/pem" + "errors" + "fmt" + "math/big" + "strings" + + "github.com/dchest/bcrypt_pbkdf" + "golang.org/x/crypto/ed25519" + "golang.org/x/crypto/ssh" +) + +// ErrIncorrectPassword is returned when the supplied passphrase was not correct for an encrypted private key. +var ErrIncorrectPassword = errors.New("sshkeys: Invalid Passphrase") + +const keySizeAES256 = 32 + +// ParseEncryptedPrivateKey returns a Signer from an encrypted private key. It supports +// the same keys as ParseEncryptedRawPrivateKey. +func ParseEncryptedPrivateKey(data []byte, passphrase []byte) (ssh.Signer, error) { + key, err := ParseEncryptedRawPrivateKey(data, passphrase) + if err != nil { + return nil, err + } + + return ssh.NewSignerFromKey(key) +} + +// ParseEncryptedRawPrivateKey returns a private key from an encrypted private key. It +// supports RSA (PKCS#1 or OpenSSH), DSA (OpenSSL), and ECDSA private keys. +// +// ErrIncorrectPassword will be returned if the supplied passphrase is wrong, +// but some formats like RSA in PKCS#1 detecting a wrong passphrase is difficult, +// and other parse errors may be returned. +func ParseEncryptedRawPrivateKey(data []byte, passphrase []byte) (interface{}, error) { + var err error + + block, _ := pem.Decode(data) + if block == nil { + return nil, errors.New("no PEM block found") + } + + if x509.IsEncryptedPEMBlock(block) { + data, err = x509.DecryptPEMBlock(block, passphrase) + if err == x509.IncorrectPasswordError { + return nil, ErrIncorrectPassword + } + if err != nil { + return nil, err + } + } else { + data = block.Bytes + } + + switch block.Type { + case "RSA PRIVATE KEY": + pk, err := x509.ParsePKCS1PrivateKey(data) + if err != nil { + // The Algos for PEM Encryption do not include strong message authentication, + // so sometimes DecryptPEMBlock works, but ParsePKCS1PrivateKey fails with an asn1 error. + // We are just catching the most common prefix here... + if strings.HasPrefix(err.Error(), "asn1: structure error") { + return nil, ErrIncorrectPassword + } + return nil, err + } + return pk, nil + case "EC PRIVATE KEY": + return x509.ParseECPrivateKey(data) + case "DSA PRIVATE KEY": + return ssh.ParseDSAPrivateKey(data) + case "OPENSSH PRIVATE KEY": + return parseOpenSSHPrivateKey(data, passphrase) + default: + return nil, fmt.Errorf("sshkeys: unsupported key type %q", block.Type) + } +} + +func parseOpenSSHPrivateKey(data []byte, passphrase []byte) (interface{}, error) { + magic := append([]byte(opensshv1Magic), 0) + if !bytes.Equal(magic, data[0:len(magic)]) { + return nil, errors.New("sshkeys: invalid openssh private key format") + } + remaining := data[len(magic):] + + w := opensshHeader{} + + if err := ssh.Unmarshal(remaining, &w); err != nil { + return nil, err + } + + if w.NumKeys != 1 { + return nil, fmt.Errorf("sshkeys: NumKeys must be 1: %d", w.NumKeys) + } + + var privateKeyBytes []byte + var encrypted bool + + switch { + // OpenSSH supports bcrypt KDF w/ AES256-CBC or AES256-CTR mode + case w.KdfName == "bcrypt" && w.CipherName == "aes256-cbc": + iv, block, err := extractBcryptIvBlock(passphrase, w) + if err != nil { + return nil, err + } + + cbc := cipher.NewCBCDecrypter(block, iv) + privateKeyBytes = []byte(w.PrivKeyBlock) + cbc.CryptBlocks(privateKeyBytes, privateKeyBytes) + + encrypted = true + + case w.KdfName == "bcrypt" && w.CipherName == "aes256-ctr": + iv, block, err := extractBcryptIvBlock(passphrase, w) + if err != nil { + return nil, err + } + + stream := cipher.NewCTR(block, iv) + privateKeyBytes = []byte(w.PrivKeyBlock) + stream.XORKeyStream(privateKeyBytes, privateKeyBytes) + + encrypted = true + + case w.KdfName == "none" && w.CipherName == "none": + privateKeyBytes = []byte(w.PrivKeyBlock) + + default: + return nil, fmt.Errorf("sshkeys: unknown Cipher/KDF: %s:%s", w.CipherName, w.KdfName) + } + + pk1 := opensshKey{} + + if err := ssh.Unmarshal(privateKeyBytes, &pk1); err != nil { + if encrypted { + return nil, ErrIncorrectPassword + } + return nil, err + } + + if pk1.Check1 != pk1.Check2 { + return nil, ErrIncorrectPassword + } + + // we only handle ed25519 and rsa keys currently + switch pk1.Keytype { + case ssh.KeyAlgoRSA: + // https://github.com/openssh/openssh-portable/blob/V_7_4_P1/sshkey.c#L2760-L2773 + key := opensshRsa{} + + err := ssh.Unmarshal(pk1.Rest, &key) + if err != nil { + return nil, err + } + + for i, b := range key.Pad { + if int(b) != i+1 { + return nil, errors.New("sshkeys: padding not as expected") + } + } + + pk := &rsa.PrivateKey{ + PublicKey: rsa.PublicKey{ + N: key.N, + E: int(key.E.Int64()), + }, + D: key.D, + Primes: []*big.Int{key.P, key.Q}, + } + + err = pk.Validate() + if err != nil { + return nil, err + } + + pk.Precompute() + + return pk, nil + case ssh.KeyAlgoED25519: + key := opensshED25519{} + + err := ssh.Unmarshal(pk1.Rest, &key) + if err != nil { + return nil, err + } + + if len(key.Priv) != ed25519.PrivateKeySize { + return nil, errors.New("sshkeys: private key unexpected length") + } + + for i, b := range key.Pad { + if int(b) != i+1 { + return nil, errors.New("sshkeys: padding not as expected") + } + } + + pk := ed25519.PrivateKey(make([]byte, ed25519.PrivateKeySize)) + copy(pk, key.Priv) + return pk, nil + default: + return nil, errors.New("sshkeys: unhandled key type") + } +} + +func extractBcryptIvBlock(passphrase []byte, w opensshHeader) ([]byte, cipher.Block, error) { + cipherKeylen := keySizeAES256 + cipherIvLen := aes.BlockSize + + var opts struct { + Salt []byte + Rounds uint32 + } + + if err := ssh.Unmarshal([]byte(w.KdfOpts), &opts); err != nil { + return nil, nil, err + } + kdfdata, err := bcrypt_pbkdf.Key(passphrase, opts.Salt, int(opts.Rounds), cipherKeylen+cipherIvLen) + if err != nil { + return nil, nil, err + } + + iv := kdfdata[cipherKeylen : cipherIvLen+cipherKeylen] + aeskey := kdfdata[0:cipherKeylen] + block, err := aes.NewCipher(aeskey) + + if err != nil { + return nil, nil, err + } + + return iv, block, nil +} diff --git a/src/cmd/linuxkit/vendor/github.com/dchest/bcrypt_pbkdf/LICENSE b/src/cmd/linuxkit/vendor/github.com/dchest/bcrypt_pbkdf/LICENSE new file mode 100644 index 000000000..b99c5e3b9 --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/dchest/bcrypt_pbkdf/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2014 Dmitry Chestnykh +Copyright (c) 2010 The Go 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 +OWNER 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/src/cmd/linuxkit/vendor/github.com/dchest/bcrypt_pbkdf/README b/src/cmd/linuxkit/vendor/github.com/dchest/bcrypt_pbkdf/README new file mode 100644 index 000000000..8ce687432 --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/dchest/bcrypt_pbkdf/README @@ -0,0 +1,21 @@ +Go implementation of bcrypt_pbkdf(3) from OpenBSD +(a variant of PBKDF2 with bcrypt-based PRF). + + +USAGE + + func Key(password, salt []byte, rounds, keyLen int) ([]byte, error) + + + Key derives a key from the password, salt and rounds count, returning a + []byte of length keyLen that can be used as cryptographic key. + + Remember to get a good random salt of at least 16 bytes. Using a higher + rounds count will increase the cost of an exhaustive search but will also + make derivation proportionally slower. + + +REFERENCES + +* http://www.tedunangst.com/flak/post/bcrypt-pbkdf +* http://cvsweb.openbsd.org/cgi-bin/cvsweb/src/lib/libutil/bcrypt_pbkdf.c diff --git a/src/cmd/linuxkit/vendor/github.com/dchest/bcrypt_pbkdf/bcrypt_pbkdf.go b/src/cmd/linuxkit/vendor/github.com/dchest/bcrypt_pbkdf/bcrypt_pbkdf.go new file mode 100644 index 000000000..60bba1c36 --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/dchest/bcrypt_pbkdf/bcrypt_pbkdf.go @@ -0,0 +1,97 @@ +// Copyright 2014 Dmitry Chestnykh. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package bcrypt_pbkdf implements password-based key derivation function based +// on bcrypt compatible with bcrypt_pbkdf(3) from OpenBSD. +package bcrypt_pbkdf + +import ( + "crypto/sha512" + "errors" + + // NOTE! Requires blowfish package version from Aug 1, 2014 or later. + // Will produce incorrect results if the package is older. + // See commit message for details: http://goo.gl/wx6g8O + "golang.org/x/crypto/blowfish" +) + +// Key derives a key from the password, salt and rounds count, returning a +// []byte of length keyLen that can be used as cryptographic key. +// +// Remember to get a good random salt of at least 16 bytes. Using a higher +// rounds count will increase the cost of an exhaustive search but will also +// make derivation proportionally slower. +func Key(password, salt []byte, rounds, keyLen int) ([]byte, error) { + if rounds < 1 { + return nil, errors.New("bcrypt_pbkdf: number of rounds is too small") + } + if len(password) == 0 { + return nil, errors.New("bcrypt_pbkdf: empty password") + } + if len(salt) == 0 || len(salt) > 1<<20 { + return nil, errors.New("bcrypt_pbkdf: bad salt length") + } + if keyLen > 1024 { + return nil, errors.New("bcrypt_pbkdf: keyLen is too large") + } + var shapass, shasalt [sha512.Size]byte + var out, tmp [32]byte + var cnt [4]byte + + numBlocks := (keyLen + len(out) - 1) / len(out) + key := make([]byte, numBlocks*len(out)) + + h := sha512.New() + h.Write(password) + h.Sum(shapass[:0]) + + for block := 1; block <= numBlocks; block++ { + h.Reset() + h.Write(salt) + cnt[0] = byte(block >> 24) + cnt[1] = byte(block >> 16) + cnt[2] = byte(block >> 8) + cnt[3] = byte(block) + h.Write(cnt[:]) + bcryptHash(tmp[:], shapass[:], h.Sum(shasalt[:0])) + copy(out[:], tmp[:]) + + for i := 2; i <= rounds; i++ { + h.Reset() + h.Write(tmp[:]) + bcryptHash(tmp[:], shapass[:], h.Sum(shasalt[:0])) + for j := 0; j < len(out); j++ { + out[j] ^= tmp[j] + } + } + + for i, v := range out { + key[i*numBlocks+(block-1)] = v + } + } + return key[:keyLen], nil +} + +var magic = []byte("OxychromaticBlowfishSwatDynamite") + +func bcryptHash(out, shapass, shasalt []byte) { + c, err := blowfish.NewSaltedCipher(shapass, shasalt) + if err != nil { + panic(err) + } + for i := 0; i < 64; i++ { + blowfish.ExpandKey(shasalt, c) + blowfish.ExpandKey(shapass, c) + } + copy(out[:], magic) + for i := 0; i < 32; i += 8 { + for j := 0; j < 64; j++ { + c.Encrypt(out[i:i+8], out[i:i+8]) + } + } + // Swap bytes due to different endianness. + for i := 0; i < 32; i += 4 { + out[i+3], out[i+2], out[i+1], out[i] = out[i], out[i+1], out[i+2], out[i+3] + } +} diff --git a/src/cmd/linuxkit/vendor/github.com/moul/anonuuid/LICENSE b/src/cmd/linuxkit/vendor/github.com/moul/anonuuid/LICENSE deleted file mode 100644 index 492e2c629..000000000 --- a/src/cmd/linuxkit/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/src/cmd/linuxkit/vendor/github.com/moul/anonuuid/README.md b/src/cmd/linuxkit/vendor/github.com/moul/anonuuid/README.md deleted file mode 100644 index 1012f4372..000000000 --- a/src/cmd/linuxkit/vendor/github.com/moul/anonuuid/README.md +++ /dev/null @@ -1,171 +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/src/cmd/linuxkit/vendor/github.com/renstrom/fuzzysearch/LICENSE b/src/cmd/linuxkit/vendor/github.com/renstrom/fuzzysearch/LICENSE deleted file mode 100644 index 9cc753370..000000000 --- a/src/cmd/linuxkit/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/src/cmd/linuxkit/vendor/github.com/renstrom/fuzzysearch/README.md b/src/cmd/linuxkit/vendor/github.com/renstrom/fuzzysearch/README.md deleted file mode 100644 index e8f0cd598..000000000 --- a/src/cmd/linuxkit/vendor/github.com/renstrom/fuzzysearch/README.md +++ /dev/null @@ -1,43 +0,0 @@ -# Fuzzy Search - -[![Build Status](https://img.shields.io/travis/renstrom/fuzzysearch.svg?style=flat-square)](https://travis-ci.org/renstrom/fuzzysearch) -[![Godoc](https://img.shields.io/badge/godoc-reference-blue.svg?style=flat-square)](https://godoc.org/github.com/renstrom/fuzzysearch/fuzzy) - -Inspired by _[bevacqua/fuzzysearch][1]_, a fuzzy matching library written in JavaScript. But contains some extras like ranking using _[Levenshtein distance][2]_ (see [`RankMatch()`](https://godoc.org/github.com/renstrom/fuzzysearch/fuzzy#RankMatch)) and finding matches in a list of words (see [`Find()`](https://godoc.org/github.com/renstrom/fuzzysearch/fuzzy#Find)). - -Fuzzy searching allows for flexibly matching a string with partial input, useful for filtering data very quickly based on lightweight user input. - -The current implementation uses the algorithm suggested by Mr. Aleph, a russian compiler engineer working at V8. - -## Usage - -```go -fuzzy.Match("twl", "cartwheel") // true -fuzzy.Match("cart", "cartwheel") // true -fuzzy.Match("cw", "cartwheel") // true -fuzzy.Match("ee", "cartwheel") // true -fuzzy.Match("art", "cartwheel") // true -fuzzy.Match("eeel", "cartwheel") // false -fuzzy.Match("dog", "cartwheel") // false - -fuzzy.RankMatch("kitten", "sitting") // 3 - -words := []string{"cartwheel", "foobar", "wheel", "baz"} -fuzzy.Find("whl", words) // [cartwheel wheel] - -fuzzy.RankFind("whl", words) // [{whl cartwheel 6} {whl wheel 2}] -``` - -You can sort the result of a `fuzzy.RankFind()` call using the [`sort`](https://golang.org/pkg/sort/) package in the standard library: - -```go -matches := fuzzy.RankFind("whl", words) // [{whl cartwheel 6} {whl wheel 2}] -sort.Sort(matches) // [{whl wheel 2} {whl cartwheel 6}] -``` - -## License - -MIT - -[1]: https://github.com/bevacqua/fuzzysearch -[2]: http://en.wikipedia.org/wiki/Levenshtein_distance diff --git a/src/cmd/linuxkit/vendor/github.com/renstrom/fuzzysearch/fuzzy/fuzzy.go b/src/cmd/linuxkit/vendor/github.com/renstrom/fuzzysearch/fuzzy/fuzzy.go deleted file mode 100644 index 63277d51e..000000000 --- a/src/cmd/linuxkit/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/src/cmd/linuxkit/vendor/github.com/renstrom/fuzzysearch/fuzzy/levenshtein.go b/src/cmd/linuxkit/vendor/github.com/renstrom/fuzzysearch/fuzzy/levenshtein.go deleted file mode 100644 index 237923d34..000000000 --- a/src/cmd/linuxkit/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/src/cmd/linuxkit/vendor/github.com/scaleway/go-scaleway/LICENSE b/src/cmd/linuxkit/vendor/github.com/scaleway/go-scaleway/LICENSE deleted file mode 100644 index d4a2ec8f0..000000000 --- a/src/cmd/linuxkit/vendor/github.com/scaleway/go-scaleway/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2018 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/src/cmd/linuxkit/vendor/github.com/scaleway/go-scaleway/README.md b/src/cmd/linuxkit/vendor/github.com/scaleway/go-scaleway/README.md deleted file mode 100644 index cdf30e7a4..000000000 --- a/src/cmd/linuxkit/vendor/github.com/scaleway/go-scaleway/README.md +++ /dev/null @@ -1,27 +0,0 @@ -# Scaleway's API golang client - -[![GoDoc](https://godoc.org/github.com/scaleway/go-scaleway?status.svg)](https://godoc.org/github.com/scaleway/go-scaleway) - -This package contains facilities to play with the Scaleway API, it includes the following features: - -- dedicated configuration file containing credentials to deal with the API -- caching to resolve UUIDs without contacting the API - -## Links - -- [API documentation](https://developer.scaleway.com) -- [Official Python SDK](https://github.com/scaleway/python-scaleway) -- Projects using this SDK - - https://github.com/scaleway/scaleway-cli - - https://github.com/scaleway/devhub - - https://github.com/scaleway/docker-machine-driver-scaleway - - https://github.com/huseyin/docker-machine-driver-scaleway - - https://github.com/scaleway-community/scaleway-ubuntu-coreos/blob/master/overlay/usr/local/update-firewall/scw-api/cache.go - - https://github.com/pulcy/quark - - https://github.com/hex-sh/terraform-provider-scaleway - - https://github.com/tscolari/bosh-scaleway-cpi -- Other **golang** clients - - https://github.com/lalyos/onlabs - - https://github.com/meatballhat/packer-builder-onlinelabs - - https://github.com/nlamirault/go-scaleway - - https://github.com/golang/build/blob/master/cmd/scaleway/scaleway.go diff --git a/src/cmd/linuxkit/vendor/github.com/scaleway/go-scaleway/api.go b/src/cmd/linuxkit/vendor/github.com/scaleway/go-scaleway/api.go deleted file mode 100644 index a48bd5a4b..000000000 --- a/src/cmd/linuxkit/vendor/github.com/scaleway/go-scaleway/api.go +++ /dev/null @@ -1,2003 +0,0 @@ -// Copyright (C) 2018 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" - "time" - - "golang.org/x/sync/errgroup" - - "github.com/scaleway/go-scaleway/cache" - "github.com/scaleway/go-scaleway/logger" - "github.com/scaleway/go-scaleway/types" -) - -// 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 -) - -// types.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 *cache.ScalewayCache - - client *http.Client - verbose bool - computeAPI string - - Region string - // - logger.Logger -} - -// Newtypes.ScalewayAPI 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: logger.NewDefaultLogger(), - - // internal - client: &http.Client{}, - verbose: os.Getenv("SCW_VERBOSE_API") != "", - password: "", - userAgent: userAgent, - } - for _, option := range options { - option(s) - } - cache, err := cache.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 types.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<- types.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 types.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) (*[]types.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 types.ScalewayServers, 2) - for _, api := range apis { - g.Go(s.fetchServers(api, query, serverChan)) - } - - if err := g.Wait(); err != nil { - return nil, err - } - close(serverChan) - var servers types.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 -} - -// GetServer gets a server from the ScalewayAPI -func (s *ScalewayAPI) GetServer(serverID string) (*types.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 types.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 := types.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 types.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 types.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 types.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 types.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 := types.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 types.ScalewayOneSnapshot - - if err = json.Unmarshal(body, &snapshot); err != nil { - return "", err - } - // FIXME arch, owner, title - s.Cache.InsertSnapshot(snapshot.Snapshot.Identifier, "", "", 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 := types.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 types.ScalewayOneImage - - if err = json.Unmarshal(body, &image); err != nil { - return "", err - } - // FIXME region, arch, owner, title - s.Cache.InsertImage(image.Image.Identifier, "", image.Image.Arch, image.Image.Organization, image.Image.Name, "") - return image.Image.Identifier, nil -} - -// PostVolume creates a new volume -func (s *ScalewayAPI) PostVolume(definition types.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 types.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 types.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) (types.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) (types.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) (types.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) (types.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) (types.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() (*[]types.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 types.ScalewayImages - - if err = json.Unmarshal(body, &OrgaImages); err != nil { - return nil, err - } - - for _, orgaImage := range OrgaImages.Images { - images.Images = append(images.Images, types.MarketImage{ - Categories: []string{"MyImages"}, - CreationDate: orgaImage.CreationDate, - CurrentPublicVersion: orgaImage.Identifier, - ModificationDate: orgaImage.ModificationDate, - Name: orgaImage.Name, - Public: false, - MarketVersions: types.MarketVersions{ - Versions: []types.MarketVersionDefinition{ - { - CreationDate: orgaImage.CreationDate, - ID: orgaImage.Identifier, - ModificationDate: orgaImage.ModificationDate, - MarketLocalImages: types.MarketLocalImages{ - LocalImages: []types.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) (*types.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 types.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() (*[]types.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 types.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, "", "", snapshot.Organization, snapshot.Name) - } - return &snapshots.Snapshots, nil -} - -// GetSnapshot gets a snapshot from the ScalewayAPI -func (s *ScalewayAPI) GetSnapshot(snapshotID string) (*types.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 types.ScalewayOneSnapshot - - if err = json.Unmarshal(body, &oneSnapshot); err != nil { - return nil, err - } - // FIXME region, arch, owner, title - s.Cache.InsertSnapshot(oneSnapshot.Snapshot.Identifier, "", "", oneSnapshot.Snapshot.Organization, oneSnapshot.Snapshot.Name) - return &oneSnapshot.Snapshot, nil -} - -// GetVolumes gets the list of volumes from the ScalewayAPI -func (s *ScalewayAPI) GetVolumes() (*[]types.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 types.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, "", "", volume.Organization, volume.Name) - } - return &volumes.Volumes, nil -} - -// GetVolume gets a volume from the ScalewayAPI -func (s *ScalewayAPI) GetVolume(volumeID string) (*types.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 types.ScalewayOneVolume - - if err = json.Unmarshal(body, &oneVolume); err != nil { - return nil, err - } - // FIXME region, arch, owner, title - s.Cache.InsertVolume(oneVolume.Volume.Identifier, "", "", oneVolume.Volume.Organization, oneVolume.Volume.Name) - return &oneVolume.Volume, nil -} - -// GetBootscripts gets the list of bootscripts from the ScalewayAPI -func (s *ScalewayAPI) GetBootscripts() (*[]types.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 types.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, "", bootscript.Arch, bootscript.Organization, bootscript.Title) - } - return &bootscripts.Bootscripts, nil -} - -// GetBootscript gets a bootscript from the ScalewayAPI -func (s *ScalewayAPI) GetBootscript(bootscriptID string) (*types.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 types.ScalewayOneBootscript - - if err = json.Unmarshal(body, &oneBootscript); err != nil { - return nil, err - } - // FIXME region, arch, owner, title - s.Cache.InsertBootscript(oneBootscript.Bootscript.Identifier, "", 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) (*types.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 types.ScalewayUserdatas - - if err = json.Unmarshal(body, &userdatas); err != nil { - return nil, err - } - return &userdatas, nil -} - -// GetUserdata gets a specific userdata for a server -func (s *ScalewayAPI) GetUserdata(serverID, key string, metadata bool) (*types.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 types.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() (*[]types.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 types.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 - } - found := false - var tokens types.ScalewayGetTokens - - if err = json.Unmarshal(body, &tokens); err != nil { - return err - } - for _, token := range tokens.Tokens { - if token.ID == s.Token { - found = true - break - } - } - if !found { - return fmt.Errorf("Invalid token %v", s.Token) - } - 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 types.ScalewayTokensDefinition - - if err = json.Unmarshal(body, &token); err != nil { - return "", err - } - return token.Token.UserID, nil -} - -// GetOrganization returns Organization -func (s *ScalewayAPI) GetOrganization() (*types.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 types.ScalewayOrganizationsDefinition - - if err = json.Unmarshal(body, &data); err != nil { - return nil, err - } - return &data, nil -} - -// GetUser returns the user -func (s *ScalewayAPI) GetUser() (*types.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 types.ScalewayUsersDefinition - - if err = json.Unmarshal(body, &user); err != nil { - return nil, err - } - return &user.User, nil -} - -// GetPermissions returns the permissions -func (s *ScalewayAPI) GetPermissions() (*types.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 types.ScalewayPermissionDefinition - - if err = json.Unmarshal(body, &permissions); err != nil { - return nil, err - } - return &permissions, nil -} - -// GetDashboard returns the dashboard -func (s *ScalewayAPI) GetDashboard() (*types.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 types.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 = types.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 types.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 = types.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 = types.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 types.ScalewayResolverResults, arch string) (ret types.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 types.ScalewayResolverResults, region string) (ret types.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) (*types.ScalewayImageIdentifier, error) { - // Parses optional type prefix, i.e: "image:name" -> "name" - _, needle = types.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 &types.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() (*types.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 types.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) (*types.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 types.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) (*types.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 types.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) (*types.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 types.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 types.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 types.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 types.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 types.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 types.ScalewayGetContainers -func (s *ScalewayAPI) GetContainers() (*types.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 types.ScalewayGetContainers - - if err = json.Unmarshal(body, &containers); err != nil { - return nil, err - } - return &containers, nil -} - -// GetContainerDatas returns a types.ScalewayGetContainerDatas -func (s *ScalewayAPI) GetContainerDatas(container string) (*types.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 types.ScalewayGetContainerDatas - - if err = json.Unmarshal(body, &datas); err != nil { - return nil, err - } - return &datas, nil -} - -// GetIPS returns a types.ScalewayGetIPS -func (s *ScalewayAPI) GetIPS() (*types.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 types.ScalewayGetIPS - - if err = json.Unmarshal(body, &ips); err != nil { - return nil, err - } - return &ips, nil -} - -// NewIP returns a new IP -func (s *ScalewayAPI) NewIP() (*types.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 types.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 types.ScalewayGetIP -func (s *ScalewayAPI) GetIP(ipID string) (*types.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 types.ScalewayGetIP - - if err = json.Unmarshal(body, &ip); err != nil { - return nil, err - } - return &ip, nil -} - -// GetQuotas returns a types.ScalewayGetQuotas -func (s *ScalewayAPI) GetQuotas() (*types.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 types.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 = types.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) (*types.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 types.MarketImages - - if uuidImage != "" { - ret.Images = make([]types.MarketImage, 1) - - var img types.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) (*types.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 types.MarketVersions - - if uuidImage != "" { - var version types.MarketVersion - ret.Versions = make([]types.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) (*types.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 types.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) (*types.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 types.MarketLocalImages - if uuidLocalImage != "" { - var localImage types.MarketLocalImage - ret.LocalImages = make([]types.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 types.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 types.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 types.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 types.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 types.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 types.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() (*types.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 types.ScalewayProductsServers - if err = json.Unmarshal(body, &productServers); err != nil { - return nil, err - } - - return &productServers, nil -} - -// 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 -} diff --git a/src/cmd/linuxkit/vendor/github.com/scaleway/go-scaleway/cache/cache.go b/src/cmd/linuxkit/vendor/github.com/scaleway/go-scaleway/cache/cache.go deleted file mode 100644 index 91023585e..000000000 --- a/src/cmd/linuxkit/vendor/github.com/scaleway/go-scaleway/cache/cache.go +++ /dev/null @@ -1,693 +0,0 @@ -// Copyright (C) 2018 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 cache - -import ( - "encoding/json" - "io/ioutil" - "os" - "path/filepath" - "regexp" - "strings" - "sync" - - "github.com/moul/anonuuid" - "github.com/renstrom/fuzzysearch/fuzzy" - "github.com/scaleway/go-scaleway/types" -) - -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() -} - -// 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 ComputeRankMatch(s *types.ScalewayResolverResult, 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) (types.ScalewayResolverResults, error) { - c.Lock.Lock() - defer c.Lock.Unlock() - - var res types.ScalewayResolverResults - var exactMatches types.ScalewayResolverResults - - if acceptUUID && anonuuid.IsUUID(needle) == nil { - if fields, ok := c.Images[needle]; ok { - entry, err := types.NewScalewayResolverResult(needle, fields[CacheTitle], fields[CacheArch], fields[CacheRegion], types.IdentifierImage) - if err != nil { - return types.ScalewayResolverResults{}, err - } - ComputeRankMatch(&entry, 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 := types.NewScalewayResolverResult(identifier, fields[CacheTitle], fields[CacheArch], fields[CacheRegion], types.IdentifierImage) - if err != nil { - return types.ScalewayResolverResults{}, err - } - ComputeRankMatch(&entry, needle) - exactMatches = append(exactMatches, entry) - } - if strings.HasPrefix(identifier, needle) || nameRegex.MatchString(fields[CacheTitle]) { - entry, err := types.NewScalewayResolverResult(identifier, fields[CacheTitle], fields[CacheArch], fields[CacheRegion], types.IdentifierImage) - if err != nil { - return types.ScalewayResolverResults{}, err - } - ComputeRankMatch(&entry, needle) - res = append(res, entry) - } else if strings.HasPrefix(fields[CacheMarketPlaceUUID], needle) || nameRegex.MatchString(fields[CacheMarketPlaceUUID]) { - entry, err := types.NewScalewayResolverResult(identifier, fields[CacheTitle], fields[CacheArch], fields[CacheRegion], types.IdentifierImage) - if err != nil { - return types.ScalewayResolverResults{}, err - } - ComputeRankMatch(&entry, 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) (types.ScalewayResolverResults, error) { - c.Lock.Lock() - defer c.Lock.Unlock() - - var res types.ScalewayResolverResults - var exactMatches types.ScalewayResolverResults - - if acceptUUID && anonuuid.IsUUID(needle) == nil { - if fields, ok := c.Snapshots[needle]; ok { - entry, err := types.NewScalewayResolverResult(needle, fields[CacheTitle], fields[CacheArch], fields[CacheRegion], types.IdentifierSnapshot) - if err != nil { - return types.ScalewayResolverResults{}, err - } - ComputeRankMatch(&entry, 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 := types.NewScalewayResolverResult(identifier, fields[CacheTitle], fields[CacheArch], fields[CacheRegion], types.IdentifierSnapshot) - if err != nil { - return types.ScalewayResolverResults{}, err - } - ComputeRankMatch(&entry, needle) - exactMatches = append(exactMatches, entry) - } - if strings.HasPrefix(identifier, needle) || nameRegex.MatchString(fields[CacheTitle]) { - entry, err := types.NewScalewayResolverResult(identifier, fields[CacheTitle], fields[CacheArch], fields[CacheRegion], types.IdentifierSnapshot) - if err != nil { - return types.ScalewayResolverResults{}, err - } - ComputeRankMatch(&entry, 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) (types.ScalewayResolverResults, error) { - c.Lock.Lock() - defer c.Lock.Unlock() - - var res types.ScalewayResolverResults - var exactMatches types.ScalewayResolverResults - - if acceptUUID && anonuuid.IsUUID(needle) == nil { - if fields, ok := c.Volumes[needle]; ok { - entry, err := types.NewScalewayResolverResult(needle, fields[CacheTitle], fields[CacheArch], fields[CacheRegion], types.IdentifierVolume) - if err != nil { - return types.ScalewayResolverResults{}, err - } - ComputeRankMatch(&entry, 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 := types.NewScalewayResolverResult(identifier, fields[CacheTitle], fields[CacheArch], fields[CacheRegion], types.IdentifierVolume) - if err != nil { - return types.ScalewayResolverResults{}, err - } - ComputeRankMatch(&entry, needle) - exactMatches = append(exactMatches, entry) - } - if strings.HasPrefix(identifier, needle) || nameRegex.MatchString(fields[CacheTitle]) { - entry, err := types.NewScalewayResolverResult(identifier, fields[CacheTitle], fields[CacheArch], fields[CacheRegion], types.IdentifierVolume) - if err != nil { - return types.ScalewayResolverResults{}, err - } - ComputeRankMatch(&entry, 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) (types.ScalewayResolverResults, error) { - c.Lock.Lock() - defer c.Lock.Unlock() - - var res types.ScalewayResolverResults - var exactMatches types.ScalewayResolverResults - - if acceptUUID && anonuuid.IsUUID(needle) == nil { - if fields, ok := c.Bootscripts[needle]; ok { - entry, err := types.NewScalewayResolverResult(needle, fields[CacheTitle], fields[CacheArch], fields[CacheRegion], types.IdentifierBootscript) - if err != nil { - return types.ScalewayResolverResults{}, err - } - ComputeRankMatch(&entry, 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 := types.NewScalewayResolverResult(identifier, fields[CacheTitle], fields[CacheArch], fields[CacheRegion], types.IdentifierBootscript) - if err != nil { - return types.ScalewayResolverResults{}, err - } - ComputeRankMatch(&entry, needle) - exactMatches = append(exactMatches, entry) - } - if strings.HasPrefix(identifier, needle) || nameRegex.MatchString(fields[CacheTitle]) { - entry, err := types.NewScalewayResolverResult(identifier, fields[CacheTitle], fields[CacheArch], fields[CacheRegion], types.IdentifierBootscript) - if err != nil { - return types.ScalewayResolverResults{}, err - } - ComputeRankMatch(&entry, 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) (types.ScalewayResolverResults, error) { - c.Lock.Lock() - defer c.Lock.Unlock() - - var res types.ScalewayResolverResults - var exactMatches types.ScalewayResolverResults - - if acceptUUID && anonuuid.IsUUID(needle) == nil { - if fields, ok := c.Servers[needle]; ok { - entry, err := types.NewScalewayResolverResult(needle, fields[CacheTitle], fields[CacheArch], fields[CacheRegion], types.IdentifierServer) - if err != nil { - return types.ScalewayResolverResults{}, err - } - ComputeRankMatch(&entry, 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 := types.NewScalewayResolverResult(identifier, fields[CacheTitle], fields[CacheArch], fields[CacheRegion], types.IdentifierServer) - if err != nil { - return types.ScalewayResolverResults{}, err - } - ComputeRankMatch(&entry, needle) - exactMatches = append(exactMatches, entry) - } - if strings.HasPrefix(identifier, needle) || nameRegex.MatchString(fields[CacheTitle]) { - entry, err := types.NewScalewayResolverResult(identifier, fields[CacheTitle], fields[CacheArch], fields[CacheRegion], types.IdentifierServer) - if err != nil { - return types.ScalewayResolverResults{}, err - } - ComputeRankMatch(&entry, 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 types.ScalewayResolverResults) types.ScalewayResolverResults { - encountered := map[string]types.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 := types.ScalewayResolverResults{} - for _, result := range encountered { - results = append(results, result) - } - return results -} - -// LookUpIdentifiers attempts to return identifiers matching a pattern -func (c *ScalewayCache) LookUpIdentifiers(needle string) (types.ScalewayResolverResults, error) { - results := types.ScalewayResolverResults{} - - identifierType, needle := types.ParseNeedle(needle) - - if identifierType&(types.IdentifierUnknown|types.IdentifierServer) > 0 { - servers, err := c.LookUpServers(needle, false) - if err != nil { - return types.ScalewayResolverResults{}, err - } - for _, result := range servers { - entry, err := types.NewScalewayResolverResult(result.Identifier, result.Name, result.Arch, result.Region, types.IdentifierServer) - if err != nil { - return types.ScalewayResolverResults{}, err - } - ComputeRankMatch(&entry, needle) - results = append(results, entry) - } - } - - if identifierType&(types.IdentifierUnknown|types.IdentifierImage) > 0 { - images, err := c.LookUpImages(needle, false) - if err != nil { - return types.ScalewayResolverResults{}, err - } - for _, result := range images { - entry, err := types.NewScalewayResolverResult(result.Identifier, result.Name, result.Arch, result.Region, types.IdentifierImage) - if err != nil { - return types.ScalewayResolverResults{}, err - } - ComputeRankMatch(&entry, needle) - results = append(results, entry) - } - } - - if identifierType&(types.IdentifierUnknown|types.IdentifierSnapshot) > 0 { - snapshots, err := c.LookUpSnapshots(needle, false) - if err != nil { - return types.ScalewayResolverResults{}, err - } - for _, result := range snapshots { - entry, err := types.NewScalewayResolverResult(result.Identifier, result.Name, result.Arch, result.Region, types.IdentifierSnapshot) - if err != nil { - return types.ScalewayResolverResults{}, err - } - ComputeRankMatch(&entry, needle) - results = append(results, entry) - } - } - - if identifierType&(types.IdentifierUnknown|types.IdentifierVolume) > 0 { - volumes, err := c.LookUpVolumes(needle, false) - if err != nil { - return types.ScalewayResolverResults{}, err - } - for _, result := range volumes { - entry, err := types.NewScalewayResolverResult(result.Identifier, result.Name, result.Arch, result.Region, types.IdentifierVolume) - if err != nil { - return types.ScalewayResolverResults{}, err - } - ComputeRankMatch(&entry, needle) - results = append(results, entry) - } - } - - if identifierType&(types.IdentifierUnknown|types.IdentifierBootscript) > 0 { - bootscripts, err := c.LookUpBootscripts(needle, false) - if err != nil { - return types.ScalewayResolverResults{}, err - } - for _, result := range bootscripts { - entry, err := types.NewScalewayResolverResult(result.Identifier, result.Name, result.Arch, result.Region, types.IdentifierBootscript) - if err != nil { - return types.ScalewayResolverResults{}, err - } - ComputeRankMatch(&entry, 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/src/cmd/linuxkit/vendor/github.com/scaleway/go-scaleway/logger/logger.go b/src/cmd/linuxkit/vendor/github.com/scaleway/go-scaleway/logger/logger.go deleted file mode 100644 index 893cfd7a0..000000000 --- a/src/cmd/linuxkit/vendor/github.com/scaleway/go-scaleway/logger/logger.go +++ /dev/null @@ -1,77 +0,0 @@ -// Copyright (C) 2018 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 logger - -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/src/cmd/linuxkit/vendor/github.com/scaleway/go-scaleway/types/types.go b/src/cmd/linuxkit/vendor/github.com/scaleway/go-scaleway/types/types.go deleted file mode 100644 index 73fbc1348..000000000 --- a/src/cmd/linuxkit/vendor/github.com/scaleway/go-scaleway/types/types.go +++ /dev/null @@ -1,983 +0,0 @@ -// Copyright (C) 2018 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 types - -import ( - "bytes" - "encoding/json" - "fmt" - "html/template" - "regexp" - "strings" - "time" - - "github.com/moul/anonuuid" -) - -// 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 -} - -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 - } - } -} - -// 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() -} - -// 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"` -} - -// 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"` -} - -// 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"` -} - -// 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"` - - // 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], VC1S) - 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"` -} - -// 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"` - - // BootType defines the type of boot - BootType string `json:"boot_type,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], VC1S) - CommercialType string `json:"commercial_type"` - - PublicIP string `json:"public_ip,omitempty"` - - EnableIPV6 bool `json:"enable_ipv6,omitempty"` - - SecurityGroup string `json:"security_group,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"` -} - -// 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) -} - -func (s *ScalewayUserdata) String() string { - return string(*s) -} diff --git a/src/cmd/linuxkit/vendor/github.com/scaleway/scaleway-sdk-go/LICENSE b/src/cmd/linuxkit/vendor/github.com/scaleway/scaleway-sdk-go/LICENSE new file mode 100644 index 000000000..2712cc51a --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/scaleway/scaleway-sdk-go/LICENSE @@ -0,0 +1,191 @@ + + Apache License + Version 2.0, January 2004 + https://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + Copyright 2019 Scaleway. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/src/cmd/linuxkit/vendor/github.com/scaleway/scaleway-sdk-go/README.md b/src/cmd/linuxkit/vendor/github.com/scaleway/scaleway-sdk-go/README.md new file mode 100644 index 000000000..aee6ab579 --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/scaleway/scaleway-sdk-go/README.md @@ -0,0 +1,81 @@ +

+ +

+ GoDoc + CircleCI + GoReportCard +

+ +# Scaleway GO SDK + + +**:warning: This is an early release, keep in mind that the API can break** + + +Scaleway is a simple way to build, deploy and scale your infrastructure in the cloud. +We help thousands of developers and businesses to run their infrastructures without any issue. + +## Documentation + +- [Godoc](https://godoc.org/github.com/scaleway/scaleway-sdk-go) +- [Developers website](https://developers.scaleway.com) (API documentation) + +## Installation + +```bash +go get github.com/scaleway/scaleway-sdk-go +``` + +## Getting Started + +```go +package main + +import ( + "fmt" + + "github.com/scaleway/scaleway-sdk-go/api/instance/v1" + "github.com/scaleway/scaleway-sdk-go/scw" + "github.com/scaleway/scaleway-sdk-go/utils" +) + +func main() { + + // Create a Scaleway client + client, err := scw.NewClient( + // Get your credentials at https://console.scaleway.com/account/credentials + scw.WithDefaultProjectID("ORGANISATION_ID"), + scw.WithAuth("ACCESS_KEY", "SECRET_KEY"), + ) + if err != nil { + panic(err) + } + + // Create SDK objects for Scaleway Instance product + instanceApi := instance.NewAPI(client) + + // Call the ListServers method on the Instance SDK + response, err := instanceApi.ListServers(&instance.ListServersRequest{ + Zone: utils.ZoneFrPar1, + }) + if err != nil { + panic(err) + } + + // Do something with the response... + for _, server := range response.Servers { + fmt.Println("Server", server.ID, server.Name) + } + +} +``` + +## Development + +This repository is at its early stage and is still in active development. +If you are looking for a way to contribute please read [CONTRIBUTING.md](CONTRIBUTING.md). + +## Reach us + +We love feedback. +Feel free to reach us on [Scaleway Slack community](https://slack.scaleway.com/), we are waiting for you on [#opensource](https://scaleway-community.slack.com/app_redirect?channel=opensource). diff --git a/src/cmd/linuxkit/vendor/github.com/scaleway/scaleway-sdk-go/api/instance/v1/instance_metadata_sdk.go b/src/cmd/linuxkit/vendor/github.com/scaleway/scaleway-sdk-go/api/instance/v1/instance_metadata_sdk.go new file mode 100644 index 000000000..5fc5f79ff --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/scaleway/scaleway-sdk-go/api/instance/v1/instance_metadata_sdk.go @@ -0,0 +1,278 @@ +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"` + 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"` + Email string `json:"email,omitempty"` + UserAgent struct { + Platform string `json:"platform,omitempty"` + Version string `json:"version,omitempty"` + String string `json:"string,omitempty"` + Browser string `json:"browser,omitempty"` + } `json:"user_agent,omitempty"` + Key string `json:"key,omitempty"` + Fingerprint string `json:"fingerprint,omitempty"` + ID string `json:"id,omitempty"` + CreationDate string `json:"creation_date,omitempty"` + Port int `json:"port,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"` + 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"` + 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/src/cmd/linuxkit/vendor/github.com/scaleway/scaleway-sdk-go/api/instance/v1/instance_sdk.go b/src/cmd/linuxkit/vendor/github.com/scaleway/scaleway-sdk-go/api/instance/v1/instance_sdk.go new file mode 100644 index 000000000..0a99b8265 --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/scaleway/scaleway-sdk-go/api/instance/v1/instance_sdk.go @@ -0,0 +1,3540 @@ +// This file was automatically generated. DO NOT EDIT. +// If you have any remark or suggestion do not hesitate to open an issue. + +package instance + +import ( + "bytes" + "encoding/json" + "fmt" + "net" + "net/http" + "net/url" + "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/scw" + "github.com/scaleway/scaleway-sdk-go/utils" +) + +// always import dependencies +var ( + _ fmt.Stringer + _ json.Unmarshaler + _ url.URL + _ net.IP + _ http.Header + _ bytes.Reader + _ time.Time + + _ scw.ScalewayRequest + _ marshaler.Duration + _ utils.File + _ = parameter.AddToQuery +) + +// 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) +} + +type GetServerTypesAvailabilityResponseAvailability string + +const ( + // GetServerTypesAvailabilityResponseAvailabilityAvailable is [insert doc]. + GetServerTypesAvailabilityResponseAvailabilityAvailable = GetServerTypesAvailabilityResponseAvailability("available") + // GetServerTypesAvailabilityResponseAvailabilityScarce is [insert doc]. + GetServerTypesAvailabilityResponseAvailabilityScarce = GetServerTypesAvailabilityResponseAvailability("scarce") + // GetServerTypesAvailabilityResponseAvailabilityShortage is [insert doc]. + GetServerTypesAvailabilityResponseAvailabilityShortage = GetServerTypesAvailabilityResponseAvailability("shortage") +) + +func (enum GetServerTypesAvailabilityResponseAvailability) String() string { + if enum == "" { + // return default value if empty + return "available" + } + return string(enum) +} + +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) +} + +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) +} + +type SecurityRuleAction string + +const ( + // SecurityRuleActionAccept is [insert doc]. + SecurityRuleActionAccept = SecurityRuleAction("accept") + // SecurityRuleActionDrop is [insert doc]. + SecurityRuleActionDrop = SecurityRuleAction("drop") +) + +func (enum SecurityRuleAction) String() string { + if enum == "" { + // return default value if empty + return "accept" + } + return string(enum) +} + +type SecurityRuleDirection string + +const ( + // SecurityRuleDirectionInbound is [insert doc]. + SecurityRuleDirectionInbound = SecurityRuleDirection("inbound") + // SecurityRuleDirectionOutbound is [insert doc]. + SecurityRuleDirectionOutbound = SecurityRuleDirection("outbound") +) + +func (enum SecurityRuleDirection) String() string { + if enum == "" { + // return default value if empty + return "inbound" + } + return string(enum) +} + +type SecurityRuleProtocol string + +const ( + // SecurityRuleProtocolTCP is [insert doc]. + SecurityRuleProtocolTCP = SecurityRuleProtocol("tcp") + // SecurityRuleProtocolUDP is [insert doc]. + SecurityRuleProtocolUDP = SecurityRuleProtocol("udp") + // SecurityRuleProtocolIcmp is [insert doc]. + SecurityRuleProtocolIcmp = SecurityRuleProtocol("icmp") +) + +func (enum SecurityRuleProtocol) String() string { + if enum == "" { + // return default value if empty + return "tcp" + } + return string(enum) +} + +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) +} + +type ServerBootType string + +const ( + // ServerBootTypeLocal is [insert doc]. + ServerBootTypeLocal = ServerBootType("local") +) + +func (enum ServerBootType) String() string { + if enum == "" { + // return default value if empty + return "local" + } + return string(enum) +} + +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) +} + +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) +} + +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) +} + +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) +} + +type VolumeType string + +const ( + // VolumeTypeLSsd is [insert doc]. + VolumeTypeLSsd = VolumeType("l_ssd") + // VolumeTypeLHdd is [insert doc]. + VolumeTypeLHdd = VolumeType("l_hdd") + // VolumeTypeRSsd is [insert doc]. + VolumeTypeRSsd = VolumeType("r_ssd") +) + +func (enum VolumeType) String() string { + if enum == "" { + // return default value if empty + return "l_ssd" + } + return string(enum) +} + +type Bootscript struct { + // Arch display the bootscripts arch + // + // Default value: x86_64 + Arch Arch `json:"arch,omitempty"` + // Bootcmdargs display the bootscript parameters + Bootcmdargs string `json:"bootcmdargs,omitempty"` + // Default dispmay if the bootscript is the default bootscript if no other boot option is configured + Default bool `json:"default,omitempty"` + // Dtb provide information regarding a Device Tree Binary (dtb) for use with C1 servers + Dtb string `json:"dtb,omitempty"` + // ID display the bootscripts ID + ID string `json:"id,omitempty"` + // Initrd display the initrd (initial ramdisk) configuration + Initrd string `json:"initrd,omitempty"` + // Kernel display the server kernel version + Kernel string `json:"kernel,omitempty"` + // Organization display the bootscripts organization + Organization string `json:"organization,omitempty"` + // Public provide information if the bootscript is public + Public bool `json:"public,omitempty"` + // Title display the bootscripts title + Title string `json:"title,omitempty"` +} + +type CreateIPResponse struct { + IP *IP `json:"ip,omitempty"` + + Location string `json:"Location,omitempty"` +} + +type CreateImageResponse struct { + Image *Image `json:"image,omitempty"` + + Location string `json:"Location,omitempty"` +} + +type CreateSecurityGroupResponse struct { + SecurityGroup *SecurityGroup `json:"security_group,omitempty"` +} + +type CreateSecurityGroupRuleResponse struct { + SecurityRule *SecurityRule `json:"security_rule,omitempty"` +} + +type CreateServerResponse struct { + Server *Server `json:"server,omitempty"` +} + +type CreateSnapshotResponse struct { + Snapshot *Snapshot `json:"snapshot,omitempty"` +} + +type CreateVolumeResponse struct { + Volume *Volume `json:"volume,omitempty"` + + Location string `json:"Location,omitempty"` +} + +type Dashboard struct { + VolumesCount uint32 `json:"volumes_count,omitempty"` + + RunningServersCount uint32 `json:"running_servers_count,omitempty"` + + ServersByTypes map[string]uint32 `json:"servers_by_types,omitempty"` + + ImagesCount uint32 `json:"images_count,omitempty"` + + SnapshotsCount uint32 `json:"snapshots_count,omitempty"` + + ServersCount uint32 `json:"servers_count,omitempty"` + + IpsCount uint32 `json:"ips_count,omitempty"` + + SecurityGroupsCount uint32 `json:"security_groups_count,omitempty"` + + IpsUnused uint32 `json:"ips_unused,omitempty"` +} + +type GetBootscriptResponse struct { + Bootscript *Bootscript `json:"bootscript,omitempty"` +} + +type GetDashboardResponse struct { + Dashboard *Dashboard `json:"dashboard,omitempty"` +} + +type GetIPResponse struct { + IP *IP `json:"ip,omitempty"` +} + +type GetImageResponse struct { + Image *Image `json:"image,omitempty"` +} + +type GetSecurityGroupResponse struct { + SecurityGroup *SecurityGroup `json:"security_group,omitempty"` +} + +type GetSecurityGroupRuleResponse struct { + SecurityRule *SecurityRule `json:"security_rule,omitempty"` +} + +type GetServerResponse struct { + Server *Server `json:"server,omitempty"` +} + +type GetServerTypesAvailabilityResponse struct { + Servers map[string]GetServerTypesAvailabilityResponseAvailability `json:"servers,omitempty"` +} + +type GetServiceInfoResponse struct { + API string `json:"api,omitempty"` + + Description string `json:"description,omitempty"` + + Version string `json:"version,omitempty"` +} + +type GetSnapshotResponse struct { + Snapshot *Snapshot `json:"snapshot,omitempty"` +} + +type GetVolumeResponse struct { + Volume *Volume `json:"volume,omitempty"` +} + +type IP struct { + ID string `json:"id,omitempty"` + + Address net.IP `json:"address,omitempty"` + + Reverse *string `json:"reverse,omitempty"` + + Server *ServerSummary `json:"server,omitempty"` + + Organization string `json:"organization,omitempty"` +} + +type Image struct { + ID string `json:"id,omitempty"` + + Name string `json:"name,omitempty"` + // Arch + // + // Default value: x86_64 + Arch Arch `json:"arch,omitempty"` + + CreationDate time.Time `json:"creation_date,omitempty"` + + ModificationDate time.Time `json:"modification_date,omitempty"` + + DefaultBootscript *Bootscript `json:"default_bootscript,omitempty"` + + ExtraVolumes map[string]*Volume `json:"extra_volumes,omitempty"` + + FromServer *ServerSummary `json:"from_server,omitempty"` + + Organization string `json:"organization,omitempty"` + + Public bool `json:"public,omitempty"` + + RootVolume *VolumeTemplate `json:"root_volume,omitempty"` + // State + // + // Default value: available + State ImageState `json:"state,omitempty"` +} + +type ListBootscriptsResponse struct { + Bootscripts []*Bootscript `json:"bootscripts,omitempty"` + + TotalCount uint32 `json:"total_count,omitempty"` +} + +type ListImagesResponse struct { + Images []*Image `json:"images,omitempty"` + + TotalCount uint32 `json:"total_count,omitempty"` +} + +type ListIpsResponse struct { + Ips []*IP `json:"ips,omitempty"` + + TotalCount uint32 `json:"total_count,omitempty"` +} + +type ListSecurityGroupRulesResponse struct { + SecurityRules []*SecurityRule `json:"security_rules,omitempty"` + + TotalCount uint32 `json:"total_count,omitempty"` +} + +type ListSecurityGroupsResponse struct { + SecurityGroups []*SecurityGroup `json:"security_groups,omitempty"` + + TotalCount uint32 `json:"total_count,omitempty"` +} + +type ListServerActionsResponse struct { + Actions []ServerAction `json:"actions,omitempty"` +} + +type ListServerUserDataResponse struct { + UserData []string `json:"user_data,omitempty"` +} + +type ListServersResponse struct { + Servers []*Server `json:"servers,omitempty"` + + TotalCount uint32 `json:"total_count,omitempty"` +} + +type ListServersTypesResponse struct { + Servers map[string]*ServerTypeDefinition `json:"servers,omitempty"` + + TotalCount uint32 `json:"total_count,omitempty"` +} + +type ListSnapshotsResponse struct { + Snapshots []*Snapshot `json:"snapshots,omitempty"` + + TotalCount uint32 `json:"total_count,omitempty"` +} + +type ListVolumesResponse struct { + Volumes []*Volume `json:"volumes,omitempty"` + + TotalCount uint32 `json:"total_count,omitempty"` +} + +type SecurityGroup struct { + // ID display the security groups' unique ID + ID string `json:"id,omitempty"` + // Name display the security groups name + Name string `json:"name,omitempty"` + // CreationDate display the security group creation date + CreationDate time.Time `json:"creation_date,omitempty"` + // ModificationDate display the security group modification date + ModificationDate time.Time `json:"modification_date,omitempty"` + // Description display the security groups description + Description string `json:"description,omitempty"` + // EnableDefaultSecurity display if the security group is set as default + EnableDefaultSecurity bool `json:"enable_default_security,omitempty"` + // InboundDefaultPolicy display the default inbound policy + // + // Default value: accept + InboundDefaultPolicy SecurityGroupPolicy `json:"inbound_default_policy,omitempty"` + // Organization display the security groups organization ID + Organization string `json:"organization,omitempty"` + // OrganizationDefault display if the security group is set as organization default + OrganizationDefault bool `json:"organization_default,omitempty"` + // OutboundDefaultPolicy display the default outbound policy + // + // Default value: accept + OutboundDefaultPolicy SecurityGroupPolicy `json:"outbound_default_policy,omitempty"` + // Servers list of servers attached to this security group + Servers []*ServerSummary `json:"servers,omitempty"` + // Stateful true if the security group is stateful + Stateful bool `json:"stateful,omitempty"` +} + +type SecurityGroupSummary struct { + ID string `json:"id,omitempty"` + + Name string `json:"name,omitempty"` +} + +type SecurityRule struct { + ID string `json:"id,omitempty"` + // Protocol + // + // Default value: tcp + Protocol SecurityRuleProtocol `json:"protocol,omitempty"` + // Direction + // + // Default value: inbound + Direction SecurityRuleDirection `json:"direction,omitempty"` + // Action + // + // Default value: accept + Action SecurityRuleAction `json:"action,omitempty"` + + IPRange string `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"` +} + +type Server struct { + // ID display the server unique ID + ID string `json:"id,omitempty"` + // Image provide information on the server image + Image *Image `json:"image,omitempty"` + // Name display the server name + Name string `json:"name,omitempty"` + // Organization display the server organization + Organization string `json:"organization,omitempty"` + // PrivateIP display the server private IP address + PrivateIP *string `json:"private_ip,omitempty"` + // PublicIP display the server public IP address + PublicIP *ServerIP `json:"public_ip,omitempty"` + // State display the server state + // + // Default value: running + State ServerState `json:"state,omitempty"` + // BootType display the server boot type + // + // Default value: local + BootType ServerBootType `json:"boot_type,omitempty"` + // Tags display the server associated tags + Tags []string `json:"tags,omitempty"` + // Volumes display the server volumes + Volumes map[string]*Volume `json:"volumes,omitempty"` + // Bootscript display the server bootscript + Bootscript *Bootscript `json:"bootscript,omitempty"` + // DynamicPublicIP display the server dynamic public IP + DynamicPublicIP bool `json:"dynamic_public_ip,omitempty"` + // CommercialType display the server commercial type (e.g. GP1-M) + CommercialType string `json:"commercial_type,omitempty"` + // CreationDate display the server creation date + CreationDate time.Time `json:"creation_date,omitempty"` + // DynamicIPRequired display if a dynamic IP is required + DynamicIPRequired bool `json:"dynamic_ip_required,omitempty"` + // EnableIPv6 display if IPv6 is enabled + EnableIPv6 bool `json:"enable_ipv6,omitempty"` + // ExtraNetworks display information about additional network interfaces + ExtraNetworks []string `json:"extra_networks,omitempty"` + // Hostname display the server host name + Hostname string `json:"hostname,omitempty"` + // AllowedActions provide as list of allowed actions on the server + AllowedActions []ServerAction `json:"allowed_actions,omitempty"` + // Arch display the server arch + // + // Default value: x86_64 + Arch Arch `json:"arch,omitempty"` + // IPv6 display the server IPv6 address + IPv6 *ServerIPv6 `json:"ipv6,omitempty"` + // Location display the server location + Location *ServerLocation `json:"location,omitempty"` + // Maintenances display the server planned maintenances + Maintenances []*ServerMaintenance `json:"maintenances,omitempty"` + // ModificationDate display the server modification date + ModificationDate time.Time `json:"modification_date,omitempty"` + // Protected display the server protection option is activated + Protected bool `json:"protected,omitempty"` + // SecurityGroup display the server security group + SecurityGroup *SecurityGroupSummary `json:"security_group,omitempty"` + // StateDetail display the server state_detail + StateDetail string `json:"state_detail,omitempty"` +} + +type ServerActionResponse struct { + Task *Task `json:"task,omitempty"` +} + +type ServerIP struct { + // ID display the unique ID of the IP address + ID string `json:"id,omitempty"` + // Address display the server public IPv4 IP-Address + Address net.IP `json:"address,omitempty"` + // Dynamic display information if the IP address will be considered as dynamic + Dynamic bool `json:"dynamic,omitempty"` +} + +type ServerIPv6 struct { + // Address display the server IPv6 IP-Address + Address net.IP `json:"address,omitempty"` + // Gateway display the IPv6 IP-addresses gateway + Gateway string `json:"gateway,omitempty"` + // Netmask display the IPv6 IP-addresses CIDR netmask + Netmask string `json:"netmask,omitempty"` +} + +type ServerLocation struct { + ClusterID string `json:"cluster_id,omitempty"` + + HypervisorID string `json:"hypervisor_id,omitempty"` + + NodeID string `json:"node_id,omitempty"` + + PlatformID string `json:"platform_id,omitempty"` + + ZoneID string `json:"zone_id,omitempty"` +} + +type ServerMaintenance struct { +} + +type ServerSummary struct { + ID string `json:"id,omitempty"` + + Name string `json:"name,omitempty"` +} + +type ServerTypeDefinition struct { + MonthlyPrice float32 `json:"monthly_price,omitempty"` + + HourlyPrice float32 `json:"hourly_price,omitempty"` + + AltNames map[uint32]string `json:"alt_names,omitempty"` + + PerVolumeConstraint map[string]*ServerTypeDefinitionVolumeConstraintSizes `json:"per_volume_constraint,omitempty"` + + VolumesConstraint *ServerTypeDefinitionVolumeConstraintSizes `json:"volumes_constraint,omitempty"` + + Ncpus uint32 `json:"ncpus,omitempty"` + + Gpu *uint64 `json:"gpu,omitempty"` + + RAM uint64 `json:"ram,omitempty"` + // Arch + // + // Default value: x86_64 + Arch Arch `json:"arch,omitempty"` + + Baremetal bool `json:"baremetal,omitempty"` + + Network *ServerTypeDefinitionNetwork `json:"network,omitempty"` +} + +type ServerTypeDefinitionNetwork struct { + Interfaces []*ServerTypeDefinitionNetworkInterface `json:"interfaces,omitempty"` + + SumInternalBandwidth *uint64 `json:"sum_internal_bandwidth,omitempty"` + + SumInternetBandwidth *uint64 `json:"sum_internet_bandwidth,omitempty"` + + IPv6Support bool `json:"ipv6_support,omitempty"` +} + +type ServerTypeDefinitionNetworkInterface struct { + InternalBandwidth *uint64 `json:"internal_bandwidth,omitempty"` + + InternetBandwidth *uint64 `json:"internet_bandwidth,omitempty"` +} + +type ServerTypeDefinitionVolumeConstraintSizes struct { + MinSize uint64 `json:"min_size,omitempty"` + + MaxSize uint64 `json:"max_size,omitempty"` +} + +type SetIPResponse struct { + IP *IP `json:"ip,omitempty"` +} + +type SetImageResponse struct { + Image *Image `json:"image,omitempty"` +} + +type SetServerResponse struct { + Server *Server `json:"server,omitempty"` +} + +type SetSnapshotResponse struct { + Snapshot *Snapshot `json:"snapshot,omitempty"` +} + +type SetVolumeResponse struct { + Volume *Volume `json:"volume,omitempty"` +} + +type Snapshot struct { + ID string `json:"id,omitempty"` + + Name string `json:"name,omitempty"` + + Organization string `json:"organization,omitempty"` + // VolumeType + // + // Default value: l_ssd + VolumeType VolumeType `json:"volume_type,omitempty"` + + Size uint64 `json:"size,omitempty"` + // State + // + // Default value: available + State SnapshotState `json:"state,omitempty"` + + BaseVolume *SnapshotBaseVolume `json:"base_volume,omitempty"` + + CreationDate time.Time `json:"creation_date,omitempty"` + + ModificationDate time.Time `json:"modification_date,omitempty"` +} + +type SnapshotBaseVolume struct { + ID string `json:"id,omitempty"` + + Name string `json:"name,omitempty"` +} + +type Task struct { + // ID the unique ID of the task + ID string `json:"id,omitempty"` + // Description the description of the task + Description string `json:"description,omitempty"` + + HrefFrom string `json:"href_from,omitempty"` + + HrefResult string `json:"href_result,omitempty"` + // Progress show the progress of the task in percent + Progress int32 `json:"progress,omitempty"` + // StartedAt display the task start date + StartedAt time.Time `json:"started_at,omitempty"` + // Status display the task status + // + // Default value: pending + Status TaskStatus `json:"status,omitempty"` + // TerminatedAt display the task end date + TerminatedAt time.Time `json:"terminated_at,omitempty"` +} + +type UpdateIPResponse struct { + IP *IP `json:"ip,omitempty"` +} + +type UpdateSecurityGroupResponse struct { + SecurityGroup *SecurityGroup `json:"security_group,omitempty"` +} + +type UpdateServerResponse struct { + Server *Server `json:"server,omitempty"` +} + +type Volume struct { + // ID display the volumes unique ID + ID string `json:"id,omitempty"` + // Name display the volumes names + Name string `json:"name,omitempty"` + // ExportURI show the volumes NBD export URI + ExportURI string `json:"export_uri,omitempty"` + // Organization display the volumes organization + Organization string `json:"organization,omitempty"` + // Server display information about the server attached to the volume + Server *ServerSummary `json:"server,omitempty"` + // Size display the volumes disk size + Size uint64 `json:"size,omitempty"` + // VolumeType display the volumes type + // + // Default value: l_ssd + VolumeType VolumeType `json:"volume_type,omitempty"` + // CreationDate display the volumes creation date + CreationDate time.Time `json:"creation_date,omitempty"` + // ModificationDate display the volumes modification date + ModificationDate time.Time `json:"modification_date,omitempty"` + // State display the volumes state + // + // Default value: available + State VolumeState `json:"state,omitempty"` +} + +type VolumeTemplate struct { + // ID display the volumes unique ID + ID string `json:"id,omitempty"` + // Name display the volumes name + Name string `json:"name,omitempty"` + // Size display the volumes disk size + Size uint64 `json:"size,omitempty"` + // VolumeType display the volumes type + // + // Default value: l_ssd + VolumeType VolumeType `json:"volume_type,omitempty"` + // Organization the organization ID + Organization string `json:"organization,omitempty"` +} + +// Service API + +type GetServerTypesAvailabilityRequest struct { + Zone utils.Zone `json:"-"` + + PerPage *int32 `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 utils.Zone `json:"-"` + + PerPage *int32 `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 ListServersRequest struct { + Zone utils.Zone `json:"-"` + + Organization *string `json:"-"` + + PerPage *int32 `json:"-"` + + Page *int32 `json:"-"` + + Name *string `json:"-"` +} + +// ListServers list servers +func (s *API) ListServers(req *ListServersRequest, opts ...scw.RequestOption) (*ListServersResponse, error) { + var err error + + defaultOrganization, exist := s.client.GetDefaultProjectID() + if (req.Organization == nil || *req.Organization == "") && exist { + req.Organization = &defaultOrganization + } + + 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) + + 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() int { + return int(r.TotalCount) +} + +// UnsafeAppend should not be used +// Internal usage only +func (r *ListServersResponse) UnsafeAppend(res interface{}) (int, scw.SdkError) { + 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 len(results.Servers), nil +} + +type CreateServerRequest struct { + Zone utils.Zone `json:"-"` + // Name display 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 define the server image id + Image string `json:"image,omitempty"` + // Volumes define the volumes attached to the server + Volumes map[string]*VolumeTemplate `json:"volumes,omitempty"` + // EnableIPv6 define if IPv6 is enabled on the server + EnableIPv6 bool `json:"enable_ipv6,omitempty"` + // PublicIP define the public IPv4 attached to the server + PublicIP string `json:"public_ip,omitempty"` + // BootType define the boot type you want to use + // + // Default value: local + BootType ServerBootType `json:"boot_type,omitempty"` + // Organization define the server organization + Organization string `json:"organization,omitempty"` + // Tags define the server tags + Tags []string `json:"tags,omitempty"` + // SecurityGroup define the security group id + SecurityGroup string `json:"security_group,omitempty"` +} + +// CreateServer create server +func (s *API) CreateServer(req *CreateServerRequest, opts ...scw.RequestOption) (*CreateServerResponse, error) { + var err error + + if req.Organization == "" { + defaultOrganization, _ := s.client.GetDefaultProjectID() + 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) + "/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 utils.Zone `json:"-"` + + ServerID string `json:"-"` +} + +// DeleteServer delete 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 utils.Zone `json:"-"` + + ServerID string `json:"-"` +} + +// GetServer get 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 utils.Zone `json:"-"` + // ID display the server unique ID + ID string `json:"-"` + // Name display the server name + Name string `json:"name,omitempty"` + // Organization display the server organization + Organization string `json:"organization,omitempty"` + // AllowedActions provide as list of allowed actions on the server + AllowedActions []ServerAction `json:"allowed_actions,omitempty"` + // Tags display the server associated tags + Tags []string `json:"tags,omitempty"` + // CommercialType display the server commercial type (e.g. GP1-M) + CommercialType string `json:"commercial_type,omitempty"` + // CreationDate display the server creation date + CreationDate time.Time `json:"creation_date,omitempty"` + // DynamicIPRequired display if a dynamic IP is required + DynamicIPRequired bool `json:"dynamic_ip_required,omitempty"` + // DynamicPublicIP display the server dynamic public IP + DynamicPublicIP bool `json:"dynamic_public_ip,omitempty"` + // EnableIPv6 display if IPv6 is enabled + EnableIPv6 bool `json:"enable_ipv6,omitempty"` + // ExtraNetworks display information about additional network interfaces + ExtraNetworks []string `json:"extra_networks,omitempty"` + // Hostname display the server host name + Hostname string `json:"hostname,omitempty"` + // Image provide information on the server image + Image *Image `json:"image,omitempty"` + // Protected display the server protection option is activated + Protected bool `json:"protected,omitempty"` + // PrivateIP display the server private IP address + PrivateIP *string `json:"private_ip,omitempty"` + // PublicIP display the server public IP address + PublicIP *ServerIP `json:"public_ip,omitempty"` + // ModificationDate display the server modification date + ModificationDate time.Time `json:"modification_date,omitempty"` + // State display the server state + // + // Default value: running + State ServerState `json:"state,omitempty"` + // Location display the server location + Location *ServerLocation `json:"location,omitempty"` + // IPv6 display the server IPv6 address + IPv6 *ServerIPv6 `json:"ipv6,omitempty"` + // Bootscript display the server bootscript + Bootscript *Bootscript `json:"bootscript,omitempty"` + // BootType display the server boot type + // + // Default value: local + BootType ServerBootType `json:"boot_type,omitempty"` + // Volumes display the server volumes + Volumes map[string]*Volume `json:"volumes,omitempty"` + // SecurityGroup display the server security group + SecurityGroup *SecurityGroupSummary `json:"security_group,omitempty"` + // Maintenances display the server planned maintenances + Maintenances []*ServerMaintenance `json:"maintenances,omitempty"` + // StateDetail display the server state_detail + StateDetail string `json:"state_detail,omitempty"` + // Arch display the server arch + // + // Default value: x86_64 + Arch Arch `json:"arch,omitempty"` +} + +func (s *API) SetServer(req *SetServerRequest, opts ...scw.RequestOption) (*SetServerResponse, error) { + var err error + + if req.Organization == "" { + defaultOrganization, _ := s.client.GetDefaultProjectID() + 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 utils.Zone `json:"-"` + + ServerID string `json:"-"` + + Name *string `json:"name,omitempty"` + // BootType + // + // Default value: local + BootType ServerBootType `json:"boot_type,omitempty"` + + Tags *[]string `json:"tags,omitempty"` + + Volumes *map[string]*VolumeTemplate `json:"volumes,omitempty"` + + Bootscript *Bootscript `json:"bootscript,omitempty"` + + DynamicIPRequired *bool `json:"dynamic_ip_required,omitempty"` + + EnableIPv6 *bool `json:"enable_ipv6,omitempty"` + + ExtraNetworks *[]string `json:"extra_networks,omitempty"` + + Protected *bool `json:"protected,omitempty"` + + SecurityGroup *SecurityGroupSummary `json:"security_group,omitempty"` +} + +// UpdateServer update 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 utils.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 utils.Zone `json:"-"` + + ServerID string `json:"-"` + // Action + // + // Default value: poweron + Action ServerAction `json:"action,omitempty"` +} + +// ServerAction perform action +// +// Perform power related actions on a server +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 utils.Zone `json:"-"` + + ServerID string `json:"-"` +} + +// ListServerUserData list user data +// +// List all user data keys register 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 utils.Zone `json:"-"` + + ServerID string `json:"-"` + + 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 SetServerUserDataRequest struct { + Zone utils.Zone `json:"-"` + + ServerID string `json:"-"` + + Key string `json:"-"` + + Content *utils.File +} + +// SetServerUserData add/Set user data +// +// Add or update a user data with the given key on a server +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") + } + + 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 +} + +type GetServerUserDataRequest struct { + Zone utils.Zone `json:"-"` + + ServerID string `json:"-"` + + Key string `json:"-"` +} + +// GetServerUserData get user data +// +// Get the content of a user data with the given key on a server +func (s *API) GetServerUserData(req *GetServerUserDataRequest, opts ...scw.RequestOption) (*utils.File, 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{}, + } + + var resp utils.File + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +type ListImagesRequest struct { + Zone utils.Zone `json:"-"` + + Organization *string `json:"-"` + + PerPage *int32 `json:"-"` + + Page *int32 `json:"-"` + + Name *string `json:"-"` + + Public bool `json:"-"` + + Arch *string `json:"-"` +} + +// ListImages list images +// +// List all images available in an account +func (s *API) ListImages(req *ListImagesRequest, opts ...scw.RequestOption) (*ListImagesResponse, error) { + var err error + + defaultOrganization, exist := s.client.GetDefaultProjectID() + if (req.Organization == nil || *req.Organization == "") && exist { + req.Organization = &defaultOrganization + } + + 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) + + 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() int { + return int(r.TotalCount) +} + +// UnsafeAppend should not be used +// Internal usage only +func (r *ListImagesResponse) UnsafeAppend(res interface{}) (int, scw.SdkError) { + 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 len(results.Images), nil +} + +type GetImageRequest struct { + Zone utils.Zone `json:"-"` + + ImageID string `json:"-"` +} + +// GetImage get 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 utils.Zone `json:"-"` + + Name string `json:"name,omitempty"` + + RootVolume string `json:"root_volume,omitempty"` + // Arch + // + // Default value: x86_64 + Arch Arch `json:"arch,omitempty"` + + DefaultBootscript *Bootscript `json:"default_bootscript,omitempty"` + + ExtraVolumes map[string]*Volume `json:"extra_volumes,omitempty"` + + Organization string `json:"organization,omitempty"` + + Public bool `json:"public,omitempty"` +} + +// CreateImage create image +func (s *API) CreateImage(req *CreateImageRequest, opts ...scw.RequestOption) (*CreateImageResponse, error) { + var err error + + if req.Organization == "" { + defaultOrganization, _ := s.client.GetDefaultProjectID() + 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) + "/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 utils.Zone `json:"-"` + + ID string `json:"-"` + + Name string `json:"name,omitempty"` + // Arch + // + // Default value: x86_64 + Arch Arch `json:"arch,omitempty"` + + CreationDate time.Time `json:"creation_date,omitempty"` + + ModificationDate time.Time `json:"modification_date,omitempty"` + + DefaultBootscript *Bootscript `json:"default_bootscript,omitempty"` + + ExtraVolumes map[string]*Volume `json:"extra_volumes,omitempty"` + + FromServer *ServerSummary `json:"from_server,omitempty"` + + Organization string `json:"organization,omitempty"` + + Public bool `json:"public,omitempty"` + + RootVolume *VolumeTemplate `json:"root_volume,omitempty"` + // State + // + // Default value: available + State ImageState `json:"state,omitempty"` +} + +// 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.Organization == "" { + defaultOrganization, _ := s.client.GetDefaultProjectID() + 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 utils.Zone `json:"-"` + + ImageID string `json:"-"` +} + +// DeleteImage delete 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 utils.Zone `json:"-"` + + Organization *string `json:"-"` + + PerPage *int32 `json:"-"` + + Page *int32 `json:"-"` + + Name *string `json:"-"` +} + +// ListSnapshots list snapshots +func (s *API) ListSnapshots(req *ListSnapshotsRequest, opts ...scw.RequestOption) (*ListSnapshotsResponse, error) { + var err error + + defaultOrganization, exist := s.client.GetDefaultProjectID() + if (req.Organization == nil || *req.Organization == "") && exist { + req.Organization = &defaultOrganization + } + + 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) + + 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() int { + return int(r.TotalCount) +} + +// UnsafeAppend should not be used +// Internal usage only +func (r *ListSnapshotsResponse) UnsafeAppend(res interface{}) (int, scw.SdkError) { + 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 len(results.Snapshots), nil +} + +type CreateSnapshotRequest struct { + Zone utils.Zone `json:"-"` + + VolumeID string `json:"volume_id,omitempty"` + + Organization string `json:"organization,omitempty"` + + Name string `json:"name,omitempty"` +} + +// CreateSnapshot create snapshot +func (s *API) CreateSnapshot(req *CreateSnapshotRequest, opts ...scw.RequestOption) (*CreateSnapshotResponse, error) { + var err error + + if req.Organization == "" { + defaultOrganization, _ := s.client.GetDefaultProjectID() + 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) + "/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 utils.Zone `json:"-"` + + SnapshotID string `json:"-"` +} + +// GetSnapshot get 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 utils.Zone `json:"-"` + + ID string `json:"-"` + + Name string `json:"name,omitempty"` + + Organization string `json:"organization,omitempty"` + // VolumeType + // + // Default value: l_ssd + VolumeType VolumeType `json:"volume_type,omitempty"` + + Size uint64 `json:"size,omitempty"` + // State + // + // Default value: available + State SnapshotState `json:"state,omitempty"` + + BaseVolume *SnapshotBaseVolume `json:"base_volume,omitempty"` + + CreationDate time.Time `json:"creation_date,omitempty"` + + ModificationDate time.Time `json:"modification_date,omitempty"` +} + +// 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.Organization == "" { + defaultOrganization, _ := s.client.GetDefaultProjectID() + 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 utils.Zone `json:"-"` + + SnapshotID string `json:"-"` +} + +// DeleteSnapshot delete 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 utils.Zone `json:"-"` + + Organization *string `json:"-"` + + PerPage *int32 `json:"-"` + + Page *int32 `json:"-"` + + Name *string `json:"-"` +} + +// ListVolumes list volumes +func (s *API) ListVolumes(req *ListVolumesRequest, opts ...scw.RequestOption) (*ListVolumesResponse, error) { + var err error + + defaultOrganization, exist := s.client.GetDefaultProjectID() + if (req.Organization == nil || *req.Organization == "") && exist { + req.Organization = &defaultOrganization + } + + 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) + + 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() int { + return int(r.TotalCount) +} + +// UnsafeAppend should not be used +// Internal usage only +func (r *ListVolumesResponse) UnsafeAppend(res interface{}) (int, scw.SdkError) { + 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 len(results.Volumes), nil +} + +type CreateVolumeRequest struct { + Zone utils.Zone `json:"-"` + + Name string `json:"name,omitempty"` + + Organization string `json:"organization,omitempty"` + // VolumeType + // + // Default value: l_ssd + VolumeType VolumeType `json:"volume_type,omitempty"` + + // Precisely one of BaseSnapshot, BaseVolume, Size must be set. + Size *uint64 `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"` +} + +func (m *CreateVolumeRequest) GetFrom() From { + switch { + case m.Size != nil: + return FromSize{*m.Size} + case m.BaseVolume != nil: + return FromBaseVolume{*m.BaseVolume} + case m.BaseSnapshot != nil: + return FromBaseSnapshot{*m.BaseSnapshot} + } + return nil +} + +// CreateVolume create volume +func (s *API) CreateVolume(req *CreateVolumeRequest, opts ...scw.RequestOption) (*CreateVolumeResponse, error) { + var err error + + if req.Organization == "" { + defaultOrganization, _ := s.client.GetDefaultProjectID() + 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 utils.Zone `json:"-"` + + VolumeID string `json:"-"` +} + +// GetVolume get 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 SetVolumeRequest struct { + Zone utils.Zone `json:"-"` + // ID display the volumes unique ID + ID string `json:"-"` + // Name display the volumes names + Name string `json:"name,omitempty"` + // ExportURI show the volumes NBD export URI + ExportURI string `json:"export_uri,omitempty"` + // Size display the volumes disk size + Size uint64 `json:"size,omitempty"` + // VolumeType display the volumes type + // + // Default value: l_ssd + VolumeType VolumeType `json:"volume_type,omitempty"` + // CreationDate display the volumes creation date + CreationDate time.Time `json:"creation_date,omitempty"` + // ModificationDate display the volumes modification date + ModificationDate time.Time `json:"modification_date,omitempty"` + // Organization display the volumes organization + Organization string `json:"organization,omitempty"` + // Server display information about the server attached to the volume + Server *ServerSummary `json:"server,omitempty"` + // State display the volumes state + // + // Default value: available + State VolumeState `json:"state,omitempty"` +} + +// SetVolume update volume +// +// Replace all volume properties with a volume message +func (s *API) SetVolume(req *SetVolumeRequest, opts ...scw.RequestOption) (*SetVolumeResponse, error) { + var err error + + if req.Organization == "" { + defaultOrganization, _ := s.client.GetDefaultProjectID() + 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) + "/volumes/" + fmt.Sprint(req.ID) + "", + Headers: http.Header{}, + } + + err = scwReq.SetBody(req) + if err != nil { + return nil, err + } + + var resp SetVolumeResponse + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +type DeleteVolumeRequest struct { + Zone utils.Zone `json:"-"` + + VolumeID string `json:"-"` +} + +// DeleteVolume delete 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 utils.Zone `json:"-"` + + Organization *string `json:"-"` + + PerPage *int32 `json:"-"` + + Page *int32 `json:"-"` + + Name *string `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 + + defaultOrganization, exist := s.client.GetDefaultProjectID() + if (req.Organization == nil || *req.Organization == "") && exist { + req.Organization = &defaultOrganization + } + + 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) + + 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() int { + return int(r.TotalCount) +} + +// UnsafeAppend should not be used +// Internal usage only +func (r *ListSecurityGroupsResponse) UnsafeAppend(res interface{}) (int, scw.SdkError) { + 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 len(results.SecurityGroups), nil +} + +type CreateSecurityGroupRequest struct { + Zone utils.Zone `json:"-"` + + Name string `json:"name,omitempty"` + + OrganizationKey string `json:"organization_key,omitempty"` + + OrganizationDefault bool `json:"organization_default,omitempty"` + + Stateful bool `json:"stateful,omitempty"` + // InboundDefaultPolicy + // + // Default value: accept + InboundDefaultPolicy SecurityGroupPolicy `json:"inbound_default_policy,omitempty"` + // OutboundDefaultPolicy + // + // Default value: accept + OutboundDefaultPolicy SecurityGroupPolicy `json:"outbound_default_policy,omitempty"` +} + +// CreateSecurityGroup create security group +func (s *API) CreateSecurityGroup(req *CreateSecurityGroupRequest, opts ...scw.RequestOption) (*CreateSecurityGroupResponse, error) { + var err error + + if req.Zone == "" { + defaultZone, _ := s.client.GetDefaultZone() + req.Zone = defaultZone + } + + if fmt.Sprint(req.Zone) == "" { + return nil, errors.New("field Zone cannot be empty in request") + } + + scwReq := &scw.ScalewayRequest{ + Method: "POST", + Path: "/instance/v1/zones/" + fmt.Sprint(req.Zone) + "/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 utils.Zone `json:"-"` + + SecurityGroupID string `json:"-"` +} + +// GetSecurityGroup get 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 utils.Zone `json:"-"` + + SecurityGroupID string `json:"-"` +} + +// DeleteSecurityGroup delete 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 utils.Zone `json:"-"` + // ID display the security groups' unique ID + ID string `json:"-"` + // Name display the security groups name + Name string `json:"name,omitempty"` + // Description display the security groups description + Description string `json:"description,omitempty"` + // EnableDefaultSecurity display if the security group is set as default + EnableDefaultSecurity bool `json:"enable_default_security,omitempty"` + // InboundDefaultPolicy display the default inbound policy + // + // Default value: accept + InboundDefaultPolicy SecurityGroupPolicy `json:"inbound_default_policy,omitempty"` + // OutboundDefaultPolicy display the default outbound policy + // + // Default value: accept + OutboundDefaultPolicy SecurityGroupPolicy `json:"outbound_default_policy,omitempty"` + // Organization display the security groups organization ID + Organization string `json:"organization,omitempty"` + // OrganizationDefault display if the security group is set as organization default + OrganizationDefault bool `json:"organization_default,omitempty"` + // CreationDate display the security group creation date + CreationDate time.Time `json:"creation_date,omitempty"` + // ModificationDate display the security group modification date + ModificationDate time.Time `json:"modification_date,omitempty"` + // Servers list of servers attached to this security group + Servers []*ServerSummary `json:"servers,omitempty"` + // Stateful true if the security group is stateful + Stateful bool `json:"stateful,omitempty"` +} + +// SetSecurityGroup update security group +// +// Replace all security group properties with a security group message +func (s *API) SetSecurityGroup(req *SetSecurityGroupRequest, opts ...scw.RequestOption) (*UpdateSecurityGroupResponse, error) { + var err error + + if req.Organization == "" { + defaultOrganization, _ := s.client.GetDefaultProjectID() + 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 UpdateSecurityGroupResponse + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +type ListSecurityGroupRulesRequest struct { + Zone utils.Zone `json:"-"` + + SecurityGroupID string `json:"-"` + + PerPage *int32 `json:"-"` + + 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() int { + return int(r.TotalCount) +} + +// UnsafeAppend should not be used +// Internal usage only +func (r *ListSecurityGroupRulesResponse) UnsafeAppend(res interface{}) (int, scw.SdkError) { + results, ok := res.(*ListSecurityGroupRulesResponse) + if !ok { + return 0, errors.New("%T type cannot be appended to type %T", res, r) + } + + r.SecurityRules = append(r.SecurityRules, results.SecurityRules...) + r.TotalCount += uint32(len(results.SecurityRules)) + return len(results.SecurityRules), nil +} + +type CreateSecurityGroupRuleRequest struct { + Zone utils.Zone `json:"-"` + + SecurityGroupID string `json:"-"` + // Protocol + // + // Default value: tcp + Protocol SecurityRuleProtocol `json:"protocol,omitempty"` + // Direction + // + // Default value: inbound + Direction SecurityRuleDirection `json:"direction,omitempty"` + // Action + // + // Default value: accept + Action SecurityRuleAction `json:"action,omitempty"` + + IPRange string `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 utils.Zone `json:"-"` + + SecurityGroupID string `json:"-"` + + SecurityRuleID 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.SecurityRuleID) == "" { + return errors.New("field SecurityRuleID 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.SecurityRuleID) + "", + Headers: http.Header{}, + } + + err = s.client.Do(scwReq, nil, opts...) + if err != nil { + return err + } + return nil +} + +type GetSecurityGroupRuleRequest struct { + Zone utils.Zone `json:"-"` + + SecurityGroupID string `json:"-"` + + SecurityRuleID 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.SecurityRuleID) == "" { + return nil, errors.New("field SecurityRuleID 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.SecurityRuleID) + "", + Headers: http.Header{}, + } + + var resp GetSecurityGroupRuleResponse + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +type ListIpsRequest struct { + Zone utils.Zone `json:"-"` + + Organization string `json:"-"` + + Name *string `json:"-"` + + PerPage *int32 `json:"-"` + + Page *int32 `json:"-"` +} + +// ListIps list IPs +func (s *API) ListIps(req *ListIpsRequest, opts ...scw.RequestOption) (*ListIpsResponse, error) { + var err error + + if req.Organization == "" { + defaultOrganization, _ := s.client.GetDefaultProjectID() + req.Organization = defaultOrganization + } + + 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, "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() int { + return int(r.TotalCount) +} + +// UnsafeAppend should not be used +// Internal usage only +func (r *ListIpsResponse) UnsafeAppend(res interface{}) (int, scw.SdkError) { + 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 len(results.Ips), nil +} + +type CreateIPRequest struct { + Zone utils.Zone `json:"-"` + + Organization string `json:"organization,omitempty"` + + Server *string `json:"server,omitempty"` +} + +// CreateIP reseve an IP +func (s *API) CreateIP(req *CreateIPRequest, opts ...scw.RequestOption) (*CreateIPResponse, error) { + var err error + + if req.Organization == "" { + defaultOrganization, _ := s.client.GetDefaultProjectID() + 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 utils.Zone `json:"-"` + + IPID string `json:"-"` +} + +// GetIP get IP +// +// Get details of an IP with the given id +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.IPID) == "" { + return nil, errors.New("field IPID cannot be empty in request") + } + + scwReq := &scw.ScalewayRequest{ + Method: "GET", + Path: "/instance/v1/zones/" + fmt.Sprint(req.Zone) + "/ips/" + fmt.Sprint(req.IPID) + "", + Headers: http.Header{}, + } + + var resp GetIPResponse + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +type SetIPRequest struct { + Zone utils.Zone `json:"-"` + + ID string `json:"-"` + + Address net.IP `json:"address,omitempty"` + + Reverse *string `json:"reverse,omitempty"` + + Server *ServerSummary `json:"server,omitempty"` + + Organization string `json:"organization,omitempty"` +} + +func (s *API) SetIP(req *SetIPRequest, opts ...scw.RequestOption) (*SetIPResponse, error) { + var err error + + if req.Organization == "" { + defaultOrganization, _ := s.client.GetDefaultProjectID() + 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) + "/ips/" + fmt.Sprint(req.ID) + "", + Headers: http.Header{}, + } + + err = scwReq.SetBody(req) + if err != nil { + return nil, err + } + + var resp SetIPResponse + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +type updateIPRequest struct { + Zone utils.Zone `json:"-"` + + IPID string `json:"-"` + + Reverse **string `json:"reverse,omitempty"` + + Server **string `json:"server,omitempty"` +} + +// updateIP update 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.IPID) == "" { + return nil, errors.New("field IPID cannot be empty in request") + } + + scwReq := &scw.ScalewayRequest{ + Method: "PATCH", + Path: "/instance/v1/zones/" + fmt.Sprint(req.Zone) + "/ips/" + fmt.Sprint(req.IPID) + "", + 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 utils.Zone `json:"-"` + + IPID string `json:"-"` +} + +// DeleteIP delete 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.IPID) == "" { + return errors.New("field IPID cannot be empty in request") + } + + scwReq := &scw.ScalewayRequest{ + Method: "DELETE", + Path: "/instance/v1/zones/" + fmt.Sprint(req.Zone) + "/ips/" + fmt.Sprint(req.IPID) + "", + Headers: http.Header{}, + } + + err = s.client.Do(scwReq, nil, opts...) + if err != nil { + return err + } + return nil +} + +type ListBootscriptsRequest struct { + Zone utils.Zone `json:"-"` + + Arch *string `json:"-"` + + Title *string `json:"-"` + + Default *bool `json:"-"` + + Public *bool `json:"-"` + + PerPage *int32 `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() int { + return int(r.TotalCount) +} + +// UnsafeAppend should not be used +// Internal usage only +func (r *ListBootscriptsResponse) UnsafeAppend(res interface{}) (int, scw.SdkError) { + 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 len(results.Bootscripts), nil +} + +type GetBootscriptRequest struct { + Zone utils.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 GetServiceInfoRequest struct { + Zone utils.Zone `json:"-"` +} + +func (s *API) GetServiceInfo(req *GetServiceInfoRequest, opts ...scw.RequestOption) (*GetServiceInfoResponse, error) { + var err error + + if req.Zone == "" { + defaultZone, _ := s.client.GetDefaultZone() + req.Zone = defaultZone + } + + if fmt.Sprint(req.Zone) == "" { + return nil, errors.New("field Zone cannot be empty in request") + } + + scwReq := &scw.ScalewayRequest{ + Method: "GET", + Path: "/instance/v1/zones/" + fmt.Sprint(req.Zone) + "", + Headers: http.Header{}, + } + + var resp GetServiceInfoResponse + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +type GetDashboardRequest struct { + Zone utils.Zone `json:"-"` + + Organization *string `json:"-"` +} + +func (s *API) GetDashboard(req *GetDashboardRequest, opts ...scw.RequestOption) (*GetDashboardResponse, error) { + var err error + + defaultOrganization, exist := s.client.GetDefaultProjectID() + if (req.Organization == nil || *req.Organization == "") && exist { + req.Organization = &defaultOrganization + } + + if req.Zone == "" { + defaultZone, _ := s.client.GetDefaultZone() + req.Zone = defaultZone + } + + query := url.Values{} + parameter.AddToQuery(query, "organization", req.Organization) + + 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 +} + +type From interface { + isFrom() +} + +type FromSize struct { + Value uint64 +} + +func (FromSize) isFrom() { +} + +type FromBaseVolume struct { + Value string +} + +func (FromBaseVolume) isFrom() { +} + +type FromBaseSnapshot struct { + Value string +} + +func (FromBaseSnapshot) isFrom() { +} diff --git a/src/cmd/linuxkit/vendor/github.com/scaleway/scaleway-sdk-go/api/instance/v1/instance_utils.go b/src/cmd/linuxkit/vendor/github.com/scaleway/scaleway-sdk-go/api/instance/v1/instance_utils.go new file mode 100644 index 000000000..946ae4767 --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/scaleway/scaleway-sdk-go/api/instance/v1/instance_utils.go @@ -0,0 +1,255 @@ +package instance + +import ( + "fmt" + + "github.com/scaleway/scaleway-sdk-go/scw" + "github.com/scaleway/scaleway-sdk-go/utils" +) + +// AttachIPRequest contains the parameters to attach an IP to a server +type AttachIPRequest struct { + Zone utils.Zone `json:"-"` + IPID string `json:"-"` + ServerID string `json:"server_id"` +} + +// AttachIPResponse contains the updated IP after attaching +type AttachIPResponse struct { + IP *IP +} + +// AttachIP attaches an IP to a server. +func (s *API) AttachIP(req *AttachIPRequest, opts ...scw.RequestOption) (*AttachIPResponse, error) { + var ptrServerID = &req.ServerID + ipResponse, err := s.updateIP(&updateIPRequest{ + Zone: req.Zone, + IPID: req.IPID, + Server: &ptrServerID, + }) + if err != nil { + return nil, err + } + + return &AttachIPResponse{IP: ipResponse.IP}, nil +} + +// DetachIPRequest contains the parameters to detach an IP from a server +type DetachIPRequest struct { + Zone utils.Zone `json:"-"` + IPID string `json:"-"` +} + +// DetachIPResponse contains the updated IP after detaching +type DetachIPResponse struct { + IP *IP +} + +// DetachIP detaches an IP from a server. +func (s *API) DetachIP(req *DetachIPRequest, opts ...scw.RequestOption) (*DetachIPResponse, error) { + var ptrServerID *string + ipResponse, err := s.updateIP(&updateIPRequest{ + Zone: req.Zone, + IPID: req.IPID, + Server: &ptrServerID, + }) + if err != nil { + return nil, err + } + + return &DetachIPResponse{IP: ipResponse.IP}, nil +} + +// UpdateIPRequest contains the parameters to update an IP +// if Reverse is an empty string, the reverse will be removed +type UpdateIPRequest struct { + Zone utils.Zone `json:"-"` + IPID string `json:"-"` + Reverse *string `json:"reverse"` +} + +// UpdateIP updates an IP +func (s *API) UpdateIP(req *UpdateIPRequest, opts ...scw.RequestOption) (*UpdateIPResponse, error) { + var reverse **string + if req.Reverse != nil { + if *req.Reverse == "" { + req.Reverse = nil + } + reverse = &req.Reverse + } + ipResponse, err := s.updateIP(&updateIPRequest{ + Zone: req.Zone, + IPID: req.IPID, + Reverse: reverse, + }) + if err != nil { + return nil, err + } + + return &UpdateIPResponse{IP: ipResponse.IP}, nil +} + +// AttachVolumeRequest contains the parameters to attach a volume to a server +type AttachVolumeRequest struct { + Zone utils.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 +func (s *API) AttachVolume(req *AttachVolumeRequest, opts ...scw.RequestOption) (*AttachVolumeResponse, error) { + // 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 + key := fmt.Sprintf("%d", len(volumes)) + newVolumes[key] = &VolumeTemplate{ + ID: req.VolumeID, + // name is ignored on this PATCH + Name: 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 utils.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 +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, fmt.Errorf("expected volume to have value in response") + } + if getVolumeResponse.Volume.Server == nil { + return nil, fmt.Errorf("server should be attached to a server") + } + serverID := getVolumeResponse.Volume.Server.ID + + // 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) +} diff --git a/src/cmd/linuxkit/vendor/github.com/scaleway/scaleway-sdk-go/api/instance/v1/server_utils.go b/src/cmd/linuxkit/vendor/github.com/scaleway/scaleway-sdk-go/api/instance/v1/server_utils.go new file mode 100644 index 000000000..309117583 --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/scaleway/scaleway-sdk-go/api/instance/v1/server_utils.go @@ -0,0 +1,53 @@ +package instance + +import ( + "time" + + "github.com/scaleway/scaleway-sdk-go/internal/async" + "github.com/scaleway/scaleway-sdk-go/utils" +) + +// WaitForServerRequest is used by WaitForServer method +type WaitForServerRequest struct { + ServerID string + Zone utils.Zone + + // Timeout: maximum time to wait before (default: 5 minutes) + Timeout 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) { + + if req.Timeout == 0 { + req.Timeout = 5 * time.Minute + } + + terminalStatus := map[ServerState]struct{}{ + ServerStateStopped: {}, + ServerStateStoppedInPlace: {}, + ServerStateLocked: {}, + ServerStateRunning: {}, + } + + server, err := async.WaitSync(&async.WaitSyncConfig{ + Get: func() (interface{}, error, bool) { + res, err := s.GetServer(&GetServerRequest{ + ServerID: req.ServerID, + Zone: req.Zone, + }) + + if err != nil { + return nil, err, false + } + _, isTerminal := terminalStatus[res.Server.State] + + return res.Server, err, isTerminal + }, + Timeout: req.Timeout, + IntervalStrategy: async.LinearIntervalStrategy(5 * time.Second), + }) + + return server.(*Server), err +} diff --git a/src/cmd/linuxkit/vendor/github.com/scaleway/scaleway-sdk-go/api/marketplace/v1/marketplace_sdk.go b/src/cmd/linuxkit/vendor/github.com/scaleway/scaleway-sdk-go/api/marketplace/v1/marketplace_sdk.go new file mode 100644 index 000000000..58c8f1af2 --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/scaleway/scaleway-sdk-go/api/marketplace/v1/marketplace_sdk.go @@ -0,0 +1,265 @@ +// This file was automatically generated. DO NOT EDIT. +// If you have any remark or suggestion do not hesitate to open an issue. + +package marketplace + +import ( + "bytes" + "encoding/json" + "fmt" + "net" + "net/http" + "net/url" + "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/scw" + "github.com/scaleway/scaleway-sdk-go/utils" +) + +// always import dependencies +var ( + _ fmt.Stringer + _ json.Unmarshaler + _ url.URL + _ net.IP + _ http.Header + _ bytes.Reader + _ time.Time + + _ scw.ScalewayRequest + _ marshaler.Duration + _ utils.File + _ = parameter.AddToQuery +) + +// 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,omitempty"` +} + +type GetServiceInfoResponse struct { + API string `json:"api,omitempty"` + + Description string `json:"description,omitempty"` + + Version string `json:"version,omitempty"` +} + +type GetVersionResponse struct { + Version *Version `json:"version,omitempty"` +} + +type Image struct { + ID string `json:"id,omitempty"` + + Name string `json:"name,omitempty"` + + Description string `json:"description,omitempty"` + + Logo string `json:"logo,omitempty"` + + Categories []string `json:"categories,omitempty"` + + Organization *Organization `json:"organization,omitempty"` + + ValidUntil time.Time `json:"valid_until,omitempty"` + + CreationDate time.Time `json:"creation_date,omitempty"` + + ModificationDate time.Time `json:"modification_date,omitempty"` + + Versions []*Version `json:"versions,omitempty"` + + CurrentPublicVersion string `json:"current_public_version,omitempty"` +} + +type ListImagesResponse struct { + Images []*Image `json:"images,omitempty"` +} + +type ListVersionsResponse struct { + Versions []*Version `json:"versions,omitempty"` +} + +type LocalImage struct { + ID string `json:"id,omitempty"` + + Arch string `json:"arch,omitempty"` + + Zone utils.Zone `json:"zone,omitempty"` + + CompatibleCommercialTypes []string `json:"compatible_commercial_types,omitempty"` +} + +type Organization struct { + ID string `json:"id,omitempty"` + + Name string `json:"name,omitempty"` +} + +type Version struct { + ID string `json:"id,omitempty"` + + Name string `json:"name,omitempty"` + + CreationDate time.Time `json:"creation_date,omitempty"` + + ModificationDate time.Time `json:"modification_date,omitempty"` + + LocalImages []*LocalImage `json:"local_images,omitempty"` +} + +// 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 *int32 `json:"-"` + + Page *int32 `json:"-"` +} + +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 +} + +type GetImageRequest struct { + ImageID string `json:"-"` +} + +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/src/cmd/linuxkit/vendor/github.com/scaleway/scaleway-sdk-go/api/marketplace/v1/marketplace_utils.go b/src/cmd/linuxkit/vendor/github.com/scaleway/scaleway-sdk-go/api/marketplace/v1/marketplace_utils.go new file mode 100644 index 000000000..a8c90e55b --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/scaleway/scaleway-sdk-go/api/marketplace/v1/marketplace_utils.go @@ -0,0 +1,81 @@ +package marketplace + +import ( + "fmt" + + "github.com/scaleway/scaleway-sdk-go/utils" +) + +// getLocalImage returns the correct local version of an image matching +// the current zone and the compatible commercial type +func (version *Version) getLocalImage(zone utils.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, fmt.Errorf("latest version could not be found for image %s", image.Name) +} + +// FindLocalImageIDByName search for an image with the given name (exact match) in the given region +// it returns the latest version of this specific image. +func (s *API) FindLocalImageIDByName(imageName string, zone utils.Zone, commercialType string) (string, error) { + + listImageRequest := &ListImagesRequest{} + listImageResponse, err := s.ListImages(listImageRequest) + if err != nil { + return "", err + } + + // TODO: handle pagination + + images := listImageResponse.Images + _ = images + + for _, image := range images { + + // Match name of the image + if image.Name == imageName { + + latestVersion, err := image.getLatestVersion() + if err != nil { + return "", fmt.Errorf("couldn't find a matching image for the given name (%s), zone (%s) and commercial type (%s): %s", imageName, zone, commercialType, err) + } + + localImage, err := latestVersion.getLocalImage(zone, commercialType) + if err != nil { + return "", fmt.Errorf("couldn't find a matching image for the given name (%s), zone (%s) and commercial type (%s): %s", imageName, zone, commercialType, err) + } + + return localImage.ID, nil + } + + } + + return "", fmt.Errorf("couldn't find a matching image for the given name (%s), zone (%s) and commercial type (%s)", imageName, zone, commercialType) +} diff --git a/src/cmd/linuxkit/vendor/github.com/scaleway/scaleway-sdk-go/go.mod b/src/cmd/linuxkit/vendor/github.com/scaleway/scaleway-sdk-go/go.mod new file mode 100644 index 000000000..fd2a5db28 --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/scaleway/scaleway-sdk-go/go.mod @@ -0,0 +1,8 @@ +module github.com/scaleway/scaleway-sdk-go + +go 1.12 + +require ( + github.com/dnaeon/go-vcr v1.0.1 + gopkg.in/yaml.v2 v2.2.2 +) diff --git a/src/cmd/linuxkit/vendor/github.com/scaleway/scaleway-sdk-go/internal/async/wait.go b/src/cmd/linuxkit/vendor/github.com/scaleway/scaleway-sdk-go/internal/async/wait.go new file mode 100644 index 000000000..60fcb83e9 --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/scaleway/scaleway-sdk-go/internal/async/wait.go @@ -0,0 +1,89 @@ +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{}, err error, isTerminal bool) + 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, err, stopCondition := 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/src/cmd/linuxkit/vendor/github.com/scaleway/scaleway-sdk-go/internal/auth/auth.go b/src/cmd/linuxkit/vendor/github.com/scaleway/scaleway-sdk-go/internal/auth/auth.go new file mode 100644 index 000000000..f7ee31fcb --- /dev/null +++ b/src/cmd/linuxkit/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/src/cmd/linuxkit/vendor/github.com/scaleway/scaleway-sdk-go/internal/auth/no_auth.go b/src/cmd/linuxkit/vendor/github.com/scaleway/scaleway-sdk-go/internal/auth/no_auth.go new file mode 100644 index 000000000..f7ec2409d --- /dev/null +++ b/src/cmd/linuxkit/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/src/cmd/linuxkit/vendor/github.com/scaleway/scaleway-sdk-go/internal/auth/token.go b/src/cmd/linuxkit/vendor/github.com/scaleway/scaleway-sdk-go/internal/auth/token.go new file mode 100644 index 000000000..ec19d1c09 --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/scaleway/scaleway-sdk-go/internal/auth/token.go @@ -0,0 +1,42 @@ +package auth + +import "net/http" + +type token struct { + accessKey string + secretKey string +} + +// XAuthTokenHeader is Scaleway standard auth header +const XAuthTokenHeader = "X-Auth-Token" + +// 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{} + var secret string + + switch { + case len(t.secretKey) == 0: + secret = "" + case len(t.secretKey) > 8: + secret = t.secretKey[0:8] + "-xxxx-xxxx-xxxx-xxxxxxxxxxxx" + default: + secret = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" + } + headers.Set(XAuthTokenHeader, secret) + return headers +} diff --git a/src/cmd/linuxkit/vendor/github.com/scaleway/scaleway-sdk-go/internal/errors/error.go b/src/cmd/linuxkit/vendor/github.com/scaleway/scaleway-sdk-go/internal/errors/error.go new file mode 100644 index 000000000..bc126bed7 --- /dev/null +++ b/src/cmd/linuxkit/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/src/cmd/linuxkit/vendor/github.com/scaleway/scaleway-sdk-go/internal/marshaler/duration.go b/src/cmd/linuxkit/vendor/github.com/scaleway/scaleway-sdk-go/internal/marshaler/duration.go new file mode 100644 index 000000000..5b07ee9cb --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/scaleway/scaleway-sdk-go/internal/marshaler/duration.go @@ -0,0 +1,139 @@ +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 +} diff --git a/src/cmd/linuxkit/vendor/github.com/scaleway/scaleway-sdk-go/internal/parameter/query.go b/src/cmd/linuxkit/vendor/github.com/scaleway/scaleway-sdk-go/internal/parameter/query.go new file mode 100644 index 000000000..d72854d25 --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/scaleway/scaleway-sdk-go/internal/parameter/query.go @@ -0,0 +1,31 @@ +package parameter + +import ( + "fmt" + "net/url" + "reflect" +) + +// 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())) + } + default: + query.Add(key, fmt.Sprint(elemValue.Interface())) + } + +} diff --git a/src/cmd/linuxkit/vendor/github.com/scaleway/scaleway-sdk-go/logger/default_logger.go b/src/cmd/linuxkit/vendor/github.com/scaleway/scaleway-sdk-go/logger/default_logger.go new file mode 100644 index 000000000..6636af29d --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/scaleway/scaleway-sdk-go/logger/default_logger.go @@ -0,0 +1,102 @@ +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/src/cmd/linuxkit/vendor/github.com/scaleway/scaleway-sdk-go/logger/logger.go b/src/cmd/linuxkit/vendor/github.com/scaleway/scaleway-sdk-go/logger/logger.go new file mode 100644 index 000000000..834f765ea --- /dev/null +++ b/src/cmd/linuxkit/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/src/cmd/linuxkit/vendor/github.com/scaleway/scaleway-sdk-go/scw/client.go b/src/cmd/linuxkit/vendor/github.com/scaleway/scaleway-sdk-go/scw/client.go new file mode 100644 index 000000000..0a462c038 --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/scaleway/scaleway-sdk-go/scw/client.go @@ -0,0 +1,304 @@ +package scw + +import ( + "crypto/tls" + "encoding/json" + "net" + "net/http" + "net/http/httputil" + "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" + "github.com/scaleway/scaleway-sdk-go/utils" +) + +// 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 + defaultProjectID *string + defaultRegion *utils.Region + defaultZone *utils.Zone + defaultPageSize *int32 +} + +func defaultOptions() []ClientOption { + return []ClientOption{ + 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, + defaultProjectID: s.defaultProjectID, + defaultRegion: s.defaultRegion, + defaultZone: s.defaultZone, + defaultPageSize: s.defaultPageSize, + }, nil +} + +// GetDefaultProjectID return 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() (string, bool) { + if c.defaultProjectID != nil { + return *c.defaultProjectID, true + } + return "", false +} + +// GetDefaultRegion return 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() (utils.Region, bool) { + if c.defaultRegion != nil { + return *c.defaultRegion, true + } + return utils.Region(""), false +} + +// GetDefaultZone return 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() (utils.Zone, bool) { + if c.defaultZone != nil { + return *c.defaultZone, true + } + return utils.Zone(""), false +} + +// GetDefaultPageSize return 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() (int32, 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) { + requestSettings := newRequestSettings() + + // apply request options + requestSettings.apply(opts) + + // validate request options + err = requestSettings.validate() + if err != nil { + return err + } + + if requestSettings.ctx != nil { + req.Ctx = requestSettings.ctx + } + + if requestSettings.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 SdkError) { + + 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(c.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(c.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 { + err = json.NewDecoder(httpResponse.Body).Decode(&res) + if err != nil { + return errors.Wrap(err, "could not parse response body") + } + + // 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 +} + +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 +} + +func hasResponseError(res *http.Response) SdkError { + if res.StatusCode >= 200 && res.StatusCode <= 299 { + return nil + } + + newErr := &ResponseError{ + StatusCode: res.StatusCode, + Status: res.Status, + } + + if res.Body == nil { + return newErr + } + + err := json.NewDecoder(res.Body).Decode(newErr) + if err != nil { + return errors.Wrap(err, "could not parse error response body") + } + + return newErr +} diff --git a/src/cmd/linuxkit/vendor/github.com/scaleway/scaleway-sdk-go/scw/client_option.go b/src/cmd/linuxkit/vendor/github.com/scaleway/scaleway-sdk-go/scw/client_option.go new file mode 100644 index 000000000..b59cb51e8 --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/scaleway/scaleway-sdk-go/scw/client_option.go @@ -0,0 +1,142 @@ +package scw + +import ( + "net/http" + + "github.com/scaleway/scaleway-sdk-go/internal/auth" + "github.com/scaleway/scaleway-sdk-go/scwconfig" + "github.com/scaleway/scaleway-sdk-go/utils" +) + +// 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 + } +} + +// WithConfig client option configure a client with Scaleway configuration. +func WithConfig(config scwconfig.Config) ClientOption { + return func(s *settings) { + // The access key is not used for API authentications. + accessKey, _ := config.GetAccessKey() + secretKey, secretKeyExist := config.GetSecretKey() + if secretKeyExist { + s.token = auth.NewToken(accessKey, secretKey) + } + + apiURL, exist := config.GetAPIURL() + if exist { + s.apiURL = apiURL + } + + insecure, exist := config.GetInsecure() + if exist { + s.insecure = insecure + } + + defaultProjectID, exist := config.GetDefaultProjectID() + if exist { + s.defaultProjectID = &defaultProjectID + } + + defaultRegion, exist := config.GetDefaultRegion() + if exist { + s.defaultRegion = &defaultRegion + } + + defaultZone, exist := config.GetDefaultZone() + if exist { + s.defaultZone = &defaultZone + } + } +} + +// WithDefaultProjectID client option sets the client default project ID. +// +// It will be used as the default value of the project_id 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 utils.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 utils.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 int32) ClientOption { + return func(s *settings) { + s.defaultPageSize = &pageSize + } +} diff --git a/src/cmd/linuxkit/vendor/github.com/scaleway/scaleway-sdk-go/scw/errors.go b/src/cmd/linuxkit/vendor/github.com/scaleway/scaleway-sdk-go/scw/errors.go new file mode 100644 index 000000000..24fa93556 --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/scaleway/scaleway-sdk-go/scw/errors.go @@ -0,0 +1,46 @@ +package scw + +import ( + "fmt" +) + +// 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 + 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:"-"` + + // Status is the HTTP status received + Status string `json:"-"` +} + +func (e *ResponseError) Error() string { + s := fmt.Sprintf("scaleway-sdk-go: http error %s", e.Status) + + 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 +} + +// IsScwSdkError implement SdkError interface +func (e *ResponseError) IsScwSdkError() {} diff --git a/src/cmd/linuxkit/vendor/github.com/scaleway/scaleway-sdk-go/scw/lister.go b/src/cmd/linuxkit/vendor/github.com/scaleway/scaleway-sdk-go/scw/lister.go new file mode 100644 index 000000000..8867a4c2e --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/scaleway/scaleway-sdk-go/scw/lister.go @@ -0,0 +1,64 @@ +package scw + +import ( + "math" + "reflect" + "strconv" + + "github.com/scaleway/scaleway-sdk-go/internal/errors" +) + +type lister interface { + UnsafeGetTotalCount() int + UnsafeAppend(interface{}) (int, SdkError) +} + +type legacyLister interface { + UnsafeSetTotalCount(totalCount int) +} + +// 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 SdkError) { + + // check for lister interface + if response, isLister := res.(lister); isLister { + + pageCount := math.MaxUint32 + for page := 1; page <= pageCount; page++ { + // set current page + req.Query.Set("page", strconv.Itoa(page)) + + // request the next page + nextPage := newPage(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 == math.MaxUint32 { + totalCount := nextPage.(lister).UnsafeGetTotalCount() + pageCount = (totalCount + pageSize - 1) / pageSize + } + } + return nil + } + + return errors.New("%T does not support pagination", res) +} + +// newPage returns a variable set to the zero value of the given type +func newPage(v interface{}) interface{} { + // reflect.New always create a pointer, that's why we use reflect.Indirect before + return reflect.New(reflect.Indirect(reflect.ValueOf(v)).Type()).Interface() +} diff --git a/src/cmd/linuxkit/vendor/github.com/scaleway/scaleway-sdk-go/scw/request.go b/src/cmd/linuxkit/vendor/github.com/scaleway/scaleway-sdk-go/scw/request.go new file mode 100644 index 000000000..12ca85a1e --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/scaleway/scaleway-sdk-go/scw/request.go @@ -0,0 +1,82 @@ +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" + "github.com/scaleway/scaleway-sdk-go/utils" +) + +// 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 + Ctx context.Context +} + +// 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 { + for _, v := range value { + allHeaders.Set(key, v) + } + } + + return allHeaders +} + +// getURL constructs a URL based on the base url and the client. +func (req *ScalewayRequest) getURL(baseURL string) (*url.URL, SdkError) { + 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 *utils.File: + contentType = b.ContentType + content = b.Content + default: + buf, err := json.Marshal(body) + if err != nil { + return err + } + contentType = "application/json" + content = bytes.NewReader(buf) + } + + req.Headers.Set("Content-Type", contentType) + req.Body = content + + return nil +} diff --git a/src/cmd/linuxkit/vendor/github.com/scaleway/scaleway-sdk-go/scw/request_option.go b/src/cmd/linuxkit/vendor/github.com/scaleway/scaleway-sdk-go/scw/request_option.go new file mode 100644 index 000000000..a3af9b549 --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/scaleway/scaleway-sdk-go/scw/request_option.go @@ -0,0 +1,43 @@ +package scw + +import ( + "context" +) + +// RequestOption is a function that applies options to a ScalewayRequest. +type RequestOption func(*requestSettings) + +// WithContext request option sets the context of a ScalewayRequest +func WithContext(ctx context.Context) RequestOption { + return func(s *requestSettings) { + 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 *requestSettings) { + s.allPages = true + } +} + +type requestSettings struct { + ctx context.Context + allPages bool +} + +func newRequestSettings() *requestSettings { + return &requestSettings{} +} + +func (s *requestSettings) apply(opts []RequestOption) { + for _, opt := range opts { + opt(s) + } +} + +func (s *requestSettings) validate() SdkError { + // nothing so far + return nil +} diff --git a/src/cmd/linuxkit/vendor/github.com/scaleway/scaleway-sdk-go/scw/settings.go b/src/cmd/linuxkit/vendor/github.com/scaleway/scaleway-sdk-go/scw/settings.go new file mode 100644 index 000000000..7607fc5ae --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/scaleway/scaleway-sdk-go/scw/settings.go @@ -0,0 +1,64 @@ +package scw + +import ( + "fmt" + "net/url" + + "github.com/scaleway/scaleway-sdk-go/internal/auth" + "github.com/scaleway/scaleway-sdk-go/utils" +) + +type settings struct { + apiURL string + token auth.Auth + userAgent string + httpClient httpClient + insecure bool + defaultProjectID *string + defaultRegion *utils.Region + defaultZone *utils.Zone + defaultPageSize *int32 +} + +func newSettings() *settings { + return &settings{} +} + +func (s *settings) apply(opts []ClientOption) { + for _, opt := range opts { + opt(s) + } +} + +func (s *settings) validate() error { + var err error + if s.token == nil { + return fmt.Errorf("no credential option provided") + } + + _, err = url.Parse(s.apiURL) + if err != nil { + return fmt.Errorf("invalid url %s: %s", s.apiURL, err) + } + + // TODO: Check ProjectID format + if s.defaultProjectID != nil && *s.defaultProjectID == "" { + return fmt.Errorf("default project id cannot be empty") + } + + // TODO: Check Region format + if s.defaultRegion != nil && *s.defaultRegion == "" { + return fmt.Errorf("default region cannot be empty") + } + + // TODO: Check Zone format + if s.defaultZone != nil && *s.defaultZone == "" { + return fmt.Errorf("default zone cannot be empty") + } + + if s.defaultPageSize != nil && *s.defaultPageSize <= 0 { + return fmt.Errorf("default page size cannot be <= 0") + } + + return nil +} diff --git a/src/cmd/linuxkit/vendor/github.com/scaleway/scaleway-sdk-go/scw/version.go b/src/cmd/linuxkit/vendor/github.com/scaleway/scaleway-sdk-go/scw/version.go new file mode 100644 index 000000000..d961ada74 --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/scaleway/scaleway-sdk-go/scw/version.go @@ -0,0 +1,11 @@ +package scw + +import ( + "fmt" + "runtime" +) + +// TODO: versionning process +const version = "0.0.0" + +var userAgent = fmt.Sprintf("scaleway-sdk-go/%s (%s; %s; %s)", version, runtime.Version(), runtime.GOOS, runtime.GOARCH) diff --git a/src/cmd/linuxkit/vendor/github.com/scaleway/scaleway-sdk-go/scwconfig/README.md b/src/cmd/linuxkit/vendor/github.com/scaleway/scaleway-sdk-go/scwconfig/README.md new file mode 100644 index 000000000..0accb8db6 --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/scaleway/scaleway-sdk-go/scwconfig/README.md @@ -0,0 +1,48 @@ +# 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_project_id: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx +default_region: fr-par +default_zone: fr-par-1 +``` + +## Config file path + +This package 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. Home directory: `$HOME/.config/scw/config.yaml` (`%USERPROFILE%/.config/scw/config.yaml` on windows) + +## V1 config (DEPRECATED) + +The V1 config `.scwrc` is supported but deprecated. +When found in the home directory, the V1 config is automatically migrated to a V2 config file in `$HOME/.config/scw/config.yaml`. + +## Reading config order + +When getting the value of a config field, the following priority order will be respected: + +1. Environment variable +2. Legacy environment variable +3. Config file V2 +4. Config file V1 + +## 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_PROJECT_ID` | Your default project 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) | diff --git a/src/cmd/linuxkit/vendor/github.com/scaleway/scaleway-sdk-go/scwconfig/config.go b/src/cmd/linuxkit/vendor/github.com/scaleway/scaleway-sdk-go/scwconfig/config.go new file mode 100644 index 000000000..540d4cbd0 --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/scaleway/scaleway-sdk-go/scwconfig/config.go @@ -0,0 +1,423 @@ +package scwconfig + +import ( + "encoding/json" + "fmt" + "os" + "strconv" + + "github.com/scaleway/scaleway-sdk-go/logger" + "github.com/scaleway/scaleway-sdk-go/utils" + "gopkg.in/yaml.v2" +) + +// Environment variables +const ( + // Up-to-date + scwConfigPathEnv = "SCW_CONFIG_PATH" + scwAccessKeyEnv = "SCW_ACCESS_KEY" + scwSecretKeyEnv = "SCW_SECRET_KEY" + scwActiveProfileEnv = "SCW_PROFILE" + scwAPIURLEnv = "SCW_API_URL" + scwInsecureEnv = "SCW_INSECURE" + 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" +) + +// Config interface is made of getters to retrieve +// the config field by field. +type Config interface { + GetAccessKey() (accessKey string, exist bool) + GetSecretKey() (secretKey string, exist bool) + GetAPIURL() (apiURL string, exist bool) + GetInsecure() (insecure bool, exist bool) + GetDefaultProjectID() (defaultProjectID string, exist bool) + GetDefaultRegion() (defaultRegion utils.Region, exist bool) + GetDefaultZone() (defaultZone utils.Zone, exist bool) +} + +type configV2 struct { + profile `yaml:",inline"` + ActiveProfile *string `yaml:"active_profile,omitempty"` + Profiles map[string]*profile `yaml:"profiles,omitempty"` + + // withProfile is used by LoadWithProfile to handle the following priority order: + // c.withProfile > os.Getenv("SCW_PROFILE") > c.ActiveProfile + withProfile string +} + +type profile struct { + AccessKey *string `yaml:"access_key,omitempty"` + SecretKey *string `yaml:"secret_key,omitempty"` + APIURL *string `yaml:"api_url,omitempty"` + Insecure *bool `yaml:"insecure,omitempty"` + DefaultProjectID *string `yaml:"default_project_id,omitempty"` + DefaultRegion *string `yaml:"default_region,omitempty"` + DefaultZone *string `yaml:"default_zone,omitempty"` +} + +func unmarshalConfV2(content []byte) (*configV2, error) { + var config configV2 + + err := yaml.Unmarshal(content, &config) + if err != nil { + return nil, err + } + return &config, nil +} + +func (c *configV2) catchInvalidProfile() (*configV2, error) { + activeProfile, err := c.getActiveProfile() + if err != nil { + return nil, err + } + if activeProfile == "" { + return c, nil + } + + _, exist := c.Profiles[activeProfile] + if !exist { + return nil, fmt.Errorf("profile %s does not exist %s", activeProfile, inConfigFile()) + } + return c, nil +} + +func (c *configV2) getActiveProfile() (string, error) { + switch { + case c.withProfile != "": + return c.withProfile, nil + case os.Getenv(scwActiveProfileEnv) != "": + return os.Getenv(scwActiveProfileEnv), nil + case c.ActiveProfile != nil: + if *c.ActiveProfile == "" { + return "", fmt.Errorf("active_profile key cannot be empty %s", inConfigFile()) + } + return *c.ActiveProfile, nil + default: + return "", nil + } +} + +// GetAccessKey retrieve the access key from the config. +// It will check the following order: +// env, legacy env, active profile, default profile +// +// If the config is present in one of the above environment the +// value (which may be empty) is returned and the boolean is true. +// Otherwise the returned value will be empty and the boolean will +// be false. +func (c *configV2) GetAccessKey() (string, bool) { + envValue, _, envExist := getenv(scwAccessKeyEnv, terraformAccessKeyEnv) + activeProfile, _ := c.getActiveProfile() + + var accessKey string + switch { + case envExist: + accessKey = envValue + case activeProfile != "" && c.Profiles[activeProfile].AccessKey != nil: + accessKey = *c.Profiles[activeProfile].AccessKey + case c.AccessKey != nil: + accessKey = *c.AccessKey + default: + logger.Warningf("no access key found") + return "", false + } + + if accessKey == "" { + logger.Warningf("access key is empty") + } + + return accessKey, true +} + +// GetSecretKey retrieve the secret key from the config. +// It will check the following order: +// env, legacy env, active profile, default profile +// +// If the config is present in one of the above environment the +// value (which may be empty) is returned and the boolean is true. +// Otherwise the returned value will be empty and the boolean will +// be false. +func (c *configV2) GetSecretKey() (string, bool) { + envValue, _, envExist := getenv(scwSecretKeyEnv, cliSecretKeyEnv, terraformSecretKeyEnv, terraformAccessKeyEnv) + activeProfile, _ := c.getActiveProfile() + + var secretKey string + switch { + case envExist: + secretKey = envValue + case activeProfile != "" && c.Profiles[activeProfile].SecretKey != nil: + secretKey = *c.Profiles[activeProfile].SecretKey + case c.SecretKey != nil: + secretKey = *c.SecretKey + default: + logger.Warningf("no secret key found") + return "", false + } + + if secretKey == "" { + logger.Warningf("secret key is empty") + } + + return secretKey, true +} + +// GetAPIURL retrieve the api url from the config. +// It will check the following order: +// env, legacy env, active profile, default profile +// +// If the config is present in one of the above environment the +// value (which may be empty) is returned and the boolean is true. +// Otherwise the returned value will be empty and the boolean will +// be false. +func (c *configV2) GetAPIURL() (string, bool) { + envValue, _, envExist := getenv(scwAPIURLEnv) + activeProfile, _ := c.getActiveProfile() + + var apiURL string + switch { + case envExist: + apiURL = envValue + case activeProfile != "" && c.Profiles[activeProfile].APIURL != nil: + apiURL = *c.Profiles[activeProfile].APIURL + case c.APIURL != nil: + apiURL = *c.APIURL + default: + return "", false + } + + if apiURL == "" { + logger.Warningf("api URL is empty") + } + + return apiURL, true +} + +// GetInsecure retrieve the insecure flag from the config. +// It will check the following order: +// env, legacy env, active profile, default profile +// +// If the config is present in one of the above environment the +// value (which may be empty) is returned and the boolean is true. +// Otherwise the returned value will be empty and the boolean will +// be false. +func (c *configV2) GetInsecure() (bool, bool) { + envValue, envKey, envExist := getenv(scwInsecureEnv, cliTLSVerifyEnv) + activeProfile, _ := c.getActiveProfile() + + var insecure bool + var err error + switch { + case envExist: + insecure, err = strconv.ParseBool(envValue) + if err != nil { + logger.Warningf("env variable %s cannot be parsed: %s is invalid boolean ", envKey, envValue) + return false, false + } + + if envKey == cliTLSVerifyEnv { + insecure = !insecure // TLSVerify is the inverse of Insecure + } + case activeProfile != "" && c.Profiles[activeProfile].Insecure != nil: + insecure = *c.Profiles[activeProfile].Insecure + case c.Insecure != nil: + insecure = *c.Insecure + default: + return false, false + } + + return insecure, true +} + +// GetDefaultProjectID retrieve the default project ID +// from the config. Legacy configs used the name +// "organization ID" or "organization" for +// this field. It will check the following order: +// env, legacy env, active profile, default profile +// +// If the config is present in one of the above environment the +// value (which may be empty) is returned and the boolean is true. +// Otherwise the returned value will be empty and the boolean will +// be false. +func (c *configV2) GetDefaultProjectID() (string, bool) { + envValue, _, envExist := getenv(scwDefaultProjectIDEnv, cliOrganizationEnv, terraformOrganizationEnv) + activeProfile, _ := c.getActiveProfile() + + var defaultProj string + switch { + case envExist: + defaultProj = envValue + case activeProfile != "" && c.Profiles[activeProfile].DefaultProjectID != nil: + defaultProj = *c.Profiles[activeProfile].DefaultProjectID + case c.DefaultProjectID != nil: + defaultProj = *c.DefaultProjectID + default: + return "", false + } + + // todo: validate format + if defaultProj == "" { + logger.Warningf("default project ID is empty") + } + + return defaultProj, true +} + +// GetDefaultRegion retrieve the default region +// from the config. It will check the following order: +// env, legacy env, active profile, default profile +// +// If the config is present in one of the above environment the +// value (which may be empty) is returned and the boolean is true. +// Otherwise the returned value will be empty and the boolean will +// be false. +func (c *configV2) GetDefaultRegion() (utils.Region, bool) { + envValue, _, envExist := getenv(scwDefaultRegionEnv, cliRegionEnv, terraformRegionEnv) + activeProfile, _ := c.getActiveProfile() + + var defaultRegion string + switch { + case envExist: + defaultRegion = v1RegionToV2(envValue) + case activeProfile != "" && c.Profiles[activeProfile].DefaultRegion != nil: + defaultRegion = *c.Profiles[activeProfile].DefaultRegion + case c.DefaultRegion != nil: + defaultRegion = *c.DefaultRegion + default: + return "", false + } + + // todo: validate format + if defaultRegion == "" { + logger.Warningf("default region is empty") + } + + return utils.Region(defaultRegion), true +} + +// GetDefaultZone retrieve the default zone +// from the config. It will check the following order: +// env, legacy env, active profile, default profile +// +// If the config is present in one of the above environment the +// value (which may be empty) is returned and the boolean is true. +// Otherwise the returned value will be empty and the boolean will +// be false. +func (c *configV2) GetDefaultZone() (utils.Zone, bool) { + envValue, _, envExist := getenv(scwDefaultZoneEnv) + activeProfile, _ := c.getActiveProfile() + + var defaultZone string + switch { + case envExist: + defaultZone = envValue + case activeProfile != "" && c.Profiles[activeProfile].DefaultZone != nil: + defaultZone = *c.Profiles[activeProfile].DefaultZone + case c.DefaultZone != nil: + defaultZone = *c.DefaultZone + default: + return "", false + } + + // todo: validate format + if defaultZone == "" { + logger.Warningf("default zone is empty") + } + + return utils.Zone(defaultZone), true +} + +func getenv(upToDateKey string, deprecatedKeys ...string) (string, string, bool) { + value, exist := os.LookupEnv(upToDateKey) + if exist { + logger.Infof("reading value from %s", upToDateKey) + return value, upToDateKey, true + } + + for _, key := range deprecatedKeys { + value, exist := os.LookupEnv(key) + if exist { + logger.Infof("reading value from %s", key) + logger.Warningf("%s is deprecated, please use %s instead", key, upToDateKey) + return value, key, true + } + } + + return "", "", false +} + +const ( + v1RegionFrPar = "par1" + v1RegionNlAms = "ams1" +) + +// 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() *configV2 { + return &configV2{ + profile: profile{ + DefaultProjectID: &v1.Organization, + SecretKey: &v1.Token, + // ignore v1 version + }, + } +} + +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/src/cmd/linuxkit/vendor/github.com/scaleway/scaleway-sdk-go/scwconfig/load.go b/src/cmd/linuxkit/vendor/github.com/scaleway/scaleway-sdk-go/scwconfig/load.go new file mode 100644 index 000000000..4dc0cd09e --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/scaleway/scaleway-sdk-go/scwconfig/load.go @@ -0,0 +1,104 @@ +package scwconfig + +import ( + "fmt" + "io/ioutil" + "os" + + "github.com/scaleway/scaleway-sdk-go/logger" +) + +const ( + documentationLink = "https://github.com/scaleway/scaleway-sdk-go/blob/master/scwconfig/README.md" +) + +// LoadWithProfile call Load() and set withProfile with the profile name. +func LoadWithProfile(profileName string) (Config, error) { + config, err := Load() + if err != nil { + return nil, err + } + + v2Loaded := config.(*configV2) + v2Loaded.withProfile = profileName + return v2Loaded.catchInvalidProfile() +} + +// Load config in the following order: +// - config file from SCW_CONFIG_PATH (V2 or V1) +// - config file V2 +// - config file V1 +// When the latest is found it migrates the V1 config +// to a V2 config following the V2 config path. +func Load() (Config, error) { + // STEP 1: try to load config file from SCW_CONFIG_PATH + configPath := os.Getenv(scwConfigPathEnv) + if configPath != "" { + content, err := ioutil.ReadFile(configPath) + if err != nil { + return nil, fmt.Errorf("cannot read config file %s: %s", scwConfigPathEnv, err) + } + confV1, err := unmarshalConfV1(content) + if err == nil { + // do not migrate automatically when using SCW_CONFIG_PATH + logger.Warningf("loaded config V1 from %s: config V1 is deprecated, please switch your config file to the V2: %s", configPath, documentationLink) + return confV1.toV2().catchInvalidProfile() + } + confV2, err := unmarshalConfV2(content) + if err != nil { + return nil, fmt.Errorf("content of config file %s is invalid: %s", configPath, err) + } + + logger.Infof("successfully loaded config V2 from %s", configPath) + return confV2.catchInvalidProfile() + } + + // STEP 2: try to load config file V2 + v2Path, v2PathOk := GetConfigV2FilePath() + if v2PathOk && fileExist(v2Path) { + file, err := ioutil.ReadFile(v2Path) + if err != nil { + return nil, fmt.Errorf("cannot read config file: %s", err) + } + + confV2, err := unmarshalConfV2(file) + if err != nil { + return nil, fmt.Errorf("content of config file %s is invalid: %s", v2Path, err) + } + + logger.Infof("successfully loaded config V2 from %s", v2Path) + return confV2.catchInvalidProfile() + } + + // STEP 3: try to load config file V1 + logger.Debugf("no config V2 found, fall back to config V1") + v1Path, v1PathOk := GetConfigV1FilePath() + if !v1PathOk { + logger.Infof("config file not found: no home directory") + return (&configV2{}).catchInvalidProfile() + } + file, err := ioutil.ReadFile(v1Path) + if err != nil { + logger.Infof("cannot read config file: %s", err) + return (&configV2{}).catchInvalidProfile() // ignore if file doesn't exist + } + confV1, err := unmarshalConfV1(file) + if err != nil { + return nil, fmt.Errorf("content of config file %s is invalid json: %s", v1Path, err) + } + + // STEP 4: migrate V1 config to V2 config file + if v2PathOk { + err = migrateV1toV2(confV1, v2Path) + if err != nil { + return nil, err + } + } + + return confV1.toV2().catchInvalidProfile() +} + +func fileExist(name string) bool { + _, err := os.Stat(name) + return err == nil +} diff --git a/src/cmd/linuxkit/vendor/github.com/scaleway/scaleway-sdk-go/scwconfig/migrate.go b/src/cmd/linuxkit/vendor/github.com/scaleway/scaleway-sdk-go/scwconfig/migrate.go new file mode 100644 index 000000000..080f2741a --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/scaleway/scaleway-sdk-go/scwconfig/migrate.go @@ -0,0 +1,47 @@ +package scwconfig + +import ( + "io/ioutil" + "os" + "path/filepath" + + "github.com/scaleway/scaleway-sdk-go/logger" + "gopkg.in/yaml.v2" +) + +const ( + defaultConfigPermission = 0600 +) + +// migrateV1toV2 converts the V1 config to V2 config and save it in the target path +// use config.Save() when the method is public +func migrateV1toV2(configV1 *configV1, targetPath string) error { + // STEP 0: get absolute target path + + targetPath = filepath.Clean(targetPath) + + // STEP 1: create dir + err := os.MkdirAll(filepath.Dir(targetPath), 0700) + if err != nil { + logger.Debugf("mkdir did not work on %s: %s", filepath.Dir(targetPath), err) + return nil + } + + // STEP 2: marshal yaml config + newConfig := configV1.toV2() + file, err := yaml.Marshal(newConfig) + if err != nil { + return err + } + + // STEP 3: save config + err = ioutil.WriteFile(targetPath, file, defaultConfigPermission) + if err != nil { + logger.Debugf("cannot write file %s: %s", targetPath, err) + return nil + } + + // STEP 4: log success + logger.Infof("config successfully migrated to %s", targetPath) + return nil +} diff --git a/src/cmd/linuxkit/vendor/github.com/scaleway/scaleway-sdk-go/scwconfig/path.go b/src/cmd/linuxkit/vendor/github.com/scaleway/scaleway-sdk-go/scwconfig/path.go new file mode 100644 index 000000000..feeea454e --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/scaleway/scaleway-sdk-go/scwconfig/path.go @@ -0,0 +1,71 @@ +package scwconfig + +import ( + "errors" + "os" + "path/filepath" +) + +const ( + unixHomeDirEnv = "HOME" + windowsHomeDirEnv = "USERPROFILE" + xdgConfigDirEnv = "XDG_CONFIG_HOME" + + defaultConfigFileName = "config.yaml" +) + +var ( + // ErrNoHomeDir errors when no user directory is found + ErrNoHomeDir = errors.New("user home directory not found") +) + +func inConfigFile() string { + v2path, exist := GetConfigV2FilePath() + if exist { + return "in config file " + v2path + } + return "" +} + +// GetConfigV2FilePath returns the path to the Scaleway CLI config file +func GetConfigV2FilePath() (string, bool) { + configDir, err := GetScwConfigDir() + if err != nil { + return "", false + } + return filepath.Join(configDir, defaultConfigFileName), true +} + +// GetConfigV1FilePath returns the path to the Scaleway CLI config file +func GetConfigV1FilePath() (string, bool) { + path, err := GetHomeDir() + if err != nil { + return "", false + } + return 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 := GetHomeDir() + if err != nil { + return "", err + } + return filepath.Join(homeDir, ".config", "scw"), nil +} + +// GetHomeDir returns the path to your home directory +func GetHomeDir() (string, error) { + switch { + case os.Getenv(unixHomeDirEnv) != "": + return os.Getenv(unixHomeDirEnv), nil + case os.Getenv(windowsHomeDirEnv) != "": + return os.Getenv(windowsHomeDirEnv), nil + default: + return "", ErrNoHomeDir + } +} diff --git a/src/cmd/linuxkit/vendor/github.com/scaleway/scaleway-sdk-go/utils/convert.go b/src/cmd/linuxkit/vendor/github.com/scaleway/scaleway-sdk-go/utils/convert.go new file mode 100644 index 000000000..eae05af32 --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/scaleway/scaleway-sdk-go/utils/convert.go @@ -0,0 +1,158 @@ +package utils + +import "time" + +// String returns a pointer to the string value passed in. +func String(v string) *string { + return &v +} + +// StringSlice converts a slice of string values into a slice of +// string pointers +func StringSlice(src []string) []*string { + dst := make([]*string, len(src)) + for i := 0; i < len(src); i++ { + dst[i] = &(src[i]) + } + return dst +} + +// Strings returns a pointer to the []string value passed in. +func Strings(v []string) *[]string { + return &v +} + +// StringsSlice converts a slice of []string values into a slice of +// []string pointers +func StringsSlice(src [][]string) []*[]string { + dst := make([]*[]string, len(src)) + for i := 0; i < len(src); i++ { + dst[i] = &(src[i]) + } + return dst +} + +// Bytes returns a pointer to the []byte value passed in. +func Bytes(v []byte) *[]byte { + return &v +} + +// BytesSlice converts a slice of []byte values into a slice of +// []byte pointers +func BytesSlice(src [][]byte) []*[]byte { + dst := make([]*[]byte, len(src)) + for i := 0; i < len(src); i++ { + dst[i] = &(src[i]) + } + return dst +} + +// Bool returns a pointer to the bool value passed in. +func Bool(v bool) *bool { + return &v +} + +// BoolSlice converts a slice of bool values into a slice of +// bool pointers +func BoolSlice(src []bool) []*bool { + dst := make([]*bool, len(src)) + for i := 0; i < len(src); i++ { + dst[i] = &(src[i]) + } + return dst +} + +// Int32 returns a pointer to the int32 value passed in. +func Int32(v int32) *int32 { + return &v +} + +// Int32Slice converts a slice of int32 values into a slice of +// int32 pointers +func Int32Slice(src []int32) []*int32 { + dst := make([]*int32, len(src)) + for i := 0; i < len(src); i++ { + dst[i] = &(src[i]) + } + return dst +} + +// Int64 returns a pointer to the int64 value passed in. +func Int64(v int64) *int64 { + return &v +} + +// Int64Slice converts a slice of int64 values into a slice of +// int64 pointers +func Int64Slice(src []int64) []*int64 { + dst := make([]*int64, len(src)) + for i := 0; i < len(src); i++ { + dst[i] = &(src[i]) + } + return dst +} + +// Uint32 returns a pointer to the uint32 value passed in. +func Uint32(v uint32) *uint32 { + return &v +} + +// Uint32Slice converts a slice of uint32 values into a slice of +// uint32 pointers +func Uint32Slice(src []uint32) []*uint32 { + dst := make([]*uint32, len(src)) + for i := 0; i < len(src); i++ { + dst[i] = &(src[i]) + } + return dst +} + +// Uint64 returns a pointer to the uint64 value passed in. +func Uint64(v uint64) *uint64 { + return &v +} + +// Uint64Slice converts a slice of uint64 values into a slice of +// uint64 pointers +func Uint64Slice(src []uint64) []*uint64 { + dst := make([]*uint64, len(src)) + for i := 0; i < len(src); i++ { + dst[i] = &(src[i]) + } + return dst +} + +// Float32 returns a pointer to the float32 value passed in. +func Float32(v float32) *float32 { + return &v +} + +// Float32Slice converts a slice of float32 values into a slice of +// float32 pointers +func Float32Slice(src []float32) []*float32 { + dst := make([]*float32, len(src)) + for i := 0; i < len(src); i++ { + dst[i] = &(src[i]) + } + return dst +} + +// Float64 returns a pointer to the float64 value passed in. +func Float64(v float64) *float64 { + return &v +} + +// Float64Slice converts a slice of float64 values into a slice of +// float64 pointers +func Float64Slice(src []float64) []*float64 { + dst := make([]*float64, len(src)) + for i := 0; i < len(src); i++ { + dst[i] = &(src[i]) + } + return dst +} + +// Duration returns a pointer to the Duration value passed in. +func Duration(v time.Duration) *time.Duration { + return &v +} diff --git a/src/cmd/linuxkit/vendor/github.com/scaleway/scaleway-sdk-go/utils/file.go b/src/cmd/linuxkit/vendor/github.com/scaleway/scaleway-sdk-go/utils/file.go new file mode 100644 index 000000000..7d99c4069 --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/scaleway/scaleway-sdk-go/utils/file.go @@ -0,0 +1,15 @@ +package utils + +import "io" + +// 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"` +} diff --git a/src/cmd/linuxkit/vendor/github.com/scaleway/scaleway-sdk-go/utils/locality.go b/src/cmd/linuxkit/vendor/github.com/scaleway/scaleway-sdk-go/utils/locality.go new file mode 100644 index 000000000..80f27559f --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/scaleway/scaleway-sdk-go/utils/locality.go @@ -0,0 +1,155 @@ +package utils + +import ( + "encoding/json" + + "github.com/scaleway/scaleway-sdk-go/logger" +) + +// 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 +} + +// 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 parse a string value into a Zone object +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: + newZone := Zone(zone) + if !newZone.Exists() { + logger.Warningf("%s is an unknown zone", 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 parse a string value into a Zone object +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: + newRegion := Region(region) + if !newRegion.Exists() { + logger.Warningf("%s is an unknown region", 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 +} diff --git a/src/cmd/linuxkit/vendor/github.com/scaleway/scaleway-sdk-go/utils/money.go b/src/cmd/linuxkit/vendor/github.com/scaleway/scaleway-sdk-go/utils/money.go new file mode 100644 index 000000000..58f3e55cb --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/scaleway/scaleway-sdk-go/utils/money.go @@ -0,0 +1,33 @@ +package utils + +// 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,omitempty"` + + // 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,omitempty"` + + // 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,omitempty"` +} + +// NewMoneyFromFloat conerts a float with currency to a Money object. +func NewMoneyFromFloat(value float64, currency string) *Money { + return &Money{ + CurrencyCode: currency, + Units: int64(value), + Nanos: int32((value - float64(int64(value))) * 1000000000), + } +} + +// ToFloat converts a Money object to a float. +func (m *Money) ToFloat() float64 { + return float64(m.Units) + float64(m.Nanos)/1000000000 +} diff --git a/src/cmd/linuxkit/vendor/github.com/scaleway/scaleway-sdk-go/utils/service_info.go b/src/cmd/linuxkit/vendor/github.com/scaleway/scaleway-sdk-go/utils/service_info.go new file mode 100644 index 000000000..01a984be4 --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/scaleway/scaleway-sdk-go/utils/service_info.go @@ -0,0 +1,17 @@ +package utils + +// 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"` +} diff --git a/src/cmd/linuxkit/vendor/golang.org/x/crypto/blowfish/block.go b/src/cmd/linuxkit/vendor/golang.org/x/crypto/blowfish/block.go new file mode 100644 index 000000000..9d80f1952 --- /dev/null +++ b/src/cmd/linuxkit/vendor/golang.org/x/crypto/blowfish/block.go @@ -0,0 +1,159 @@ +// Copyright 2010 The Go 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 blowfish + +// getNextWord returns the next big-endian uint32 value from the byte slice +// at the given position in a circular manner, updating the position. +func getNextWord(b []byte, pos *int) uint32 { + var w uint32 + j := *pos + for i := 0; i < 4; i++ { + w = w<<8 | uint32(b[j]) + j++ + if j >= len(b) { + j = 0 + } + } + *pos = j + return w +} + +// ExpandKey performs a key expansion on the given *Cipher. Specifically, it +// performs the Blowfish algorithm's key schedule which sets up the *Cipher's +// pi and substitution tables for calls to Encrypt. This is used, primarily, +// by the bcrypt package to reuse the Blowfish key schedule during its +// set up. It's unlikely that you need to use this directly. +func ExpandKey(key []byte, c *Cipher) { + j := 0 + for i := 0; i < 18; i++ { + // Using inlined getNextWord for performance. + var d uint32 + for k := 0; k < 4; k++ { + d = d<<8 | uint32(key[j]) + j++ + if j >= len(key) { + j = 0 + } + } + c.p[i] ^= d + } + + var l, r uint32 + for i := 0; i < 18; i += 2 { + l, r = encryptBlock(l, r, c) + c.p[i], c.p[i+1] = l, r + } + + for i := 0; i < 256; i += 2 { + l, r = encryptBlock(l, r, c) + c.s0[i], c.s0[i+1] = l, r + } + for i := 0; i < 256; i += 2 { + l, r = encryptBlock(l, r, c) + c.s1[i], c.s1[i+1] = l, r + } + for i := 0; i < 256; i += 2 { + l, r = encryptBlock(l, r, c) + c.s2[i], c.s2[i+1] = l, r + } + for i := 0; i < 256; i += 2 { + l, r = encryptBlock(l, r, c) + c.s3[i], c.s3[i+1] = l, r + } +} + +// This is similar to ExpandKey, but folds the salt during the key +// schedule. While ExpandKey is essentially expandKeyWithSalt with an all-zero +// salt passed in, reusing ExpandKey turns out to be a place of inefficiency +// and specializing it here is useful. +func expandKeyWithSalt(key []byte, salt []byte, c *Cipher) { + j := 0 + for i := 0; i < 18; i++ { + c.p[i] ^= getNextWord(key, &j) + } + + j = 0 + var l, r uint32 + for i := 0; i < 18; i += 2 { + l ^= getNextWord(salt, &j) + r ^= getNextWord(salt, &j) + l, r = encryptBlock(l, r, c) + c.p[i], c.p[i+1] = l, r + } + + for i := 0; i < 256; i += 2 { + l ^= getNextWord(salt, &j) + r ^= getNextWord(salt, &j) + l, r = encryptBlock(l, r, c) + c.s0[i], c.s0[i+1] = l, r + } + + for i := 0; i < 256; i += 2 { + l ^= getNextWord(salt, &j) + r ^= getNextWord(salt, &j) + l, r = encryptBlock(l, r, c) + c.s1[i], c.s1[i+1] = l, r + } + + for i := 0; i < 256; i += 2 { + l ^= getNextWord(salt, &j) + r ^= getNextWord(salt, &j) + l, r = encryptBlock(l, r, c) + c.s2[i], c.s2[i+1] = l, r + } + + for i := 0; i < 256; i += 2 { + l ^= getNextWord(salt, &j) + r ^= getNextWord(salt, &j) + l, r = encryptBlock(l, r, c) + c.s3[i], c.s3[i+1] = l, r + } +} + +func encryptBlock(l, r uint32, c *Cipher) (uint32, uint32) { + xl, xr := l, r + xl ^= c.p[0] + xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[1] + xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[2] + xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[3] + xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[4] + xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[5] + xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[6] + xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[7] + xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[8] + xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[9] + xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[10] + xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[11] + xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[12] + xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[13] + xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[14] + xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[15] + xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[16] + xr ^= c.p[17] + return xr, xl +} + +func decryptBlock(l, r uint32, c *Cipher) (uint32, uint32) { + xl, xr := l, r + xl ^= c.p[17] + xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[16] + xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[15] + xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[14] + xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[13] + xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[12] + xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[11] + xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[10] + xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[9] + xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[8] + xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[7] + xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[6] + xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[5] + xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[4] + xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[3] + xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[2] + xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[1] + xr ^= c.p[0] + return xr, xl +} diff --git a/src/cmd/linuxkit/vendor/golang.org/x/crypto/blowfish/cipher.go b/src/cmd/linuxkit/vendor/golang.org/x/crypto/blowfish/cipher.go new file mode 100644 index 000000000..2641dadd6 --- /dev/null +++ b/src/cmd/linuxkit/vendor/golang.org/x/crypto/blowfish/cipher.go @@ -0,0 +1,91 @@ +// Copyright 2010 The Go 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 blowfish implements Bruce Schneier's Blowfish encryption algorithm. +package blowfish // import "golang.org/x/crypto/blowfish" + +// The code is a port of Bruce Schneier's C implementation. +// See https://www.schneier.com/blowfish.html. + +import "strconv" + +// The Blowfish block size in bytes. +const BlockSize = 8 + +// A Cipher is an instance of Blowfish encryption using a particular key. +type Cipher struct { + p [18]uint32 + s0, s1, s2, s3 [256]uint32 +} + +type KeySizeError int + +func (k KeySizeError) Error() string { + return "crypto/blowfish: invalid key size " + strconv.Itoa(int(k)) +} + +// NewCipher creates and returns a Cipher. +// The key argument should be the Blowfish key, from 1 to 56 bytes. +func NewCipher(key []byte) (*Cipher, error) { + var result Cipher + if k := len(key); k < 1 || k > 56 { + return nil, KeySizeError(k) + } + initCipher(&result) + ExpandKey(key, &result) + return &result, nil +} + +// NewSaltedCipher creates a returns a Cipher that folds a salt into its key +// schedule. For most purposes, NewCipher, instead of NewSaltedCipher, is +// sufficient and desirable. For bcrypt compatibility, the key can be over 56 +// bytes. +func NewSaltedCipher(key, salt []byte) (*Cipher, error) { + if len(salt) == 0 { + return NewCipher(key) + } + var result Cipher + if k := len(key); k < 1 { + return nil, KeySizeError(k) + } + initCipher(&result) + expandKeyWithSalt(key, salt, &result) + return &result, nil +} + +// BlockSize returns the Blowfish block size, 8 bytes. +// It is necessary to satisfy the Block interface in the +// package "crypto/cipher". +func (c *Cipher) BlockSize() int { return BlockSize } + +// Encrypt encrypts the 8-byte buffer src using the key k +// and stores the result in dst. +// Note that for amounts of data larger than a block, +// it is not safe to just call Encrypt on successive blocks; +// instead, use an encryption mode like CBC (see crypto/cipher/cbc.go). +func (c *Cipher) Encrypt(dst, src []byte) { + l := uint32(src[0])<<24 | uint32(src[1])<<16 | uint32(src[2])<<8 | uint32(src[3]) + r := uint32(src[4])<<24 | uint32(src[5])<<16 | uint32(src[6])<<8 | uint32(src[7]) + l, r = encryptBlock(l, r, c) + dst[0], dst[1], dst[2], dst[3] = byte(l>>24), byte(l>>16), byte(l>>8), byte(l) + dst[4], dst[5], dst[6], dst[7] = byte(r>>24), byte(r>>16), byte(r>>8), byte(r) +} + +// Decrypt decrypts the 8-byte buffer src using the key k +// and stores the result in dst. +func (c *Cipher) Decrypt(dst, src []byte) { + l := uint32(src[0])<<24 | uint32(src[1])<<16 | uint32(src[2])<<8 | uint32(src[3]) + r := uint32(src[4])<<24 | uint32(src[5])<<16 | uint32(src[6])<<8 | uint32(src[7]) + l, r = decryptBlock(l, r, c) + dst[0], dst[1], dst[2], dst[3] = byte(l>>24), byte(l>>16), byte(l>>8), byte(l) + dst[4], dst[5], dst[6], dst[7] = byte(r>>24), byte(r>>16), byte(r>>8), byte(r) +} + +func initCipher(c *Cipher) { + copy(c.p[0:], p[0:]) + copy(c.s0[0:], s0[0:]) + copy(c.s1[0:], s1[0:]) + copy(c.s2[0:], s2[0:]) + copy(c.s3[0:], s3[0:]) +} diff --git a/src/cmd/linuxkit/vendor/golang.org/x/crypto/blowfish/const.go b/src/cmd/linuxkit/vendor/golang.org/x/crypto/blowfish/const.go new file mode 100644 index 000000000..d04077595 --- /dev/null +++ b/src/cmd/linuxkit/vendor/golang.org/x/crypto/blowfish/const.go @@ -0,0 +1,199 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// The startup permutation array and substitution boxes. +// They are the hexadecimal digits of PI; see: +// https://www.schneier.com/code/constants.txt. + +package blowfish + +var s0 = [256]uint32{ + 0xd1310ba6, 0x98dfb5ac, 0x2ffd72db, 0xd01adfb7, 0xb8e1afed, 0x6a267e96, + 0xba7c9045, 0xf12c7f99, 0x24a19947, 0xb3916cf7, 0x0801f2e2, 0x858efc16, + 0x636920d8, 0x71574e69, 0xa458fea3, 0xf4933d7e, 0x0d95748f, 0x728eb658, + 0x718bcd58, 0x82154aee, 0x7b54a41d, 0xc25a59b5, 0x9c30d539, 0x2af26013, + 0xc5d1b023, 0x286085f0, 0xca417918, 0xb8db38ef, 0x8e79dcb0, 0x603a180e, + 0x6c9e0e8b, 0xb01e8a3e, 0xd71577c1, 0xbd314b27, 0x78af2fda, 0x55605c60, + 0xe65525f3, 0xaa55ab94, 0x57489862, 0x63e81440, 0x55ca396a, 0x2aab10b6, + 0xb4cc5c34, 0x1141e8ce, 0xa15486af, 0x7c72e993, 0xb3ee1411, 0x636fbc2a, + 0x2ba9c55d, 0x741831f6, 0xce5c3e16, 0x9b87931e, 0xafd6ba33, 0x6c24cf5c, + 0x7a325381, 0x28958677, 0x3b8f4898, 0x6b4bb9af, 0xc4bfe81b, 0x66282193, + 0x61d809cc, 0xfb21a991, 0x487cac60, 0x5dec8032, 0xef845d5d, 0xe98575b1, + 0xdc262302, 0xeb651b88, 0x23893e81, 0xd396acc5, 0x0f6d6ff3, 0x83f44239, + 0x2e0b4482, 0xa4842004, 0x69c8f04a, 0x9e1f9b5e, 0x21c66842, 0xf6e96c9a, + 0x670c9c61, 0xabd388f0, 0x6a51a0d2, 0xd8542f68, 0x960fa728, 0xab5133a3, + 0x6eef0b6c, 0x137a3be4, 0xba3bf050, 0x7efb2a98, 0xa1f1651d, 0x39af0176, + 0x66ca593e, 0x82430e88, 0x8cee8619, 0x456f9fb4, 0x7d84a5c3, 0x3b8b5ebe, + 0xe06f75d8, 0x85c12073, 0x401a449f, 0x56c16aa6, 0x4ed3aa62, 0x363f7706, + 0x1bfedf72, 0x429b023d, 0x37d0d724, 0xd00a1248, 0xdb0fead3, 0x49f1c09b, + 0x075372c9, 0x80991b7b, 0x25d479d8, 0xf6e8def7, 0xe3fe501a, 0xb6794c3b, + 0x976ce0bd, 0x04c006ba, 0xc1a94fb6, 0x409f60c4, 0x5e5c9ec2, 0x196a2463, + 0x68fb6faf, 0x3e6c53b5, 0x1339b2eb, 0x3b52ec6f, 0x6dfc511f, 0x9b30952c, + 0xcc814544, 0xaf5ebd09, 0xbee3d004, 0xde334afd, 0x660f2807, 0x192e4bb3, + 0xc0cba857, 0x45c8740f, 0xd20b5f39, 0xb9d3fbdb, 0x5579c0bd, 0x1a60320a, + 0xd6a100c6, 0x402c7279, 0x679f25fe, 0xfb1fa3cc, 0x8ea5e9f8, 0xdb3222f8, + 0x3c7516df, 0xfd616b15, 0x2f501ec8, 0xad0552ab, 0x323db5fa, 0xfd238760, + 0x53317b48, 0x3e00df82, 0x9e5c57bb, 0xca6f8ca0, 0x1a87562e, 0xdf1769db, + 0xd542a8f6, 0x287effc3, 0xac6732c6, 0x8c4f5573, 0x695b27b0, 0xbbca58c8, + 0xe1ffa35d, 0xb8f011a0, 0x10fa3d98, 0xfd2183b8, 0x4afcb56c, 0x2dd1d35b, + 0x9a53e479, 0xb6f84565, 0xd28e49bc, 0x4bfb9790, 0xe1ddf2da, 0xa4cb7e33, + 0x62fb1341, 0xcee4c6e8, 0xef20cada, 0x36774c01, 0xd07e9efe, 0x2bf11fb4, + 0x95dbda4d, 0xae909198, 0xeaad8e71, 0x6b93d5a0, 0xd08ed1d0, 0xafc725e0, + 0x8e3c5b2f, 0x8e7594b7, 0x8ff6e2fb, 0xf2122b64, 0x8888b812, 0x900df01c, + 0x4fad5ea0, 0x688fc31c, 0xd1cff191, 0xb3a8c1ad, 0x2f2f2218, 0xbe0e1777, + 0xea752dfe, 0x8b021fa1, 0xe5a0cc0f, 0xb56f74e8, 0x18acf3d6, 0xce89e299, + 0xb4a84fe0, 0xfd13e0b7, 0x7cc43b81, 0xd2ada8d9, 0x165fa266, 0x80957705, + 0x93cc7314, 0x211a1477, 0xe6ad2065, 0x77b5fa86, 0xc75442f5, 0xfb9d35cf, + 0xebcdaf0c, 0x7b3e89a0, 0xd6411bd3, 0xae1e7e49, 0x00250e2d, 0x2071b35e, + 0x226800bb, 0x57b8e0af, 0x2464369b, 0xf009b91e, 0x5563911d, 0x59dfa6aa, + 0x78c14389, 0xd95a537f, 0x207d5ba2, 0x02e5b9c5, 0x83260376, 0x6295cfa9, + 0x11c81968, 0x4e734a41, 0xb3472dca, 0x7b14a94a, 0x1b510052, 0x9a532915, + 0xd60f573f, 0xbc9bc6e4, 0x2b60a476, 0x81e67400, 0x08ba6fb5, 0x571be91f, + 0xf296ec6b, 0x2a0dd915, 0xb6636521, 0xe7b9f9b6, 0xff34052e, 0xc5855664, + 0x53b02d5d, 0xa99f8fa1, 0x08ba4799, 0x6e85076a, +} + +var s1 = [256]uint32{ + 0x4b7a70e9, 0xb5b32944, 0xdb75092e, 0xc4192623, 0xad6ea6b0, 0x49a7df7d, + 0x9cee60b8, 0x8fedb266, 0xecaa8c71, 0x699a17ff, 0x5664526c, 0xc2b19ee1, + 0x193602a5, 0x75094c29, 0xa0591340, 0xe4183a3e, 0x3f54989a, 0x5b429d65, + 0x6b8fe4d6, 0x99f73fd6, 0xa1d29c07, 0xefe830f5, 0x4d2d38e6, 0xf0255dc1, + 0x4cdd2086, 0x8470eb26, 0x6382e9c6, 0x021ecc5e, 0x09686b3f, 0x3ebaefc9, + 0x3c971814, 0x6b6a70a1, 0x687f3584, 0x52a0e286, 0xb79c5305, 0xaa500737, + 0x3e07841c, 0x7fdeae5c, 0x8e7d44ec, 0x5716f2b8, 0xb03ada37, 0xf0500c0d, + 0xf01c1f04, 0x0200b3ff, 0xae0cf51a, 0x3cb574b2, 0x25837a58, 0xdc0921bd, + 0xd19113f9, 0x7ca92ff6, 0x94324773, 0x22f54701, 0x3ae5e581, 0x37c2dadc, + 0xc8b57634, 0x9af3dda7, 0xa9446146, 0x0fd0030e, 0xecc8c73e, 0xa4751e41, + 0xe238cd99, 0x3bea0e2f, 0x3280bba1, 0x183eb331, 0x4e548b38, 0x4f6db908, + 0x6f420d03, 0xf60a04bf, 0x2cb81290, 0x24977c79, 0x5679b072, 0xbcaf89af, + 0xde9a771f, 0xd9930810, 0xb38bae12, 0xdccf3f2e, 0x5512721f, 0x2e6b7124, + 0x501adde6, 0x9f84cd87, 0x7a584718, 0x7408da17, 0xbc9f9abc, 0xe94b7d8c, + 0xec7aec3a, 0xdb851dfa, 0x63094366, 0xc464c3d2, 0xef1c1847, 0x3215d908, + 0xdd433b37, 0x24c2ba16, 0x12a14d43, 0x2a65c451, 0x50940002, 0x133ae4dd, + 0x71dff89e, 0x10314e55, 0x81ac77d6, 0x5f11199b, 0x043556f1, 0xd7a3c76b, + 0x3c11183b, 0x5924a509, 0xf28fe6ed, 0x97f1fbfa, 0x9ebabf2c, 0x1e153c6e, + 0x86e34570, 0xeae96fb1, 0x860e5e0a, 0x5a3e2ab3, 0x771fe71c, 0x4e3d06fa, + 0x2965dcb9, 0x99e71d0f, 0x803e89d6, 0x5266c825, 0x2e4cc978, 0x9c10b36a, + 0xc6150eba, 0x94e2ea78, 0xa5fc3c53, 0x1e0a2df4, 0xf2f74ea7, 0x361d2b3d, + 0x1939260f, 0x19c27960, 0x5223a708, 0xf71312b6, 0xebadfe6e, 0xeac31f66, + 0xe3bc4595, 0xa67bc883, 0xb17f37d1, 0x018cff28, 0xc332ddef, 0xbe6c5aa5, + 0x65582185, 0x68ab9802, 0xeecea50f, 0xdb2f953b, 0x2aef7dad, 0x5b6e2f84, + 0x1521b628, 0x29076170, 0xecdd4775, 0x619f1510, 0x13cca830, 0xeb61bd96, + 0x0334fe1e, 0xaa0363cf, 0xb5735c90, 0x4c70a239, 0xd59e9e0b, 0xcbaade14, + 0xeecc86bc, 0x60622ca7, 0x9cab5cab, 0xb2f3846e, 0x648b1eaf, 0x19bdf0ca, + 0xa02369b9, 0x655abb50, 0x40685a32, 0x3c2ab4b3, 0x319ee9d5, 0xc021b8f7, + 0x9b540b19, 0x875fa099, 0x95f7997e, 0x623d7da8, 0xf837889a, 0x97e32d77, + 0x11ed935f, 0x16681281, 0x0e358829, 0xc7e61fd6, 0x96dedfa1, 0x7858ba99, + 0x57f584a5, 0x1b227263, 0x9b83c3ff, 0x1ac24696, 0xcdb30aeb, 0x532e3054, + 0x8fd948e4, 0x6dbc3128, 0x58ebf2ef, 0x34c6ffea, 0xfe28ed61, 0xee7c3c73, + 0x5d4a14d9, 0xe864b7e3, 0x42105d14, 0x203e13e0, 0x45eee2b6, 0xa3aaabea, + 0xdb6c4f15, 0xfacb4fd0, 0xc742f442, 0xef6abbb5, 0x654f3b1d, 0x41cd2105, + 0xd81e799e, 0x86854dc7, 0xe44b476a, 0x3d816250, 0xcf62a1f2, 0x5b8d2646, + 0xfc8883a0, 0xc1c7b6a3, 0x7f1524c3, 0x69cb7492, 0x47848a0b, 0x5692b285, + 0x095bbf00, 0xad19489d, 0x1462b174, 0x23820e00, 0x58428d2a, 0x0c55f5ea, + 0x1dadf43e, 0x233f7061, 0x3372f092, 0x8d937e41, 0xd65fecf1, 0x6c223bdb, + 0x7cde3759, 0xcbee7460, 0x4085f2a7, 0xce77326e, 0xa6078084, 0x19f8509e, + 0xe8efd855, 0x61d99735, 0xa969a7aa, 0xc50c06c2, 0x5a04abfc, 0x800bcadc, + 0x9e447a2e, 0xc3453484, 0xfdd56705, 0x0e1e9ec9, 0xdb73dbd3, 0x105588cd, + 0x675fda79, 0xe3674340, 0xc5c43465, 0x713e38d8, 0x3d28f89e, 0xf16dff20, + 0x153e21e7, 0x8fb03d4a, 0xe6e39f2b, 0xdb83adf7, +} + +var s2 = [256]uint32{ + 0xe93d5a68, 0x948140f7, 0xf64c261c, 0x94692934, 0x411520f7, 0x7602d4f7, + 0xbcf46b2e, 0xd4a20068, 0xd4082471, 0x3320f46a, 0x43b7d4b7, 0x500061af, + 0x1e39f62e, 0x97244546, 0x14214f74, 0xbf8b8840, 0x4d95fc1d, 0x96b591af, + 0x70f4ddd3, 0x66a02f45, 0xbfbc09ec, 0x03bd9785, 0x7fac6dd0, 0x31cb8504, + 0x96eb27b3, 0x55fd3941, 0xda2547e6, 0xabca0a9a, 0x28507825, 0x530429f4, + 0x0a2c86da, 0xe9b66dfb, 0x68dc1462, 0xd7486900, 0x680ec0a4, 0x27a18dee, + 0x4f3ffea2, 0xe887ad8c, 0xb58ce006, 0x7af4d6b6, 0xaace1e7c, 0xd3375fec, + 0xce78a399, 0x406b2a42, 0x20fe9e35, 0xd9f385b9, 0xee39d7ab, 0x3b124e8b, + 0x1dc9faf7, 0x4b6d1856, 0x26a36631, 0xeae397b2, 0x3a6efa74, 0xdd5b4332, + 0x6841e7f7, 0xca7820fb, 0xfb0af54e, 0xd8feb397, 0x454056ac, 0xba489527, + 0x55533a3a, 0x20838d87, 0xfe6ba9b7, 0xd096954b, 0x55a867bc, 0xa1159a58, + 0xcca92963, 0x99e1db33, 0xa62a4a56, 0x3f3125f9, 0x5ef47e1c, 0x9029317c, + 0xfdf8e802, 0x04272f70, 0x80bb155c, 0x05282ce3, 0x95c11548, 0xe4c66d22, + 0x48c1133f, 0xc70f86dc, 0x07f9c9ee, 0x41041f0f, 0x404779a4, 0x5d886e17, + 0x325f51eb, 0xd59bc0d1, 0xf2bcc18f, 0x41113564, 0x257b7834, 0x602a9c60, + 0xdff8e8a3, 0x1f636c1b, 0x0e12b4c2, 0x02e1329e, 0xaf664fd1, 0xcad18115, + 0x6b2395e0, 0x333e92e1, 0x3b240b62, 0xeebeb922, 0x85b2a20e, 0xe6ba0d99, + 0xde720c8c, 0x2da2f728, 0xd0127845, 0x95b794fd, 0x647d0862, 0xe7ccf5f0, + 0x5449a36f, 0x877d48fa, 0xc39dfd27, 0xf33e8d1e, 0x0a476341, 0x992eff74, + 0x3a6f6eab, 0xf4f8fd37, 0xa812dc60, 0xa1ebddf8, 0x991be14c, 0xdb6e6b0d, + 0xc67b5510, 0x6d672c37, 0x2765d43b, 0xdcd0e804, 0xf1290dc7, 0xcc00ffa3, + 0xb5390f92, 0x690fed0b, 0x667b9ffb, 0xcedb7d9c, 0xa091cf0b, 0xd9155ea3, + 0xbb132f88, 0x515bad24, 0x7b9479bf, 0x763bd6eb, 0x37392eb3, 0xcc115979, + 0x8026e297, 0xf42e312d, 0x6842ada7, 0xc66a2b3b, 0x12754ccc, 0x782ef11c, + 0x6a124237, 0xb79251e7, 0x06a1bbe6, 0x4bfb6350, 0x1a6b1018, 0x11caedfa, + 0x3d25bdd8, 0xe2e1c3c9, 0x44421659, 0x0a121386, 0xd90cec6e, 0xd5abea2a, + 0x64af674e, 0xda86a85f, 0xbebfe988, 0x64e4c3fe, 0x9dbc8057, 0xf0f7c086, + 0x60787bf8, 0x6003604d, 0xd1fd8346, 0xf6381fb0, 0x7745ae04, 0xd736fccc, + 0x83426b33, 0xf01eab71, 0xb0804187, 0x3c005e5f, 0x77a057be, 0xbde8ae24, + 0x55464299, 0xbf582e61, 0x4e58f48f, 0xf2ddfda2, 0xf474ef38, 0x8789bdc2, + 0x5366f9c3, 0xc8b38e74, 0xb475f255, 0x46fcd9b9, 0x7aeb2661, 0x8b1ddf84, + 0x846a0e79, 0x915f95e2, 0x466e598e, 0x20b45770, 0x8cd55591, 0xc902de4c, + 0xb90bace1, 0xbb8205d0, 0x11a86248, 0x7574a99e, 0xb77f19b6, 0xe0a9dc09, + 0x662d09a1, 0xc4324633, 0xe85a1f02, 0x09f0be8c, 0x4a99a025, 0x1d6efe10, + 0x1ab93d1d, 0x0ba5a4df, 0xa186f20f, 0x2868f169, 0xdcb7da83, 0x573906fe, + 0xa1e2ce9b, 0x4fcd7f52, 0x50115e01, 0xa70683fa, 0xa002b5c4, 0x0de6d027, + 0x9af88c27, 0x773f8641, 0xc3604c06, 0x61a806b5, 0xf0177a28, 0xc0f586e0, + 0x006058aa, 0x30dc7d62, 0x11e69ed7, 0x2338ea63, 0x53c2dd94, 0xc2c21634, + 0xbbcbee56, 0x90bcb6de, 0xebfc7da1, 0xce591d76, 0x6f05e409, 0x4b7c0188, + 0x39720a3d, 0x7c927c24, 0x86e3725f, 0x724d9db9, 0x1ac15bb4, 0xd39eb8fc, + 0xed545578, 0x08fca5b5, 0xd83d7cd3, 0x4dad0fc4, 0x1e50ef5e, 0xb161e6f8, + 0xa28514d9, 0x6c51133c, 0x6fd5c7e7, 0x56e14ec4, 0x362abfce, 0xddc6c837, + 0xd79a3234, 0x92638212, 0x670efa8e, 0x406000e0, +} + +var s3 = [256]uint32{ + 0x3a39ce37, 0xd3faf5cf, 0xabc27737, 0x5ac52d1b, 0x5cb0679e, 0x4fa33742, + 0xd3822740, 0x99bc9bbe, 0xd5118e9d, 0xbf0f7315, 0xd62d1c7e, 0xc700c47b, + 0xb78c1b6b, 0x21a19045, 0xb26eb1be, 0x6a366eb4, 0x5748ab2f, 0xbc946e79, + 0xc6a376d2, 0x6549c2c8, 0x530ff8ee, 0x468dde7d, 0xd5730a1d, 0x4cd04dc6, + 0x2939bbdb, 0xa9ba4650, 0xac9526e8, 0xbe5ee304, 0xa1fad5f0, 0x6a2d519a, + 0x63ef8ce2, 0x9a86ee22, 0xc089c2b8, 0x43242ef6, 0xa51e03aa, 0x9cf2d0a4, + 0x83c061ba, 0x9be96a4d, 0x8fe51550, 0xba645bd6, 0x2826a2f9, 0xa73a3ae1, + 0x4ba99586, 0xef5562e9, 0xc72fefd3, 0xf752f7da, 0x3f046f69, 0x77fa0a59, + 0x80e4a915, 0x87b08601, 0x9b09e6ad, 0x3b3ee593, 0xe990fd5a, 0x9e34d797, + 0x2cf0b7d9, 0x022b8b51, 0x96d5ac3a, 0x017da67d, 0xd1cf3ed6, 0x7c7d2d28, + 0x1f9f25cf, 0xadf2b89b, 0x5ad6b472, 0x5a88f54c, 0xe029ac71, 0xe019a5e6, + 0x47b0acfd, 0xed93fa9b, 0xe8d3c48d, 0x283b57cc, 0xf8d56629, 0x79132e28, + 0x785f0191, 0xed756055, 0xf7960e44, 0xe3d35e8c, 0x15056dd4, 0x88f46dba, + 0x03a16125, 0x0564f0bd, 0xc3eb9e15, 0x3c9057a2, 0x97271aec, 0xa93a072a, + 0x1b3f6d9b, 0x1e6321f5, 0xf59c66fb, 0x26dcf319, 0x7533d928, 0xb155fdf5, + 0x03563482, 0x8aba3cbb, 0x28517711, 0xc20ad9f8, 0xabcc5167, 0xccad925f, + 0x4de81751, 0x3830dc8e, 0x379d5862, 0x9320f991, 0xea7a90c2, 0xfb3e7bce, + 0x5121ce64, 0x774fbe32, 0xa8b6e37e, 0xc3293d46, 0x48de5369, 0x6413e680, + 0xa2ae0810, 0xdd6db224, 0x69852dfd, 0x09072166, 0xb39a460a, 0x6445c0dd, + 0x586cdecf, 0x1c20c8ae, 0x5bbef7dd, 0x1b588d40, 0xccd2017f, 0x6bb4e3bb, + 0xdda26a7e, 0x3a59ff45, 0x3e350a44, 0xbcb4cdd5, 0x72eacea8, 0xfa6484bb, + 0x8d6612ae, 0xbf3c6f47, 0xd29be463, 0x542f5d9e, 0xaec2771b, 0xf64e6370, + 0x740e0d8d, 0xe75b1357, 0xf8721671, 0xaf537d5d, 0x4040cb08, 0x4eb4e2cc, + 0x34d2466a, 0x0115af84, 0xe1b00428, 0x95983a1d, 0x06b89fb4, 0xce6ea048, + 0x6f3f3b82, 0x3520ab82, 0x011a1d4b, 0x277227f8, 0x611560b1, 0xe7933fdc, + 0xbb3a792b, 0x344525bd, 0xa08839e1, 0x51ce794b, 0x2f32c9b7, 0xa01fbac9, + 0xe01cc87e, 0xbcc7d1f6, 0xcf0111c3, 0xa1e8aac7, 0x1a908749, 0xd44fbd9a, + 0xd0dadecb, 0xd50ada38, 0x0339c32a, 0xc6913667, 0x8df9317c, 0xe0b12b4f, + 0xf79e59b7, 0x43f5bb3a, 0xf2d519ff, 0x27d9459c, 0xbf97222c, 0x15e6fc2a, + 0x0f91fc71, 0x9b941525, 0xfae59361, 0xceb69ceb, 0xc2a86459, 0x12baa8d1, + 0xb6c1075e, 0xe3056a0c, 0x10d25065, 0xcb03a442, 0xe0ec6e0e, 0x1698db3b, + 0x4c98a0be, 0x3278e964, 0x9f1f9532, 0xe0d392df, 0xd3a0342b, 0x8971f21e, + 0x1b0a7441, 0x4ba3348c, 0xc5be7120, 0xc37632d8, 0xdf359f8d, 0x9b992f2e, + 0xe60b6f47, 0x0fe3f11d, 0xe54cda54, 0x1edad891, 0xce6279cf, 0xcd3e7e6f, + 0x1618b166, 0xfd2c1d05, 0x848fd2c5, 0xf6fb2299, 0xf523f357, 0xa6327623, + 0x93a83531, 0x56cccd02, 0xacf08162, 0x5a75ebb5, 0x6e163697, 0x88d273cc, + 0xde966292, 0x81b949d0, 0x4c50901b, 0x71c65614, 0xe6c6c7bd, 0x327a140a, + 0x45e1d006, 0xc3f27b9a, 0xc9aa53fd, 0x62a80f00, 0xbb25bfe2, 0x35bdd2f6, + 0x71126905, 0xb2040222, 0xb6cbcf7c, 0xcd769c2b, 0x53113ec0, 0x1640e3d3, + 0x38abbd60, 0x2547adf0, 0xba38209c, 0xf746ce76, 0x77afa1c5, 0x20756060, + 0x85cbfe4e, 0x8ae88dd8, 0x7aaaf9b0, 0x4cf9aa7e, 0x1948c25c, 0x02fb8a8c, + 0x01c36ae4, 0xd6ebe1f9, 0x90d4f869, 0xa65cdea0, 0x3f09252d, 0xc208e69f, + 0xb74e6132, 0xce77e25b, 0x578fdfe3, 0x3ac372e6, +} + +var p = [18]uint32{ + 0x243f6a88, 0x85a308d3, 0x13198a2e, 0x03707344, 0xa4093822, 0x299f31d0, + 0x082efa98, 0xec4e6c89, 0x452821e6, 0x38d01377, 0xbe5466cf, 0x34e90c6c, + 0xc0ac29b7, 0xc97c50dd, 0x3f84d5b5, 0xb5470917, 0x9216d5d9, 0x8979fb1b, +}