github.com/scaleway/scaleway-cli@v1.11.1/pkg/api/api.go (about)

     1  // Copyright (C) 2015 Scaleway. All rights reserved.
     2  // Use of this source code is governed by a MIT-style
     3  // license that can be found in the LICENSE.md file.
     4  
     5  // Interact with Scaleway API
     6  
     7  // Package api contains client and functions to interact with Scaleway API
     8  package api
     9  
    10  import (
    11  	"bytes"
    12  	"crypto/tls"
    13  	"encoding/json"
    14  	"errors"
    15  	"fmt"
    16  	"io"
    17  	"io/ioutil"
    18  	"net"
    19  	"net/http"
    20  	"net/http/httputil"
    21  	"net/url"
    22  	"os"
    23  	"sort"
    24  	"strconv"
    25  	"strings"
    26  	"text/tabwriter"
    27  	"text/template"
    28  	"time"
    29  
    30  	"golang.org/x/sync/errgroup"
    31  )
    32  
    33  // Default values
    34  var (
    35  	AccountAPI     = "https://account.scaleway.com/"
    36  	MetadataAPI    = "http://169.254.42.42/"
    37  	MarketplaceAPI = "https://api-marketplace.scaleway.com"
    38  	ComputeAPIPar1 = "https://cp-par1.scaleway.com/"
    39  	ComputeAPIAms1 = "https://cp-ams1.scaleway.com"
    40  
    41  	URLPublicDNS  = ".pub.cloud.scaleway.com"
    42  	URLPrivateDNS = ".priv.cloud.scaleway.com"
    43  )
    44  
    45  func init() {
    46  	if url := os.Getenv("SCW_ACCOUNT_API"); url != "" {
    47  		AccountAPI = url
    48  	}
    49  	if url := os.Getenv("SCW_METADATA_API"); url != "" {
    50  		MetadataAPI = url
    51  	}
    52  	if url := os.Getenv("SCW_MARKETPLACE_API"); url != "" {
    53  		MarketplaceAPI = url
    54  	}
    55  }
    56  
    57  const (
    58  	perPage = 50
    59  )
    60  
    61  // ScalewayAPI is the interface used to communicate with the Scaleway API
    62  type ScalewayAPI struct {
    63  	// Organization is the identifier of the Scaleway organization
    64  	Organization string
    65  
    66  	// Token is the authentication token for the Scaleway organization
    67  	Token string
    68  
    69  	// Password is the authentication password
    70  	password string
    71  
    72  	userAgent string
    73  
    74  	// Cache is used to quickly resolve identifiers from names
    75  	Cache *ScalewayCache
    76  
    77  	client     *http.Client
    78  	verbose    bool
    79  	computeAPI string
    80  
    81  	Region string
    82  	//
    83  	Logger
    84  }
    85  
    86  // ScalewayAPIError represents a Scaleway API Error
    87  type ScalewayAPIError struct {
    88  	// Message is a human-friendly error message
    89  	APIMessage string `json:"message,omitempty"`
    90  
    91  	// Type is a string code that defines the kind of error
    92  	Type string `json:"type,omitempty"`
    93  
    94  	// Fields contains detail about validation error
    95  	Fields map[string][]string `json:"fields,omitempty"`
    96  
    97  	// StatusCode is the HTTP status code received
    98  	StatusCode int `json:"-"`
    99  
   100  	// Message
   101  	Message string `json:"-"`
   102  }
   103  
   104  // Error returns a string representing the error
   105  func (e ScalewayAPIError) Error() string {
   106  	var b bytes.Buffer
   107  
   108  	fmt.Fprintf(&b, "StatusCode: %v, ", e.StatusCode)
   109  	fmt.Fprintf(&b, "Type: %v, ", e.Type)
   110  	fmt.Fprintf(&b, "APIMessage: \x1b[31m%v\x1b[0m", e.APIMessage)
   111  	if len(e.Fields) > 0 {
   112  		fmt.Fprintf(&b, ", Details: %v", e.Fields)
   113  	}
   114  	return b.String()
   115  }
   116  
   117  // HideAPICredentials removes API credentials from a string
   118  func (s *ScalewayAPI) HideAPICredentials(input string) string {
   119  	output := input
   120  	if s.Token != "" {
   121  		output = strings.Replace(output, s.Token, "00000000-0000-4000-8000-000000000000", -1)
   122  	}
   123  	if s.Organization != "" {
   124  		output = strings.Replace(output, s.Organization, "00000000-0000-5000-9000-000000000000", -1)
   125  	}
   126  	if s.password != "" {
   127  		output = strings.Replace(output, s.password, "XX-XX-XX-XX", -1)
   128  	}
   129  	return output
   130  }
   131  
   132  // ScalewayIPAddress represents a Scaleway IP address
   133  type ScalewayIPAddress struct {
   134  	// Identifier is a unique identifier for the IP address
   135  	Identifier string `json:"id,omitempty"`
   136  
   137  	// IP is an IPv4 address
   138  	IP string `json:"address,omitempty"`
   139  
   140  	// Dynamic is a flag that defines an IP that change on each reboot
   141  	Dynamic *bool `json:"dynamic,omitempty"`
   142  }
   143  
   144  // ScalewayVolume represents a Scaleway Volume
   145  type ScalewayVolume struct {
   146  	// Identifier is a unique identifier for the volume
   147  	Identifier string `json:"id,omitempty"`
   148  
   149  	// Size is the allocated size of the volume
   150  	Size uint64 `json:"size,omitempty"`
   151  
   152  	// CreationDate is the creation date of the volume
   153  	CreationDate string `json:"creation_date,omitempty"`
   154  
   155  	// ModificationDate is the date of the last modification of the volume
   156  	ModificationDate string `json:"modification_date,omitempty"`
   157  
   158  	// Organization is the organization owning the volume
   159  	Organization string `json:"organization,omitempty"`
   160  
   161  	// Name is the name of the volume
   162  	Name string `json:"name,omitempty"`
   163  
   164  	// Server is the server using this image
   165  	Server *struct {
   166  		Identifier string `json:"id,omitempty"`
   167  		Name       string `json:"name,omitempty"`
   168  	} `json:"server,omitempty"`
   169  
   170  	// VolumeType is a Scaleway identifier for the kind of volume (default: l_ssd)
   171  	VolumeType string `json:"volume_type,omitempty"`
   172  
   173  	// ExportURI represents the url used by initrd/scripts to attach the volume
   174  	ExportURI string `json:"export_uri,omitempty"`
   175  }
   176  
   177  // ScalewayOneVolume represents the response of a GET /volumes/UUID API call
   178  type ScalewayOneVolume struct {
   179  	Volume ScalewayVolume `json:"volume,omitempty"`
   180  }
   181  
   182  // ScalewayVolumes represents a group of Scaleway volumes
   183  type ScalewayVolumes struct {
   184  	// Volumes holds scaleway volumes of the response
   185  	Volumes []ScalewayVolume `json:"volumes,omitempty"`
   186  }
   187  
   188  // ScalewayVolumeDefinition represents a Scaleway volume definition
   189  type ScalewayVolumeDefinition struct {
   190  	// Name is the user-defined name of the volume
   191  	Name string `json:"name"`
   192  
   193  	// Image is the image used by the volume
   194  	Size uint64 `json:"size"`
   195  
   196  	// Bootscript is the bootscript used by the volume
   197  	Type string `json:"volume_type"`
   198  
   199  	// Organization is the owner of the volume
   200  	Organization string `json:"organization"`
   201  }
   202  
   203  // ScalewayVolumePutDefinition represents a Scaleway volume with nullable fields (for PUT)
   204  type ScalewayVolumePutDefinition struct {
   205  	Identifier       *string `json:"id,omitempty"`
   206  	Size             *uint64 `json:"size,omitempty"`
   207  	CreationDate     *string `json:"creation_date,omitempty"`
   208  	ModificationDate *string `json:"modification_date,omitempty"`
   209  	Organization     *string `json:"organization,omitempty"`
   210  	Name             *string `json:"name,omitempty"`
   211  	Server           struct {
   212  		Identifier *string `json:"id,omitempty"`
   213  		Name       *string `json:"name,omitempty"`
   214  	} `json:"server,omitempty"`
   215  	VolumeType *string `json:"volume_type,omitempty"`
   216  	ExportURI  *string `json:"export_uri,omitempty"`
   217  }
   218  
   219  // ScalewayImage represents a Scaleway Image
   220  type ScalewayImage struct {
   221  	// Identifier is a unique identifier for the image
   222  	Identifier string `json:"id,omitempty"`
   223  
   224  	// Name is a user-defined name for the image
   225  	Name string `json:"name,omitempty"`
   226  
   227  	// CreationDate is the creation date of the image
   228  	CreationDate string `json:"creation_date,omitempty"`
   229  
   230  	// ModificationDate is the date of the last modification of the image
   231  	ModificationDate string `json:"modification_date,omitempty"`
   232  
   233  	// RootVolume is the root volume bound to the image
   234  	RootVolume ScalewayVolume `json:"root_volume,omitempty"`
   235  
   236  	// Public is true for public images and false for user images
   237  	Public bool `json:"public,omitempty"`
   238  
   239  	// Bootscript is the bootscript bound to the image
   240  	DefaultBootscript *ScalewayBootscript `json:"default_bootscript,omitempty"`
   241  
   242  	// Organization is the owner of the image
   243  	Organization string `json:"organization,omitempty"`
   244  
   245  	// Arch is the architecture target of the image
   246  	Arch string `json:"arch,omitempty"`
   247  
   248  	// FIXME: extra_volumes
   249  }
   250  
   251  // ScalewayImageIdentifier represents a Scaleway Image Identifier
   252  type ScalewayImageIdentifier struct {
   253  	Identifier string
   254  	Arch       string
   255  	Region     string
   256  	Owner      string
   257  }
   258  
   259  // ScalewayOneImage represents the response of a GET /images/UUID API call
   260  type ScalewayOneImage struct {
   261  	Image ScalewayImage `json:"image,omitempty"`
   262  }
   263  
   264  // ScalewayImages represents a group of Scaleway images
   265  type ScalewayImages struct {
   266  	// Images holds scaleway images of the response
   267  	Images []ScalewayImage `json:"images,omitempty"`
   268  }
   269  
   270  // ScalewaySnapshot represents a Scaleway Snapshot
   271  type ScalewaySnapshot struct {
   272  	// Identifier is a unique identifier for the snapshot
   273  	Identifier string `json:"id,omitempty"`
   274  
   275  	// Name is a user-defined name for the snapshot
   276  	Name string `json:"name,omitempty"`
   277  
   278  	// CreationDate is the creation date of the snapshot
   279  	CreationDate string `json:"creation_date,omitempty"`
   280  
   281  	// ModificationDate is the date of the last modification of the snapshot
   282  	ModificationDate string `json:"modification_date,omitempty"`
   283  
   284  	// Size is the allocated size of the volume
   285  	Size uint64 `json:"size,omitempty"`
   286  
   287  	// Organization is the owner of the snapshot
   288  	Organization string `json:"organization"`
   289  
   290  	// State is the current state of the snapshot
   291  	State string `json:"state"`
   292  
   293  	// VolumeType is the kind of volume behind the snapshot
   294  	VolumeType string `json:"volume_type"`
   295  
   296  	// BaseVolume is the volume from which the snapshot inherits
   297  	BaseVolume ScalewayVolume `json:"base_volume,omitempty"`
   298  }
   299  
   300  // ScalewayOneSnapshot represents the response of a GET /snapshots/UUID API call
   301  type ScalewayOneSnapshot struct {
   302  	Snapshot ScalewaySnapshot `json:"snapshot,omitempty"`
   303  }
   304  
   305  // ScalewaySnapshots represents a group of Scaleway snapshots
   306  type ScalewaySnapshots struct {
   307  	// Snapshots holds scaleway snapshots of the response
   308  	Snapshots []ScalewaySnapshot `json:"snapshots,omitempty"`
   309  }
   310  
   311  // ScalewayBootscript represents a Scaleway Bootscript
   312  type ScalewayBootscript struct {
   313  	Bootcmdargs string `json:"bootcmdargs,omitempty"`
   314  	Dtb         string `json:"dtb,omitempty"`
   315  	Initrd      string `json:"initrd,omitempty"`
   316  	Kernel      string `json:"kernel,omitempty"`
   317  
   318  	// Arch is the architecture target of the bootscript
   319  	Arch string `json:"architecture,omitempty"`
   320  
   321  	// Identifier is a unique identifier for the bootscript
   322  	Identifier string `json:"id,omitempty"`
   323  
   324  	// Organization is the owner of the bootscript
   325  	Organization string `json:"organization,omitempty"`
   326  
   327  	// Name is a user-defined name for the bootscript
   328  	Title string `json:"title,omitempty"`
   329  
   330  	// Public is true for public bootscripts and false for user bootscripts
   331  	Public bool `json:"public,omitempty"`
   332  
   333  	Default bool `json:"default,omitempty"`
   334  }
   335  
   336  // ScalewayOneBootscript represents the response of a GET /bootscripts/UUID API call
   337  type ScalewayOneBootscript struct {
   338  	Bootscript ScalewayBootscript `json:"bootscript,omitempty"`
   339  }
   340  
   341  // ScalewayBootscripts represents a group of Scaleway bootscripts
   342  type ScalewayBootscripts struct {
   343  	// Bootscripts holds Scaleway bootscripts of the response
   344  	Bootscripts []ScalewayBootscript `json:"bootscripts,omitempty"`
   345  }
   346  
   347  // ScalewayTask represents a Scaleway Task
   348  type ScalewayTask struct {
   349  	// Identifier is a unique identifier for the task
   350  	Identifier string `json:"id,omitempty"`
   351  
   352  	// StartDate is the start date of the task
   353  	StartDate string `json:"started_at,omitempty"`
   354  
   355  	// TerminationDate is the termination date of the task
   356  	TerminationDate string `json:"terminated_at,omitempty"`
   357  
   358  	HrefFrom string `json:"href_from,omitempty"`
   359  
   360  	Description string `json:"description,omitempty"`
   361  
   362  	Status string `json:"status,omitempty"`
   363  
   364  	Progress int `json:"progress,omitempty"`
   365  }
   366  
   367  // ScalewayOneTask represents the response of a GET /tasks/UUID API call
   368  type ScalewayOneTask struct {
   369  	Task ScalewayTask `json:"task,omitempty"`
   370  }
   371  
   372  // ScalewayTasks represents a group of Scaleway tasks
   373  type ScalewayTasks struct {
   374  	// Tasks holds scaleway tasks of the response
   375  	Tasks []ScalewayTask `json:"tasks,omitempty"`
   376  }
   377  
   378  // ScalewaySecurityGroupRule definition
   379  type ScalewaySecurityGroupRule struct {
   380  	Direction    string `json:"direction"`
   381  	Protocol     string `json:"protocol"`
   382  	IPRange      string `json:"ip_range"`
   383  	DestPortFrom int    `json:"dest_port_from,omitempty"`
   384  	Action       string `json:"action"`
   385  	Position     int    `json:"position"`
   386  	DestPortTo   string `json:"dest_port_to"`
   387  	Editable     bool   `json:"editable"`
   388  	ID           string `json:"id"`
   389  }
   390  
   391  // ScalewayGetSecurityGroupRules represents the response of a GET /security_group/{groupID}/rules
   392  type ScalewayGetSecurityGroupRules struct {
   393  	Rules []ScalewaySecurityGroupRule `json:"rules"`
   394  }
   395  
   396  // ScalewayGetSecurityGroupRule represents the response of a GET /security_group/{groupID}/rules/{ruleID}
   397  type ScalewayGetSecurityGroupRule struct {
   398  	Rules ScalewaySecurityGroupRule `json:"rule"`
   399  }
   400  
   401  // ScalewayNewSecurityGroupRule definition POST/PUT request /security_group/{groupID}
   402  type ScalewayNewSecurityGroupRule struct {
   403  	Action       string `json:"action"`
   404  	Direction    string `json:"direction"`
   405  	IPRange      string `json:"ip_range"`
   406  	Protocol     string `json:"protocol"`
   407  	DestPortFrom int    `json:"dest_port_from,omitempty"`
   408  }
   409  
   410  // ScalewaySecurityGroups definition
   411  type ScalewaySecurityGroups struct {
   412  	Description           string                  `json:"description"`
   413  	ID                    string                  `json:"id"`
   414  	Organization          string                  `json:"organization"`
   415  	Name                  string                  `json:"name"`
   416  	Servers               []ScalewaySecurityGroup `json:"servers"`
   417  	EnableDefaultSecurity bool                    `json:"enable_default_security"`
   418  	OrganizationDefault   bool                    `json:"organization_default"`
   419  }
   420  
   421  // ScalewayGetSecurityGroups represents the response of a GET /security_groups/
   422  type ScalewayGetSecurityGroups struct {
   423  	SecurityGroups []ScalewaySecurityGroups `json:"security_groups"`
   424  }
   425  
   426  // ScalewayGetSecurityGroup represents the response of a GET /security_groups/{groupID}
   427  type ScalewayGetSecurityGroup struct {
   428  	SecurityGroups ScalewaySecurityGroups `json:"security_group"`
   429  }
   430  
   431  // ScalewayIPDefinition represents the IP's fields
   432  type ScalewayIPDefinition struct {
   433  	Organization string  `json:"organization"`
   434  	Reverse      *string `json:"reverse"`
   435  	ID           string  `json:"id"`
   436  	Server       *struct {
   437  		Identifier string `json:"id,omitempty"`
   438  		Name       string `json:"name,omitempty"`
   439  	} `json:"server"`
   440  	Address string `json:"address"`
   441  }
   442  
   443  // ScalewayGetIPS represents the response of a GET /ips/
   444  type ScalewayGetIPS struct {
   445  	IPS []ScalewayIPDefinition `json:"ips"`
   446  }
   447  
   448  // ScalewayGetIP represents the response of a GET /ips/{id_ip}
   449  type ScalewayGetIP struct {
   450  	IP ScalewayIPDefinition `json:"ip"`
   451  }
   452  
   453  // ScalewaySecurityGroup represents a Scaleway security group
   454  type ScalewaySecurityGroup struct {
   455  	// Identifier is a unique identifier for the security group
   456  	Identifier string `json:"id,omitempty"`
   457  
   458  	// Name is the user-defined name of the security group
   459  	Name string `json:"name,omitempty"`
   460  }
   461  
   462  // ScalewayNewSecurityGroup definition POST request /security_groups
   463  type ScalewayNewSecurityGroup struct {
   464  	Organization string `json:"organization"`
   465  	Name         string `json:"name"`
   466  	Description  string `json:"description"`
   467  }
   468  
   469  // ScalewayUpdateSecurityGroup definition PUT request /security_groups
   470  type ScalewayUpdateSecurityGroup struct {
   471  	Organization        string `json:"organization"`
   472  	Name                string `json:"name"`
   473  	Description         string `json:"description"`
   474  	OrganizationDefault bool   `json:"organization_default"`
   475  }
   476  
   477  // ScalewayServer represents a Scaleway server
   478  type ScalewayServer struct {
   479  	// Arch is the architecture target of the server
   480  	Arch string `json:"arch,omitempty"`
   481  
   482  	// Identifier is a unique identifier for the server
   483  	Identifier string `json:"id,omitempty"`
   484  
   485  	// Name is the user-defined name of the server
   486  	Name string `json:"name,omitempty"`
   487  
   488  	// CreationDate is the creation date of the server
   489  	CreationDate string `json:"creation_date,omitempty"`
   490  
   491  	// ModificationDate is the date of the last modification of the server
   492  	ModificationDate string `json:"modification_date,omitempty"`
   493  
   494  	// Image is the image used by the server
   495  	Image ScalewayImage `json:"image,omitempty"`
   496  
   497  	// DynamicIPRequired is a flag that defines a server with a dynamic ip address attached
   498  	DynamicIPRequired *bool `json:"dynamic_ip_required,omitempty"`
   499  
   500  	// PublicIP is the public IP address bound to the server
   501  	PublicAddress ScalewayIPAddress `json:"public_ip,omitempty"`
   502  
   503  	// State is the current status of the server
   504  	State string `json:"state,omitempty"`
   505  
   506  	// StateDetail is the detailed status of the server
   507  	StateDetail string `json:"state_detail,omitempty"`
   508  
   509  	// PrivateIP represents the private IPV4 attached to the server (changes on each boot)
   510  	PrivateIP string `json:"private_ip,omitempty"`
   511  
   512  	// Bootscript is the unique identifier of the selected bootscript
   513  	Bootscript *ScalewayBootscript `json:"bootscript,omitempty"`
   514  
   515  	// Hostname represents the ServerName in a format compatible with unix's hostname
   516  	Hostname string `json:"hostname,omitempty"`
   517  
   518  	// Tags represents user-defined tags
   519  	Tags []string `json:"tags,omitempty"`
   520  
   521  	// Volumes are the attached volumes
   522  	Volumes map[string]ScalewayVolume `json:"volumes,omitempty"`
   523  
   524  	// SecurityGroup is the selected security group object
   525  	SecurityGroup ScalewaySecurityGroup `json:"security_group,omitempty"`
   526  
   527  	// Organization is the owner of the server
   528  	Organization string `json:"organization,omitempty"`
   529  
   530  	// CommercialType is the commercial type of the server (i.e: C1, C2[SML], VC1S)
   531  	CommercialType string `json:"commercial_type,omitempty"`
   532  
   533  	// Location of the server
   534  	Location struct {
   535  		Platform   string `json:"platform_id,omitempty"`
   536  		Chassis    string `json:"chassis_id,omitempty"`
   537  		Cluster    string `json:"cluster_id,omitempty"`
   538  		Hypervisor string `json:"hypervisor_id,omitempty"`
   539  		Blade      string `json:"blade_id,omitempty"`
   540  		Node       string `json:"node_id,omitempty"`
   541  		ZoneID     string `json:"zone_id,omitempty"`
   542  	} `json:"location,omitempty"`
   543  
   544  	IPV6 *ScalewayIPV6Definition `json:"ipv6,omitempty"`
   545  
   546  	EnableIPV6 bool `json:"enable_ipv6,omitempty"`
   547  
   548  	// This fields are not returned by the API, we generate it
   549  	DNSPublic  string `json:"dns_public,omitempty"`
   550  	DNSPrivate string `json:"dns_private,omitempty"`
   551  }
   552  
   553  // ScalewayIPV6Definition represents a Scaleway ipv6
   554  type ScalewayIPV6Definition struct {
   555  	Netmask string `json:"netmask"`
   556  	Gateway string `json:"gateway"`
   557  	Address string `json:"address"`
   558  }
   559  
   560  // ScalewayServerPatchDefinition represents a Scaleway server with nullable fields (for PATCH)
   561  type ScalewayServerPatchDefinition struct {
   562  	Arch              *string                    `json:"arch,omitempty"`
   563  	Name              *string                    `json:"name,omitempty"`
   564  	CreationDate      *string                    `json:"creation_date,omitempty"`
   565  	ModificationDate  *string                    `json:"modification_date,omitempty"`
   566  	Image             *ScalewayImage             `json:"image,omitempty"`
   567  	DynamicIPRequired *bool                      `json:"dynamic_ip_required,omitempty"`
   568  	PublicAddress     *ScalewayIPAddress         `json:"public_ip,omitempty"`
   569  	State             *string                    `json:"state,omitempty"`
   570  	StateDetail       *string                    `json:"state_detail,omitempty"`
   571  	PrivateIP         *string                    `json:"private_ip,omitempty"`
   572  	Bootscript        *string                    `json:"bootscript,omitempty"`
   573  	Hostname          *string                    `json:"hostname,omitempty"`
   574  	Volumes           *map[string]ScalewayVolume `json:"volumes,omitempty"`
   575  	SecurityGroup     *ScalewaySecurityGroup     `json:"security_group,omitempty"`
   576  	Organization      *string                    `json:"organization,omitempty"`
   577  	Tags              *[]string                  `json:"tags,omitempty"`
   578  	IPV6              *ScalewayIPV6Definition    `json:"ipv6,omitempty"`
   579  	EnableIPV6        *bool                      `json:"enable_ipv6,omitempty"`
   580  }
   581  
   582  // ScalewayServerDefinition represents a Scaleway server with image definition
   583  type ScalewayServerDefinition struct {
   584  	// Name is the user-defined name of the server
   585  	Name string `json:"name"`
   586  
   587  	// Image is the image used by the server
   588  	Image *string `json:"image,omitempty"`
   589  
   590  	// Volumes are the attached volumes
   591  	Volumes map[string]string `json:"volumes,omitempty"`
   592  
   593  	// DynamicIPRequired is a flag that defines a server with a dynamic ip address attached
   594  	DynamicIPRequired *bool `json:"dynamic_ip_required,omitempty"`
   595  
   596  	// Bootscript is the bootscript used by the server
   597  	Bootscript *string `json:"bootscript"`
   598  
   599  	// Tags are the metadata tags attached to the server
   600  	Tags []string `json:"tags,omitempty"`
   601  
   602  	// Organization is the owner of the server
   603  	Organization string `json:"organization"`
   604  
   605  	// CommercialType is the commercial type of the server (i.e: C1, C2[SML], VC1S)
   606  	CommercialType string `json:"commercial_type"`
   607  
   608  	PublicIP string `json:"public_ip,omitempty"`
   609  
   610  	EnableIPV6 bool `json:"enable_ipv6,omitempty"`
   611  
   612  	SecurityGroup string `json:"security_group,omitempty"`
   613  }
   614  
   615  // ScalewayOneServer represents the response of a GET /servers/UUID API call
   616  type ScalewayOneServer struct {
   617  	Server ScalewayServer `json:"server,omitempty"`
   618  }
   619  
   620  // ScalewayServers represents a group of Scaleway servers
   621  type ScalewayServers struct {
   622  	// Servers holds scaleway servers of the response
   623  	Servers []ScalewayServer `json:"servers,omitempty"`
   624  }
   625  
   626  // ScalewayServerAction represents an action to perform on a Scaleway server
   627  type ScalewayServerAction struct {
   628  	// Action is the name of the action to trigger
   629  	Action string `json:"action,omitempty"`
   630  }
   631  
   632  // ScalewaySnapshotDefinition represents a Scaleway snapshot definition
   633  type ScalewaySnapshotDefinition struct {
   634  	VolumeIDentifier string `json:"volume_id"`
   635  	Name             string `json:"name,omitempty"`
   636  	Organization     string `json:"organization"`
   637  }
   638  
   639  // ScalewayImageDefinition represents a Scaleway image definition
   640  type ScalewayImageDefinition struct {
   641  	SnapshotIDentifier string  `json:"root_volume"`
   642  	Name               string  `json:"name,omitempty"`
   643  	Organization       string  `json:"organization"`
   644  	Arch               string  `json:"arch"`
   645  	DefaultBootscript  *string `json:"default_bootscript,omitempty"`
   646  }
   647  
   648  // ScalewayRoleDefinition represents a Scaleway Token UserId Role
   649  type ScalewayRoleDefinition struct {
   650  	Organization ScalewayOrganizationDefinition `json:"organization,omitempty"`
   651  	Role         string                         `json:"role,omitempty"`
   652  }
   653  
   654  // ScalewayTokenDefinition represents a Scaleway Token
   655  type ScalewayTokenDefinition struct {
   656  	UserID             string                 `json:"user_id"`
   657  	Description        string                 `json:"description,omitempty"`
   658  	Roles              ScalewayRoleDefinition `json:"roles"`
   659  	Expires            string                 `json:"expires"`
   660  	InheritsUsersPerms bool                   `json:"inherits_user_perms"`
   661  	ID                 string                 `json:"id"`
   662  }
   663  
   664  // ScalewayTokensDefinition represents a Scaleway Tokens
   665  type ScalewayTokensDefinition struct {
   666  	Token ScalewayTokenDefinition `json:"token"`
   667  }
   668  
   669  // ScalewayGetTokens represents a list of Scaleway Tokens
   670  type ScalewayGetTokens struct {
   671  	Tokens []ScalewayTokenDefinition `json:"tokens"`
   672  }
   673  
   674  // ScalewayContainerData represents a Scaleway container data (S3)
   675  type ScalewayContainerData struct {
   676  	LastModified string `json:"last_modified"`
   677  	Name         string `json:"name"`
   678  	Size         string `json:"size"`
   679  }
   680  
   681  // ScalewayGetContainerDatas represents a list of Scaleway containers data (S3)
   682  type ScalewayGetContainerDatas struct {
   683  	Container []ScalewayContainerData `json:"container"`
   684  }
   685  
   686  // ScalewayContainer represents a Scaleway container (S3)
   687  type ScalewayContainer struct {
   688  	ScalewayOrganizationDefinition `json:"organization"`
   689  	Name                           string `json:"name"`
   690  	Size                           string `json:"size"`
   691  }
   692  
   693  // ScalewayGetContainers represents a list of Scaleway containers (S3)
   694  type ScalewayGetContainers struct {
   695  	Containers []ScalewayContainer `json:"containers"`
   696  }
   697  
   698  // ScalewayConnectResponse represents the answer from POST /tokens
   699  type ScalewayConnectResponse struct {
   700  	Token ScalewayTokenDefinition `json:"token"`
   701  }
   702  
   703  // ScalewayConnect represents the data to connect
   704  type ScalewayConnect struct {
   705  	Email       string `json:"email"`
   706  	Password    string `json:"password"`
   707  	Description string `json:"description"`
   708  	Expires     bool   `json:"expires"`
   709  }
   710  
   711  // ScalewayOrganizationDefinition represents a Scaleway Organization
   712  type ScalewayOrganizationDefinition struct {
   713  	ID    string                   `json:"id"`
   714  	Name  string                   `json:"name"`
   715  	Users []ScalewayUserDefinition `json:"users"`
   716  }
   717  
   718  // ScalewayOrganizationsDefinition represents a Scaleway Organizations
   719  type ScalewayOrganizationsDefinition struct {
   720  	Organizations []ScalewayOrganizationDefinition `json:"organizations"`
   721  }
   722  
   723  // ScalewayUserDefinition represents a Scaleway User
   724  type ScalewayUserDefinition struct {
   725  	Email         string                           `json:"email"`
   726  	Firstname     string                           `json:"firstname"`
   727  	Fullname      string                           `json:"fullname"`
   728  	ID            string                           `json:"id"`
   729  	Lastname      string                           `json:"lastname"`
   730  	Organizations []ScalewayOrganizationDefinition `json:"organizations"`
   731  	Roles         []ScalewayRoleDefinition         `json:"roles"`
   732  	SSHPublicKeys []ScalewayKeyDefinition          `json:"ssh_public_keys"`
   733  }
   734  
   735  // ScalewayUsersDefinition represents the response of a GET /user
   736  type ScalewayUsersDefinition struct {
   737  	User ScalewayUserDefinition `json:"user"`
   738  }
   739  
   740  // ScalewayKeyDefinition represents a key
   741  type ScalewayKeyDefinition struct {
   742  	Key         string `json:"key"`
   743  	Fingerprint string `json:"fingerprint,omitempty"`
   744  }
   745  
   746  // ScalewayUserPatchSSHKeyDefinition represents a User Patch
   747  type ScalewayUserPatchSSHKeyDefinition struct {
   748  	SSHPublicKeys []ScalewayKeyDefinition `json:"ssh_public_keys"`
   749  }
   750  
   751  // ScalewayDashboardResp represents a dashboard received from the API
   752  type ScalewayDashboardResp struct {
   753  	Dashboard ScalewayDashboard
   754  }
   755  
   756  // ScalewayDashboard represents a dashboard
   757  type ScalewayDashboard struct {
   758  	VolumesCount        int `json:"volumes_count"`
   759  	RunningServersCount int `json:"running_servers_count"`
   760  	ImagesCount         int `json:"images_count"`
   761  	SnapshotsCount      int `json:"snapshots_count"`
   762  	ServersCount        int `json:"servers_count"`
   763  	IPsCount            int `json:"ips_count"`
   764  }
   765  
   766  // ScalewayPermissions represents the response of GET /permissions
   767  type ScalewayPermissions map[string]ScalewayPermCategory
   768  
   769  // ScalewayPermCategory represents ScalewayPermissions's fields
   770  type ScalewayPermCategory map[string][]string
   771  
   772  // ScalewayPermissionDefinition represents the permissions
   773  type ScalewayPermissionDefinition struct {
   774  	Permissions ScalewayPermissions `json:"permissions"`
   775  }
   776  
   777  // ScalewayUserdatas represents the response of a GET /user_data
   778  type ScalewayUserdatas struct {
   779  	UserData []string `json:"user_data"`
   780  }
   781  
   782  // ScalewayQuota represents a map of quota (name, value)
   783  type ScalewayQuota map[string]int
   784  
   785  // ScalewayGetQuotas represents the response of GET /organizations/{orga_id}/quotas
   786  type ScalewayGetQuotas struct {
   787  	Quotas ScalewayQuota `json:"quotas"`
   788  }
   789  
   790  // ScalewayUserdata represents []byte
   791  type ScalewayUserdata []byte
   792  
   793  // FuncMap used for json inspection
   794  var FuncMap = template.FuncMap{
   795  	"json": func(v interface{}) string {
   796  		a, _ := json.Marshal(v)
   797  		return string(a)
   798  	},
   799  }
   800  
   801  // MarketLocalImageDefinition represents localImage of marketplace version
   802  type MarketLocalImageDefinition struct {
   803  	Arch string `json:"arch"`
   804  	ID   string `json:"id"`
   805  	Zone string `json:"zone"`
   806  }
   807  
   808  // MarketLocalImages represents an array of local images
   809  type MarketLocalImages struct {
   810  	LocalImages []MarketLocalImageDefinition `json:"local_images"`
   811  }
   812  
   813  // MarketLocalImage represents local image
   814  type MarketLocalImage struct {
   815  	LocalImages MarketLocalImageDefinition `json:"local_image"`
   816  }
   817  
   818  // MarketVersionDefinition represents version of marketplace image
   819  type MarketVersionDefinition struct {
   820  	CreationDate string `json:"creation_date"`
   821  	ID           string `json:"id"`
   822  	Image        struct {
   823  		ID   string `json:"id"`
   824  		Name string `json:"name"`
   825  	} `json:"image"`
   826  	ModificationDate string `json:"modification_date"`
   827  	Name             string `json:"name"`
   828  	MarketLocalImages
   829  }
   830  
   831  // MarketVersions represents an array of marketplace image versions
   832  type MarketVersions struct {
   833  	Versions []MarketVersionDefinition `json:"versions"`
   834  }
   835  
   836  // MarketVersion represents version of marketplace image
   837  type MarketVersion struct {
   838  	Version MarketVersionDefinition `json:"version"`
   839  }
   840  
   841  // MarketImage represents MarketPlace image
   842  type MarketImage struct {
   843  	Categories           []string `json:"categories"`
   844  	CreationDate         string   `json:"creation_date"`
   845  	CurrentPublicVersion string   `json:"current_public_version"`
   846  	Description          string   `json:"description"`
   847  	ID                   string   `json:"id"`
   848  	Logo                 string   `json:"logo"`
   849  	ModificationDate     string   `json:"modification_date"`
   850  	Name                 string   `json:"name"`
   851  	Organization         struct {
   852  		ID   string `json:"id"`
   853  		Name string `json:"name"`
   854  	} `json:"organization"`
   855  	Public bool `json:"-"`
   856  	MarketVersions
   857  }
   858  
   859  // MarketImages represents MarketPlace images
   860  type MarketImages struct {
   861  	Images []MarketImage `json:"images"`
   862  }
   863  
   864  // NewScalewayAPI creates a ready-to-use ScalewayAPI client
   865  func NewScalewayAPI(organization, token, userAgent, region string, options ...func(*ScalewayAPI)) (*ScalewayAPI, error) {
   866  	s := &ScalewayAPI{
   867  		// exposed
   868  		Organization: organization,
   869  		Token:        token,
   870  		Logger:       NewDefaultLogger(),
   871  
   872  		// internal
   873  		client:    &http.Client{},
   874  		verbose:   os.Getenv("SCW_VERBOSE_API") != "",
   875  		password:  "",
   876  		userAgent: userAgent,
   877  	}
   878  	for _, option := range options {
   879  		option(s)
   880  	}
   881  	cache, err := NewScalewayCache(func() { s.Logger.Debugf("Writing cache file to disk") })
   882  	if err != nil {
   883  		return nil, err
   884  	}
   885  	s.Cache = cache
   886  	if os.Getenv("SCW_TLSVERIFY") == "0" {
   887  		s.client.Transport = &http.Transport{
   888  			TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
   889  		}
   890  	}
   891  	switch region {
   892  	case "par1", "":
   893  		s.computeAPI = ComputeAPIPar1
   894  	case "ams1":
   895  		s.computeAPI = ComputeAPIAms1
   896  	default:
   897  		return nil, fmt.Errorf("%s isn't a valid region", region)
   898  	}
   899  	s.Region = region
   900  	if url := os.Getenv("SCW_COMPUTE_API"); url != "" {
   901  		s.computeAPI = url
   902  	}
   903  	return s, nil
   904  }
   905  
   906  // ClearCache clears the cache
   907  func (s *ScalewayAPI) ClearCache() {
   908  	s.Cache.Clear()
   909  }
   910  
   911  // Sync flushes out the cache to the disk
   912  func (s *ScalewayAPI) Sync() {
   913  	s.Cache.Save()
   914  }
   915  
   916  func (s *ScalewayAPI) response(method, uri string, content io.Reader) (resp *http.Response, err error) {
   917  	var (
   918  		req *http.Request
   919  	)
   920  
   921  	req, err = http.NewRequest(method, uri, content)
   922  	if err != nil {
   923  		err = fmt.Errorf("response %s %s", method, uri)
   924  		return
   925  	}
   926  	req.Header.Set("X-Auth-Token", s.Token)
   927  	req.Header.Set("Content-Type", "application/json")
   928  	req.Header.Set("User-Agent", s.userAgent)
   929  	s.LogHTTP(req)
   930  	if s.verbose {
   931  		dump, _ := httputil.DumpRequest(req, true)
   932  		s.Debugf("%v", string(dump))
   933  	} else {
   934  		s.Debugf("[%s]: %v", method, uri)
   935  	}
   936  	resp, err = s.client.Do(req)
   937  	return
   938  }
   939  
   940  // GetResponsePaginate fetchs all resources and returns an http.Response object for the requested resource
   941  func (s *ScalewayAPI) GetResponsePaginate(apiURL, resource string, values url.Values) (*http.Response, error) {
   942  	resp, err := s.response("HEAD", fmt.Sprintf("%s/%s?%s", strings.TrimRight(apiURL, "/"), resource, values.Encode()), nil)
   943  	if err != nil {
   944  		return nil, err
   945  	}
   946  
   947  	count := resp.Header.Get("X-Total-Count")
   948  	var maxElem int
   949  	if count == "" {
   950  		maxElem = 0
   951  	} else {
   952  		maxElem, err = strconv.Atoi(count)
   953  		if err != nil {
   954  			return nil, err
   955  		}
   956  	}
   957  
   958  	get := maxElem / perPage
   959  	if (float32(maxElem) / perPage) > float32(get) {
   960  		get++
   961  	}
   962  
   963  	if get <= 1 { // If there is 0 or 1 page of result, the response is not paginated
   964  		if len(values) == 0 {
   965  			return s.response("GET", fmt.Sprintf("%s/%s", strings.TrimRight(apiURL, "/"), resource), nil)
   966  		}
   967  		return s.response("GET", fmt.Sprintf("%s/%s?%s", strings.TrimRight(apiURL, "/"), resource, values.Encode()), nil)
   968  	}
   969  
   970  	fetchAll := !(values.Get("per_page") != "" || values.Get("page") != "")
   971  	if fetchAll {
   972  		var g errgroup.Group
   973  
   974  		ch := make(chan *http.Response, get)
   975  		for i := 1; i <= get; i++ {
   976  			i := i // closure tricks
   977  			g.Go(func() (err error) {
   978  				var resp *http.Response
   979  
   980  				val := url.Values{}
   981  				val.Set("per_page", fmt.Sprintf("%v", perPage))
   982  				val.Set("page", fmt.Sprintf("%v", i))
   983  				resp, err = s.response("GET", fmt.Sprintf("%s/%s?%s", strings.TrimRight(apiURL, "/"), resource, val.Encode()), nil)
   984  				ch <- resp
   985  				return
   986  			})
   987  		}
   988  		if err = g.Wait(); err != nil {
   989  			return nil, err
   990  		}
   991  		newBody := make(map[string][]json.RawMessage)
   992  		body := make(map[string][]json.RawMessage)
   993  		key := ""
   994  		for i := 0; i < get; i++ {
   995  			res := <-ch
   996  			if res.StatusCode != http.StatusOK {
   997  				return res, nil
   998  			}
   999  			content, err := ioutil.ReadAll(res.Body)
  1000  			res.Body.Close()
  1001  			if err != nil {
  1002  				return nil, err
  1003  			}
  1004  			if err := json.Unmarshal(content, &body); err != nil {
  1005  				return nil, err
  1006  			}
  1007  
  1008  			if i == 0 {
  1009  				resp = res
  1010  				for k := range body {
  1011  					key = k
  1012  					break
  1013  				}
  1014  			}
  1015  			newBody[key] = append(newBody[key], body[key]...)
  1016  		}
  1017  		payload := new(bytes.Buffer)
  1018  		if err := json.NewEncoder(payload).Encode(newBody); err != nil {
  1019  			return nil, err
  1020  		}
  1021  		resp.Body = ioutil.NopCloser(payload)
  1022  	} else {
  1023  		resp, err = s.response("GET", fmt.Sprintf("%s/%s?%s", strings.TrimRight(apiURL, "/"), resource, values.Encode()), nil)
  1024  	}
  1025  	return resp, err
  1026  }
  1027  
  1028  // PostResponse returns an http.Response object for the updated resource
  1029  func (s *ScalewayAPI) PostResponse(apiURL, resource string, data interface{}) (*http.Response, error) {
  1030  	payload := new(bytes.Buffer)
  1031  	if err := json.NewEncoder(payload).Encode(data); err != nil {
  1032  		return nil, err
  1033  	}
  1034  	return s.response("POST", fmt.Sprintf("%s/%s", strings.TrimRight(apiURL, "/"), resource), payload)
  1035  }
  1036  
  1037  // PatchResponse returns an http.Response object for the updated resource
  1038  func (s *ScalewayAPI) PatchResponse(apiURL, resource string, data interface{}) (*http.Response, error) {
  1039  	payload := new(bytes.Buffer)
  1040  	if err := json.NewEncoder(payload).Encode(data); err != nil {
  1041  		return nil, err
  1042  	}
  1043  	return s.response("PATCH", fmt.Sprintf("%s/%s", strings.TrimRight(apiURL, "/"), resource), payload)
  1044  }
  1045  
  1046  // PutResponse returns an http.Response object for the updated resource
  1047  func (s *ScalewayAPI) PutResponse(apiURL, resource string, data interface{}) (*http.Response, error) {
  1048  	payload := new(bytes.Buffer)
  1049  	if err := json.NewEncoder(payload).Encode(data); err != nil {
  1050  		return nil, err
  1051  	}
  1052  	return s.response("PUT", fmt.Sprintf("%s/%s", strings.TrimRight(apiURL, "/"), resource), payload)
  1053  }
  1054  
  1055  // DeleteResponse returns an http.Response object for the deleted resource
  1056  func (s *ScalewayAPI) DeleteResponse(apiURL, resource string) (*http.Response, error) {
  1057  	return s.response("DELETE", fmt.Sprintf("%s/%s", strings.TrimRight(apiURL, "/"), resource), nil)
  1058  }
  1059  
  1060  // handleHTTPError checks the statusCode and displays the error
  1061  func (s *ScalewayAPI) handleHTTPError(goodStatusCode []int, resp *http.Response) ([]byte, error) {
  1062  	body, err := ioutil.ReadAll(resp.Body)
  1063  	if err != nil {
  1064  		return nil, err
  1065  	}
  1066  	if s.verbose {
  1067  		resp.Body = ioutil.NopCloser(bytes.NewBuffer(body))
  1068  		dump, err := httputil.DumpResponse(resp, true)
  1069  		if err == nil {
  1070  			var js bytes.Buffer
  1071  
  1072  			err = json.Indent(&js, body, "", "  ")
  1073  			if err != nil {
  1074  				s.Debugf("[Response]: [%v]\n%v", resp.StatusCode, string(dump))
  1075  			} else {
  1076  				s.Debugf("[Response]: [%v]\n%v", resp.StatusCode, js.String())
  1077  			}
  1078  		}
  1079  	} else {
  1080  		s.Debugf("[Response]: [%v]\n%v", resp.StatusCode, string(body))
  1081  	}
  1082  
  1083  	if resp.StatusCode >= http.StatusInternalServerError {
  1084  		return nil, errors.New(string(body))
  1085  	}
  1086  	good := false
  1087  	for _, code := range goodStatusCode {
  1088  		if code == resp.StatusCode {
  1089  			good = true
  1090  		}
  1091  	}
  1092  	if !good {
  1093  		var scwError ScalewayAPIError
  1094  
  1095  		if err := json.Unmarshal(body, &scwError); err != nil {
  1096  			return nil, err
  1097  		}
  1098  		scwError.StatusCode = resp.StatusCode
  1099  		s.Debugf("%s", scwError.Error())
  1100  		return nil, scwError
  1101  	}
  1102  	return body, nil
  1103  }
  1104  
  1105  func (s *ScalewayAPI) fetchServers(api string, query url.Values, out chan<- ScalewayServers) func() error {
  1106  	return func() error {
  1107  		resp, err := s.GetResponsePaginate(api, "servers", query)
  1108  		if resp != nil {
  1109  			defer resp.Body.Close()
  1110  		}
  1111  		if err != nil {
  1112  			return err
  1113  		}
  1114  
  1115  		body, err := s.handleHTTPError([]int{http.StatusOK}, resp)
  1116  		if err != nil {
  1117  			return err
  1118  		}
  1119  		var servers ScalewayServers
  1120  
  1121  		if err = json.Unmarshal(body, &servers); err != nil {
  1122  			return err
  1123  		}
  1124  		out <- servers
  1125  		return nil
  1126  	}
  1127  }
  1128  
  1129  // GetServers gets the list of servers from the ScalewayAPI
  1130  func (s *ScalewayAPI) GetServers(all bool, limit int) (*[]ScalewayServer, error) {
  1131  	query := url.Values{}
  1132  	if !all {
  1133  		query.Set("state", "running")
  1134  	}
  1135  	if limit > 0 {
  1136  		// FIXME: wait for the API to be ready
  1137  		// query.Set("per_page", strconv.Itoa(limit))
  1138  		panic("Not implemented yet")
  1139  	}
  1140  	if all && limit == 0 {
  1141  		s.Cache.ClearServers()
  1142  	}
  1143  	var (
  1144  		g    errgroup.Group
  1145  		apis = []string{
  1146  			ComputeAPIPar1,
  1147  			ComputeAPIAms1,
  1148  		}
  1149  	)
  1150  
  1151  	serverChan := make(chan ScalewayServers, 2)
  1152  	for _, api := range apis {
  1153  		g.Go(s.fetchServers(api, query, serverChan))
  1154  	}
  1155  
  1156  	if err := g.Wait(); err != nil {
  1157  		return nil, err
  1158  	}
  1159  	close(serverChan)
  1160  	var servers ScalewayServers
  1161  
  1162  	for server := range serverChan {
  1163  		servers.Servers = append(servers.Servers, server.Servers...)
  1164  	}
  1165  
  1166  	for i, server := range servers.Servers {
  1167  		servers.Servers[i].DNSPublic = server.Identifier + URLPublicDNS
  1168  		servers.Servers[i].DNSPrivate = server.Identifier + URLPrivateDNS
  1169  		s.Cache.InsertServer(server.Identifier, server.Location.ZoneID, server.Arch, server.Organization, server.Name)
  1170  	}
  1171  	return &servers.Servers, nil
  1172  }
  1173  
  1174  // ScalewaySortServers represents a wrapper to sort by CreationDate the servers
  1175  type ScalewaySortServers []ScalewayServer
  1176  
  1177  func (s ScalewaySortServers) Len() int {
  1178  	return len(s)
  1179  }
  1180  
  1181  func (s ScalewaySortServers) Swap(i, j int) {
  1182  	s[i], s[j] = s[j], s[i]
  1183  }
  1184  
  1185  func (s ScalewaySortServers) Less(i, j int) bool {
  1186  	date1, _ := time.Parse("2006-01-02T15:04:05.000000+00:00", s[i].CreationDate)
  1187  	date2, _ := time.Parse("2006-01-02T15:04:05.000000+00:00", s[j].CreationDate)
  1188  	return date2.Before(date1)
  1189  }
  1190  
  1191  // GetServer gets a server from the ScalewayAPI
  1192  func (s *ScalewayAPI) GetServer(serverID string) (*ScalewayServer, error) {
  1193  	resp, err := s.GetResponsePaginate(s.computeAPI, "servers/"+serverID, url.Values{})
  1194  	if resp != nil {
  1195  		defer resp.Body.Close()
  1196  	}
  1197  	if err != nil {
  1198  		return nil, err
  1199  	}
  1200  
  1201  	body, err := s.handleHTTPError([]int{http.StatusOK}, resp)
  1202  	if err != nil {
  1203  		return nil, err
  1204  	}
  1205  
  1206  	var oneServer ScalewayOneServer
  1207  
  1208  	if err = json.Unmarshal(body, &oneServer); err != nil {
  1209  		return nil, err
  1210  	}
  1211  	// FIXME arch, owner, title
  1212  	oneServer.Server.DNSPublic = oneServer.Server.Identifier + URLPublicDNS
  1213  	oneServer.Server.DNSPrivate = oneServer.Server.Identifier + URLPrivateDNS
  1214  	s.Cache.InsertServer(oneServer.Server.Identifier, oneServer.Server.Location.ZoneID, oneServer.Server.Arch, oneServer.Server.Organization, oneServer.Server.Name)
  1215  	return &oneServer.Server, nil
  1216  }
  1217  
  1218  // PostServerAction posts an action on a server
  1219  func (s *ScalewayAPI) PostServerAction(serverID, action string) error {
  1220  	data := ScalewayServerAction{
  1221  		Action: action,
  1222  	}
  1223  	resp, err := s.PostResponse(s.computeAPI, fmt.Sprintf("servers/%s/action", serverID), data)
  1224  	if resp != nil {
  1225  		defer resp.Body.Close()
  1226  	}
  1227  	if err != nil {
  1228  		return err
  1229  	}
  1230  
  1231  	_, err = s.handleHTTPError([]int{http.StatusAccepted}, resp)
  1232  	return err
  1233  }
  1234  
  1235  // DeleteServer deletes a server
  1236  func (s *ScalewayAPI) DeleteServer(serverID string) error {
  1237  	defer s.Cache.RemoveServer(serverID)
  1238  	resp, err := s.DeleteResponse(s.computeAPI, fmt.Sprintf("servers/%s", serverID))
  1239  	if resp != nil {
  1240  		defer resp.Body.Close()
  1241  	}
  1242  	if err != nil {
  1243  		return err
  1244  	}
  1245  
  1246  	if _, err = s.handleHTTPError([]int{http.StatusNoContent}, resp); err != nil {
  1247  		return err
  1248  	}
  1249  	return nil
  1250  }
  1251  
  1252  // PostServer creates a new server
  1253  func (s *ScalewayAPI) PostServer(definition ScalewayServerDefinition) (string, error) {
  1254  	definition.Organization = s.Organization
  1255  
  1256  	resp, err := s.PostResponse(s.computeAPI, "servers", definition)
  1257  	if resp != nil {
  1258  		defer resp.Body.Close()
  1259  	}
  1260  	if err != nil {
  1261  		return "", err
  1262  	}
  1263  
  1264  	body, err := s.handleHTTPError([]int{http.StatusCreated}, resp)
  1265  	if err != nil {
  1266  		return "", err
  1267  	}
  1268  	var server ScalewayOneServer
  1269  
  1270  	if err = json.Unmarshal(body, &server); err != nil {
  1271  		return "", err
  1272  	}
  1273  	// FIXME arch, owner, title
  1274  	s.Cache.InsertServer(server.Server.Identifier, server.Server.Location.ZoneID, server.Server.Arch, server.Server.Organization, server.Server.Name)
  1275  	return server.Server.Identifier, nil
  1276  }
  1277  
  1278  // PatchUserSSHKey updates a user
  1279  func (s *ScalewayAPI) PatchUserSSHKey(UserID string, definition ScalewayUserPatchSSHKeyDefinition) error {
  1280  	resp, err := s.PatchResponse(AccountAPI, fmt.Sprintf("users/%s", UserID), definition)
  1281  	if resp != nil {
  1282  		defer resp.Body.Close()
  1283  	}
  1284  	if err != nil {
  1285  		return err
  1286  	}
  1287  	if _, err := s.handleHTTPError([]int{http.StatusOK}, resp); err != nil {
  1288  		return err
  1289  	}
  1290  	return nil
  1291  }
  1292  
  1293  // PatchServer updates a server
  1294  func (s *ScalewayAPI) PatchServer(serverID string, definition ScalewayServerPatchDefinition) error {
  1295  	resp, err := s.PatchResponse(s.computeAPI, fmt.Sprintf("servers/%s", serverID), definition)
  1296  	if resp != nil {
  1297  		defer resp.Body.Close()
  1298  	}
  1299  	if err != nil {
  1300  		return err
  1301  	}
  1302  
  1303  	if _, err := s.handleHTTPError([]int{http.StatusOK}, resp); err != nil {
  1304  		return err
  1305  	}
  1306  	return nil
  1307  }
  1308  
  1309  // PostSnapshot creates a new snapshot
  1310  func (s *ScalewayAPI) PostSnapshot(volumeID string, name string) (string, error) {
  1311  	definition := ScalewaySnapshotDefinition{
  1312  		VolumeIDentifier: volumeID,
  1313  		Name:             name,
  1314  		Organization:     s.Organization,
  1315  	}
  1316  	resp, err := s.PostResponse(s.computeAPI, "snapshots", definition)
  1317  	if resp != nil {
  1318  		defer resp.Body.Close()
  1319  	}
  1320  	if err != nil {
  1321  		return "", err
  1322  	}
  1323  
  1324  	body, err := s.handleHTTPError([]int{http.StatusCreated}, resp)
  1325  	if err != nil {
  1326  		return "", err
  1327  	}
  1328  	var snapshot ScalewayOneSnapshot
  1329  
  1330  	if err = json.Unmarshal(body, &snapshot); err != nil {
  1331  		return "", err
  1332  	}
  1333  	// FIXME arch, owner, title
  1334  	s.Cache.InsertSnapshot(snapshot.Snapshot.Identifier, "", "", snapshot.Snapshot.Organization, snapshot.Snapshot.Name)
  1335  	return snapshot.Snapshot.Identifier, nil
  1336  }
  1337  
  1338  // PostImage creates a new image
  1339  func (s *ScalewayAPI) PostImage(volumeID string, name string, bootscript string, arch string) (string, error) {
  1340  	definition := ScalewayImageDefinition{
  1341  		SnapshotIDentifier: volumeID,
  1342  		Name:               name,
  1343  		Organization:       s.Organization,
  1344  		Arch:               arch,
  1345  	}
  1346  	if bootscript != "" {
  1347  		definition.DefaultBootscript = &bootscript
  1348  	}
  1349  
  1350  	resp, err := s.PostResponse(s.computeAPI, "images", definition)
  1351  	if resp != nil {
  1352  		defer resp.Body.Close()
  1353  	}
  1354  	if err != nil {
  1355  		return "", err
  1356  	}
  1357  
  1358  	body, err := s.handleHTTPError([]int{http.StatusCreated}, resp)
  1359  	if err != nil {
  1360  		return "", err
  1361  	}
  1362  	var image ScalewayOneImage
  1363  
  1364  	if err = json.Unmarshal(body, &image); err != nil {
  1365  		return "", err
  1366  	}
  1367  	// FIXME region, arch, owner, title
  1368  	s.Cache.InsertImage(image.Image.Identifier, "", image.Image.Arch, image.Image.Organization, image.Image.Name, "")
  1369  	return image.Image.Identifier, nil
  1370  }
  1371  
  1372  // PostVolume creates a new volume
  1373  func (s *ScalewayAPI) PostVolume(definition ScalewayVolumeDefinition) (string, error) {
  1374  	definition.Organization = s.Organization
  1375  	if definition.Type == "" {
  1376  		definition.Type = "l_ssd"
  1377  	}
  1378  
  1379  	resp, err := s.PostResponse(s.computeAPI, "volumes", definition)
  1380  	if resp != nil {
  1381  		defer resp.Body.Close()
  1382  	}
  1383  	if err != nil {
  1384  		return "", err
  1385  	}
  1386  
  1387  	body, err := s.handleHTTPError([]int{http.StatusCreated}, resp)
  1388  	if err != nil {
  1389  		return "", err
  1390  	}
  1391  	var volume ScalewayOneVolume
  1392  
  1393  	if err = json.Unmarshal(body, &volume); err != nil {
  1394  		return "", err
  1395  	}
  1396  	// FIXME: s.Cache.InsertVolume(volume.Volume.Identifier, volume.Volume.Name)
  1397  	return volume.Volume.Identifier, nil
  1398  }
  1399  
  1400  // PutVolume updates a volume
  1401  func (s *ScalewayAPI) PutVolume(volumeID string, definition ScalewayVolumePutDefinition) error {
  1402  	resp, err := s.PutResponse(s.computeAPI, fmt.Sprintf("volumes/%s", volumeID), definition)
  1403  	if resp != nil {
  1404  		defer resp.Body.Close()
  1405  	}
  1406  	if err != nil {
  1407  		return err
  1408  	}
  1409  
  1410  	_, err = s.handleHTTPError([]int{http.StatusOK}, resp)
  1411  	return err
  1412  }
  1413  
  1414  // ResolveServer attempts to find a matching Identifier for the input string
  1415  func (s *ScalewayAPI) ResolveServer(needle string) (ScalewayResolverResults, error) {
  1416  	servers, err := s.Cache.LookUpServers(needle, true)
  1417  	if err != nil {
  1418  		return servers, err
  1419  	}
  1420  	if len(servers) == 0 {
  1421  		if _, err = s.GetServers(true, 0); err != nil {
  1422  			return nil, err
  1423  		}
  1424  		servers, err = s.Cache.LookUpServers(needle, true)
  1425  	}
  1426  	return servers, err
  1427  }
  1428  
  1429  // ResolveVolume attempts to find a matching Identifier for the input string
  1430  func (s *ScalewayAPI) ResolveVolume(needle string) (ScalewayResolverResults, error) {
  1431  	volumes, err := s.Cache.LookUpVolumes(needle, true)
  1432  	if err != nil {
  1433  		return volumes, err
  1434  	}
  1435  	if len(volumes) == 0 {
  1436  		if _, err = s.GetVolumes(); err != nil {
  1437  			return nil, err
  1438  		}
  1439  		volumes, err = s.Cache.LookUpVolumes(needle, true)
  1440  	}
  1441  	return volumes, err
  1442  }
  1443  
  1444  // ResolveSnapshot attempts to find a matching Identifier for the input string
  1445  func (s *ScalewayAPI) ResolveSnapshot(needle string) (ScalewayResolverResults, error) {
  1446  	snapshots, err := s.Cache.LookUpSnapshots(needle, true)
  1447  	if err != nil {
  1448  		return snapshots, err
  1449  	}
  1450  	if len(snapshots) == 0 {
  1451  		if _, err = s.GetSnapshots(); err != nil {
  1452  			return nil, err
  1453  		}
  1454  		snapshots, err = s.Cache.LookUpSnapshots(needle, true)
  1455  	}
  1456  	return snapshots, err
  1457  }
  1458  
  1459  // ResolveImage attempts to find a matching Identifier for the input string
  1460  func (s *ScalewayAPI) ResolveImage(needle string) (ScalewayResolverResults, error) {
  1461  	images, err := s.Cache.LookUpImages(needle, true)
  1462  	if err != nil {
  1463  		return images, err
  1464  	}
  1465  	if len(images) == 0 {
  1466  		if _, err = s.GetImages(); err != nil {
  1467  			return nil, err
  1468  		}
  1469  		images, err = s.Cache.LookUpImages(needle, true)
  1470  	}
  1471  	return images, err
  1472  }
  1473  
  1474  // ResolveBootscript attempts to find a matching Identifier for the input string
  1475  func (s *ScalewayAPI) ResolveBootscript(needle string) (ScalewayResolverResults, error) {
  1476  	bootscripts, err := s.Cache.LookUpBootscripts(needle, true)
  1477  	if err != nil {
  1478  		return bootscripts, err
  1479  	}
  1480  	if len(bootscripts) == 0 {
  1481  		if _, err = s.GetBootscripts(); err != nil {
  1482  			return nil, err
  1483  		}
  1484  		bootscripts, err = s.Cache.LookUpBootscripts(needle, true)
  1485  	}
  1486  	return bootscripts, err
  1487  }
  1488  
  1489  // GetImages gets the list of images from the ScalewayAPI
  1490  func (s *ScalewayAPI) GetImages() (*[]MarketImage, error) {
  1491  	images, err := s.GetMarketPlaceImages("")
  1492  	if err != nil {
  1493  		return nil, err
  1494  	}
  1495  	s.Cache.ClearImages()
  1496  	for i, image := range images.Images {
  1497  		if image.CurrentPublicVersion != "" {
  1498  			for _, version := range image.Versions {
  1499  				if version.ID == image.CurrentPublicVersion {
  1500  					for _, localImage := range version.LocalImages {
  1501  						images.Images[i].Public = true
  1502  						s.Cache.InsertImage(localImage.ID, localImage.Zone, localImage.Arch, image.Organization.ID, image.Name, image.CurrentPublicVersion)
  1503  					}
  1504  				}
  1505  			}
  1506  		}
  1507  	}
  1508  	values := url.Values{}
  1509  	values.Set("organization", s.Organization)
  1510  	resp, err := s.GetResponsePaginate(s.computeAPI, "images", values)
  1511  	if resp != nil {
  1512  		defer resp.Body.Close()
  1513  	}
  1514  	if err != nil {
  1515  		return nil, err
  1516  	}
  1517  	body, err := s.handleHTTPError([]int{http.StatusOK}, resp)
  1518  	if err != nil {
  1519  		return nil, err
  1520  	}
  1521  	var OrgaImages ScalewayImages
  1522  
  1523  	if err = json.Unmarshal(body, &OrgaImages); err != nil {
  1524  		return nil, err
  1525  	}
  1526  
  1527  	for _, orgaImage := range OrgaImages.Images {
  1528  		images.Images = append(images.Images, MarketImage{
  1529  			Categories:           []string{"MyImages"},
  1530  			CreationDate:         orgaImage.CreationDate,
  1531  			CurrentPublicVersion: orgaImage.Identifier,
  1532  			ModificationDate:     orgaImage.ModificationDate,
  1533  			Name:                 orgaImage.Name,
  1534  			Public:               false,
  1535  			MarketVersions: MarketVersions{
  1536  				Versions: []MarketVersionDefinition{
  1537  					{
  1538  						CreationDate:     orgaImage.CreationDate,
  1539  						ID:               orgaImage.Identifier,
  1540  						ModificationDate: orgaImage.ModificationDate,
  1541  						MarketLocalImages: MarketLocalImages{
  1542  							LocalImages: []MarketLocalImageDefinition{
  1543  								{
  1544  									Arch: orgaImage.Arch,
  1545  									ID:   orgaImage.Identifier,
  1546  									// TODO: fecth images from ams1 and par1
  1547  									Zone: s.Region,
  1548  								},
  1549  							},
  1550  						},
  1551  					},
  1552  				},
  1553  			},
  1554  		})
  1555  		s.Cache.InsertImage(orgaImage.Identifier, s.Region, orgaImage.Arch, orgaImage.Organization, orgaImage.Name, "")
  1556  	}
  1557  	return &images.Images, nil
  1558  }
  1559  
  1560  // GetImage gets an image from the ScalewayAPI
  1561  func (s *ScalewayAPI) GetImage(imageID string) (*ScalewayImage, error) {
  1562  	resp, err := s.GetResponsePaginate(s.computeAPI, "images/"+imageID, url.Values{})
  1563  	if resp != nil {
  1564  		defer resp.Body.Close()
  1565  	}
  1566  	if err != nil {
  1567  		return nil, err
  1568  	}
  1569  
  1570  	body, err := s.handleHTTPError([]int{http.StatusOK}, resp)
  1571  	if err != nil {
  1572  		return nil, err
  1573  	}
  1574  	var oneImage ScalewayOneImage
  1575  
  1576  	if err = json.Unmarshal(body, &oneImage); err != nil {
  1577  		return nil, err
  1578  	}
  1579  	// FIXME owner, title
  1580  	s.Cache.InsertImage(oneImage.Image.Identifier, s.Region, oneImage.Image.Arch, oneImage.Image.Organization, oneImage.Image.Name, "")
  1581  	return &oneImage.Image, nil
  1582  }
  1583  
  1584  // DeleteImage deletes a image
  1585  func (s *ScalewayAPI) DeleteImage(imageID string) error {
  1586  	defer s.Cache.RemoveImage(imageID)
  1587  	resp, err := s.DeleteResponse(s.computeAPI, fmt.Sprintf("images/%s", imageID))
  1588  	if resp != nil {
  1589  		defer resp.Body.Close()
  1590  	}
  1591  	if err != nil {
  1592  		return err
  1593  	}
  1594  
  1595  	if _, err := s.handleHTTPError([]int{http.StatusNoContent}, resp); err != nil {
  1596  		return err
  1597  	}
  1598  	return nil
  1599  }
  1600  
  1601  // DeleteSnapshot deletes a snapshot
  1602  func (s *ScalewayAPI) DeleteSnapshot(snapshotID string) error {
  1603  	defer s.Cache.RemoveSnapshot(snapshotID)
  1604  	resp, err := s.DeleteResponse(s.computeAPI, fmt.Sprintf("snapshots/%s", snapshotID))
  1605  	if resp != nil {
  1606  		defer resp.Body.Close()
  1607  	}
  1608  	if err != nil {
  1609  		return err
  1610  	}
  1611  
  1612  	if _, err := s.handleHTTPError([]int{http.StatusNoContent}, resp); err != nil {
  1613  		return err
  1614  	}
  1615  	return nil
  1616  }
  1617  
  1618  // DeleteVolume deletes a volume
  1619  func (s *ScalewayAPI) DeleteVolume(volumeID string) error {
  1620  	defer s.Cache.RemoveVolume(volumeID)
  1621  	resp, err := s.DeleteResponse(s.computeAPI, fmt.Sprintf("volumes/%s", volumeID))
  1622  	if resp != nil {
  1623  		defer resp.Body.Close()
  1624  	}
  1625  	if err != nil {
  1626  		return err
  1627  	}
  1628  
  1629  	if _, err := s.handleHTTPError([]int{http.StatusNoContent}, resp); err != nil {
  1630  		return err
  1631  	}
  1632  	return nil
  1633  }
  1634  
  1635  // GetSnapshots gets the list of snapshots from the ScalewayAPI
  1636  func (s *ScalewayAPI) GetSnapshots() (*[]ScalewaySnapshot, error) {
  1637  	query := url.Values{}
  1638  	s.Cache.ClearSnapshots()
  1639  
  1640  	resp, err := s.GetResponsePaginate(s.computeAPI, "snapshots", query)
  1641  	if resp != nil {
  1642  		defer resp.Body.Close()
  1643  	}
  1644  	if err != nil {
  1645  		return nil, err
  1646  	}
  1647  
  1648  	body, err := s.handleHTTPError([]int{http.StatusOK}, resp)
  1649  	if err != nil {
  1650  		return nil, err
  1651  	}
  1652  	var snapshots ScalewaySnapshots
  1653  
  1654  	if err = json.Unmarshal(body, &snapshots); err != nil {
  1655  		return nil, err
  1656  	}
  1657  	for _, snapshot := range snapshots.Snapshots {
  1658  		// FIXME region, arch, owner, title
  1659  		s.Cache.InsertSnapshot(snapshot.Identifier, "", "", snapshot.Organization, snapshot.Name)
  1660  	}
  1661  	return &snapshots.Snapshots, nil
  1662  }
  1663  
  1664  // GetSnapshot gets a snapshot from the ScalewayAPI
  1665  func (s *ScalewayAPI) GetSnapshot(snapshotID string) (*ScalewaySnapshot, error) {
  1666  	resp, err := s.GetResponsePaginate(s.computeAPI, "snapshots/"+snapshotID, url.Values{})
  1667  	if resp != nil {
  1668  		defer resp.Body.Close()
  1669  	}
  1670  	if err != nil {
  1671  		return nil, err
  1672  	}
  1673  
  1674  	body, err := s.handleHTTPError([]int{http.StatusOK}, resp)
  1675  	if err != nil {
  1676  		return nil, err
  1677  	}
  1678  	var oneSnapshot ScalewayOneSnapshot
  1679  
  1680  	if err = json.Unmarshal(body, &oneSnapshot); err != nil {
  1681  		return nil, err
  1682  	}
  1683  	// FIXME region, arch, owner, title
  1684  	s.Cache.InsertSnapshot(oneSnapshot.Snapshot.Identifier, "", "", oneSnapshot.Snapshot.Organization, oneSnapshot.Snapshot.Name)
  1685  	return &oneSnapshot.Snapshot, nil
  1686  }
  1687  
  1688  // GetVolumes gets the list of volumes from the ScalewayAPI
  1689  func (s *ScalewayAPI) GetVolumes() (*[]ScalewayVolume, error) {
  1690  	query := url.Values{}
  1691  	s.Cache.ClearVolumes()
  1692  
  1693  	resp, err := s.GetResponsePaginate(s.computeAPI, "volumes", query)
  1694  	if resp != nil {
  1695  		defer resp.Body.Close()
  1696  	}
  1697  	if err != nil {
  1698  		return nil, err
  1699  	}
  1700  
  1701  	body, err := s.handleHTTPError([]int{http.StatusOK}, resp)
  1702  	if err != nil {
  1703  		return nil, err
  1704  	}
  1705  
  1706  	var volumes ScalewayVolumes
  1707  
  1708  	if err = json.Unmarshal(body, &volumes); err != nil {
  1709  		return nil, err
  1710  	}
  1711  	for _, volume := range volumes.Volumes {
  1712  		// FIXME region, arch, owner, title
  1713  		s.Cache.InsertVolume(volume.Identifier, "", "", volume.Organization, volume.Name)
  1714  	}
  1715  	return &volumes.Volumes, nil
  1716  }
  1717  
  1718  // GetVolume gets a volume from the ScalewayAPI
  1719  func (s *ScalewayAPI) GetVolume(volumeID string) (*ScalewayVolume, error) {
  1720  	resp, err := s.GetResponsePaginate(s.computeAPI, "volumes/"+volumeID, url.Values{})
  1721  	if resp != nil {
  1722  		defer resp.Body.Close()
  1723  	}
  1724  	if err != nil {
  1725  		return nil, err
  1726  	}
  1727  
  1728  	body, err := s.handleHTTPError([]int{http.StatusOK}, resp)
  1729  	if err != nil {
  1730  		return nil, err
  1731  	}
  1732  	var oneVolume ScalewayOneVolume
  1733  
  1734  	if err = json.Unmarshal(body, &oneVolume); err != nil {
  1735  		return nil, err
  1736  	}
  1737  	// FIXME region, arch, owner, title
  1738  	s.Cache.InsertVolume(oneVolume.Volume.Identifier, "", "", oneVolume.Volume.Organization, oneVolume.Volume.Name)
  1739  	return &oneVolume.Volume, nil
  1740  }
  1741  
  1742  // GetBootscripts gets the list of bootscripts from the ScalewayAPI
  1743  func (s *ScalewayAPI) GetBootscripts() (*[]ScalewayBootscript, error) {
  1744  	query := url.Values{}
  1745  
  1746  	s.Cache.ClearBootscripts()
  1747  	resp, err := s.GetResponsePaginate(s.computeAPI, "bootscripts", query)
  1748  	if resp != nil {
  1749  		defer resp.Body.Close()
  1750  	}
  1751  	if err != nil {
  1752  		return nil, err
  1753  	}
  1754  
  1755  	body, err := s.handleHTTPError([]int{http.StatusOK}, resp)
  1756  	if err != nil {
  1757  		return nil, err
  1758  	}
  1759  	var bootscripts ScalewayBootscripts
  1760  
  1761  	if err = json.Unmarshal(body, &bootscripts); err != nil {
  1762  		return nil, err
  1763  	}
  1764  	for _, bootscript := range bootscripts.Bootscripts {
  1765  		// FIXME region, arch, owner, title
  1766  		s.Cache.InsertBootscript(bootscript.Identifier, "", bootscript.Arch, bootscript.Organization, bootscript.Title)
  1767  	}
  1768  	return &bootscripts.Bootscripts, nil
  1769  }
  1770  
  1771  // GetBootscript gets a bootscript from the ScalewayAPI
  1772  func (s *ScalewayAPI) GetBootscript(bootscriptID string) (*ScalewayBootscript, error) {
  1773  	resp, err := s.GetResponsePaginate(s.computeAPI, "bootscripts/"+bootscriptID, url.Values{})
  1774  	if resp != nil {
  1775  		defer resp.Body.Close()
  1776  	}
  1777  	if err != nil {
  1778  		return nil, err
  1779  	}
  1780  
  1781  	body, err := s.handleHTTPError([]int{http.StatusOK}, resp)
  1782  	if err != nil {
  1783  		return nil, err
  1784  	}
  1785  	var oneBootscript ScalewayOneBootscript
  1786  
  1787  	if err = json.Unmarshal(body, &oneBootscript); err != nil {
  1788  		return nil, err
  1789  	}
  1790  	// FIXME region, arch, owner, title
  1791  	s.Cache.InsertBootscript(oneBootscript.Bootscript.Identifier, "", oneBootscript.Bootscript.Arch, oneBootscript.Bootscript.Organization, oneBootscript.Bootscript.Title)
  1792  	return &oneBootscript.Bootscript, nil
  1793  }
  1794  
  1795  // GetUserdatas gets list of userdata for a server
  1796  func (s *ScalewayAPI) GetUserdatas(serverID string, metadata bool) (*ScalewayUserdatas, error) {
  1797  	var uri, endpoint string
  1798  
  1799  	endpoint = s.computeAPI
  1800  	if metadata {
  1801  		uri = "/user_data"
  1802  		endpoint = MetadataAPI
  1803  	} else {
  1804  		uri = fmt.Sprintf("servers/%s/user_data", serverID)
  1805  	}
  1806  
  1807  	resp, err := s.GetResponsePaginate(endpoint, uri, url.Values{})
  1808  	if resp != nil {
  1809  		defer resp.Body.Close()
  1810  	}
  1811  	if err != nil {
  1812  		return nil, err
  1813  	}
  1814  
  1815  	body, err := s.handleHTTPError([]int{http.StatusOK}, resp)
  1816  	if err != nil {
  1817  		return nil, err
  1818  	}
  1819  	var userdatas ScalewayUserdatas
  1820  
  1821  	if err = json.Unmarshal(body, &userdatas); err != nil {
  1822  		return nil, err
  1823  	}
  1824  	return &userdatas, nil
  1825  }
  1826  
  1827  func (s *ScalewayUserdata) String() string {
  1828  	return string(*s)
  1829  }
  1830  
  1831  // GetUserdata gets a specific userdata for a server
  1832  func (s *ScalewayAPI) GetUserdata(serverID, key string, metadata bool) (*ScalewayUserdata, error) {
  1833  	var uri, endpoint string
  1834  
  1835  	endpoint = s.computeAPI
  1836  	if metadata {
  1837  		uri = fmt.Sprintf("/user_data/%s", key)
  1838  		endpoint = MetadataAPI
  1839  	} else {
  1840  		uri = fmt.Sprintf("servers/%s/user_data/%s", serverID, key)
  1841  	}
  1842  
  1843  	var err error
  1844  	resp, err := s.GetResponsePaginate(endpoint, uri, url.Values{})
  1845  	if resp != nil {
  1846  		defer resp.Body.Close()
  1847  	}
  1848  	if err != nil {
  1849  		return nil, err
  1850  	}
  1851  
  1852  	if resp.StatusCode != http.StatusOK {
  1853  		return nil, fmt.Errorf("no such user_data %q (%d)", key, resp.StatusCode)
  1854  	}
  1855  	var data ScalewayUserdata
  1856  	data, err = ioutil.ReadAll(resp.Body)
  1857  	return &data, err
  1858  }
  1859  
  1860  // PatchUserdata sets a user data
  1861  func (s *ScalewayAPI) PatchUserdata(serverID, key string, value []byte, metadata bool) error {
  1862  	var resource, endpoint string
  1863  
  1864  	endpoint = s.computeAPI
  1865  	if metadata {
  1866  		resource = fmt.Sprintf("/user_data/%s", key)
  1867  		endpoint = MetadataAPI
  1868  	} else {
  1869  		resource = fmt.Sprintf("servers/%s/user_data/%s", serverID, key)
  1870  	}
  1871  
  1872  	uri := fmt.Sprintf("%s/%s", strings.TrimRight(endpoint, "/"), resource)
  1873  	payload := new(bytes.Buffer)
  1874  	payload.Write(value)
  1875  
  1876  	req, err := http.NewRequest("PATCH", uri, payload)
  1877  	if err != nil {
  1878  		return err
  1879  	}
  1880  
  1881  	req.Header.Set("X-Auth-Token", s.Token)
  1882  	req.Header.Set("Content-Type", "text/plain")
  1883  	req.Header.Set("User-Agent", s.userAgent)
  1884  
  1885  	s.LogHTTP(req)
  1886  
  1887  	resp, err := s.client.Do(req)
  1888  	if resp != nil {
  1889  		defer resp.Body.Close()
  1890  	}
  1891  	if err != nil {
  1892  		return err
  1893  	}
  1894  
  1895  	if resp.StatusCode == http.StatusNoContent {
  1896  		return nil
  1897  	}
  1898  
  1899  	return fmt.Errorf("cannot set user_data (%d)", resp.StatusCode)
  1900  }
  1901  
  1902  // DeleteUserdata deletes a server user_data
  1903  func (s *ScalewayAPI) DeleteUserdata(serverID, key string, metadata bool) error {
  1904  	var url, endpoint string
  1905  
  1906  	endpoint = s.computeAPI
  1907  	if metadata {
  1908  		url = fmt.Sprintf("/user_data/%s", key)
  1909  		endpoint = MetadataAPI
  1910  	} else {
  1911  		url = fmt.Sprintf("servers/%s/user_data/%s", serverID, key)
  1912  	}
  1913  
  1914  	resp, err := s.DeleteResponse(endpoint, url)
  1915  	if resp != nil {
  1916  		defer resp.Body.Close()
  1917  	}
  1918  	if err != nil {
  1919  		return err
  1920  	}
  1921  
  1922  	_, err = s.handleHTTPError([]int{http.StatusNoContent}, resp)
  1923  	return err
  1924  }
  1925  
  1926  // GetTasks get the list of tasks from the ScalewayAPI
  1927  func (s *ScalewayAPI) GetTasks() (*[]ScalewayTask, error) {
  1928  	query := url.Values{}
  1929  	resp, err := s.GetResponsePaginate(s.computeAPI, "tasks", query)
  1930  	if resp != nil {
  1931  		defer resp.Body.Close()
  1932  	}
  1933  	if err != nil {
  1934  		return nil, err
  1935  	}
  1936  
  1937  	body, err := s.handleHTTPError([]int{http.StatusOK}, resp)
  1938  	if err != nil {
  1939  		return nil, err
  1940  	}
  1941  	var tasks ScalewayTasks
  1942  
  1943  	if err = json.Unmarshal(body, &tasks); err != nil {
  1944  		return nil, err
  1945  	}
  1946  	return &tasks.Tasks, nil
  1947  }
  1948  
  1949  // CheckCredentials performs a dummy check to ensure we can contact the API
  1950  func (s *ScalewayAPI) CheckCredentials() error {
  1951  	query := url.Values{}
  1952  
  1953  	resp, err := s.GetResponsePaginate(AccountAPI, "tokens", query)
  1954  	if resp != nil {
  1955  		defer resp.Body.Close()
  1956  	}
  1957  	if err != nil {
  1958  		return err
  1959  	}
  1960  	body, err := s.handleHTTPError([]int{http.StatusOK}, resp)
  1961  	if err != nil {
  1962  		return err
  1963  	}
  1964  	found := false
  1965  	var tokens ScalewayGetTokens
  1966  
  1967  	if err = json.Unmarshal(body, &tokens); err != nil {
  1968  		return err
  1969  	}
  1970  	for _, token := range tokens.Tokens {
  1971  		if token.ID == s.Token {
  1972  			found = true
  1973  			break
  1974  		}
  1975  	}
  1976  	if !found {
  1977  		return fmt.Errorf("Invalid token %v", s.Token)
  1978  	}
  1979  	return nil
  1980  }
  1981  
  1982  // GetUserID returns the userID
  1983  func (s *ScalewayAPI) GetUserID() (string, error) {
  1984  	resp, err := s.GetResponsePaginate(AccountAPI, fmt.Sprintf("tokens/%s", s.Token), url.Values{})
  1985  	if resp != nil {
  1986  		defer resp.Body.Close()
  1987  	}
  1988  	if err != nil {
  1989  		return "", err
  1990  	}
  1991  
  1992  	body, err := s.handleHTTPError([]int{http.StatusOK}, resp)
  1993  	if err != nil {
  1994  		return "", err
  1995  	}
  1996  	var token ScalewayTokensDefinition
  1997  
  1998  	if err = json.Unmarshal(body, &token); err != nil {
  1999  		return "", err
  2000  	}
  2001  	return token.Token.UserID, nil
  2002  }
  2003  
  2004  // GetOrganization returns Organization
  2005  func (s *ScalewayAPI) GetOrganization() (*ScalewayOrganizationsDefinition, error) {
  2006  	resp, err := s.GetResponsePaginate(AccountAPI, "organizations", url.Values{})
  2007  	if resp != nil {
  2008  		defer resp.Body.Close()
  2009  	}
  2010  	if err != nil {
  2011  		return nil, err
  2012  	}
  2013  
  2014  	body, err := s.handleHTTPError([]int{http.StatusOK}, resp)
  2015  	if err != nil {
  2016  		return nil, err
  2017  	}
  2018  	var data ScalewayOrganizationsDefinition
  2019  
  2020  	if err = json.Unmarshal(body, &data); err != nil {
  2021  		return nil, err
  2022  	}
  2023  	return &data, nil
  2024  }
  2025  
  2026  // GetUser returns the user
  2027  func (s *ScalewayAPI) GetUser() (*ScalewayUserDefinition, error) {
  2028  	userID, err := s.GetUserID()
  2029  	if err != nil {
  2030  		return nil, err
  2031  	}
  2032  	resp, err := s.GetResponsePaginate(AccountAPI, fmt.Sprintf("users/%s", userID), url.Values{})
  2033  	if resp != nil {
  2034  		defer resp.Body.Close()
  2035  	}
  2036  	if err != nil {
  2037  		return nil, err
  2038  	}
  2039  
  2040  	body, err := s.handleHTTPError([]int{http.StatusOK}, resp)
  2041  	if err != nil {
  2042  		return nil, err
  2043  	}
  2044  	var user ScalewayUsersDefinition
  2045  
  2046  	if err = json.Unmarshal(body, &user); err != nil {
  2047  		return nil, err
  2048  	}
  2049  	return &user.User, nil
  2050  }
  2051  
  2052  // GetPermissions returns the permissions
  2053  func (s *ScalewayAPI) GetPermissions() (*ScalewayPermissionDefinition, error) {
  2054  	resp, err := s.GetResponsePaginate(AccountAPI, fmt.Sprintf("tokens/%s/permissions", s.Token), url.Values{})
  2055  	if resp != nil {
  2056  		defer resp.Body.Close()
  2057  	}
  2058  	if err != nil {
  2059  		return nil, err
  2060  	}
  2061  
  2062  	body, err := s.handleHTTPError([]int{http.StatusOK}, resp)
  2063  	if err != nil {
  2064  		return nil, err
  2065  	}
  2066  	var permissions ScalewayPermissionDefinition
  2067  
  2068  	if err = json.Unmarshal(body, &permissions); err != nil {
  2069  		return nil, err
  2070  	}
  2071  	return &permissions, nil
  2072  }
  2073  
  2074  // GetDashboard returns the dashboard
  2075  func (s *ScalewayAPI) GetDashboard() (*ScalewayDashboard, error) {
  2076  	resp, err := s.GetResponsePaginate(s.computeAPI, "dashboard", url.Values{})
  2077  	if resp != nil {
  2078  		defer resp.Body.Close()
  2079  	}
  2080  	if err != nil {
  2081  		return nil, err
  2082  	}
  2083  
  2084  	body, err := s.handleHTTPError([]int{http.StatusOK}, resp)
  2085  	if err != nil {
  2086  		return nil, err
  2087  	}
  2088  	var dashboard ScalewayDashboardResp
  2089  
  2090  	if err = json.Unmarshal(body, &dashboard); err != nil {
  2091  		return nil, err
  2092  	}
  2093  	return &dashboard.Dashboard, nil
  2094  }
  2095  
  2096  // GetServerID returns exactly one server matching
  2097  func (s *ScalewayAPI) GetServerID(needle string) (string, error) {
  2098  	// Parses optional type prefix, i.e: "server:name" -> "name"
  2099  	_, needle = parseNeedle(needle)
  2100  
  2101  	servers, err := s.ResolveServer(needle)
  2102  	if err != nil {
  2103  		return "", fmt.Errorf("Unable to resolve server %s: %s", needle, err)
  2104  	}
  2105  	if len(servers) == 1 {
  2106  		return servers[0].Identifier, nil
  2107  	}
  2108  	if len(servers) == 0 {
  2109  		return "", fmt.Errorf("No such server: %s", needle)
  2110  	}
  2111  	return "", showResolverResults(needle, servers)
  2112  }
  2113  
  2114  func showResolverResults(needle string, results ScalewayResolverResults) error {
  2115  	w := tabwriter.NewWriter(os.Stderr, 20, 1, 3, ' ', 0)
  2116  	defer w.Flush()
  2117  	sort.Sort(results)
  2118  	fmt.Fprintf(w, "  IMAGEID\tFROM\tNAME\tZONE\tARCH\n")
  2119  	for _, result := range results {
  2120  		if result.Arch == "" {
  2121  			result.Arch = "n/a"
  2122  		}
  2123  		fmt.Fprintf(w, "- %s\t%s\t%s\t%s\t%s\n", result.TruncIdentifier(), result.CodeName(), result.Name, result.Region, result.Arch)
  2124  	}
  2125  	return fmt.Errorf("Too many candidates for %s (%d)", needle, len(results))
  2126  }
  2127  
  2128  // GetVolumeID returns exactly one volume matching
  2129  func (s *ScalewayAPI) GetVolumeID(needle string) (string, error) {
  2130  	// Parses optional type prefix, i.e: "volume:name" -> "name"
  2131  	_, needle = parseNeedle(needle)
  2132  
  2133  	volumes, err := s.ResolveVolume(needle)
  2134  	if err != nil {
  2135  		return "", fmt.Errorf("Unable to resolve volume %s: %s", needle, err)
  2136  	}
  2137  	if len(volumes) == 1 {
  2138  		return volumes[0].Identifier, nil
  2139  	}
  2140  	if len(volumes) == 0 {
  2141  		return "", fmt.Errorf("No such volume: %s", needle)
  2142  	}
  2143  	return "", showResolverResults(needle, volumes)
  2144  }
  2145  
  2146  // GetSnapshotID returns exactly one snapshot matching
  2147  func (s *ScalewayAPI) GetSnapshotID(needle string) (string, error) {
  2148  	// Parses optional type prefix, i.e: "snapshot:name" -> "name"
  2149  	_, needle = parseNeedle(needle)
  2150  
  2151  	snapshots, err := s.ResolveSnapshot(needle)
  2152  	if err != nil {
  2153  		return "", fmt.Errorf("Unable to resolve snapshot %s: %s", needle, err)
  2154  	}
  2155  	if len(snapshots) == 1 {
  2156  		return snapshots[0].Identifier, nil
  2157  	}
  2158  	if len(snapshots) == 0 {
  2159  		return "", fmt.Errorf("No such snapshot: %s", needle)
  2160  	}
  2161  	return "", showResolverResults(needle, snapshots)
  2162  }
  2163  
  2164  // FilterImagesByArch removes entry that doesn't match with architecture
  2165  func FilterImagesByArch(res ScalewayResolverResults, arch string) (ret ScalewayResolverResults) {
  2166  	if arch == "*" {
  2167  		return res
  2168  	}
  2169  	for _, result := range res {
  2170  		if result.Arch == arch {
  2171  			ret = append(ret, result)
  2172  		}
  2173  	}
  2174  	return
  2175  }
  2176  
  2177  // FilterImagesByRegion removes entry that doesn't match with region
  2178  func FilterImagesByRegion(res ScalewayResolverResults, region string) (ret ScalewayResolverResults) {
  2179  	if region == "*" {
  2180  		return res
  2181  	}
  2182  	for _, result := range res {
  2183  		if result.Region == region {
  2184  			ret = append(ret, result)
  2185  		}
  2186  	}
  2187  	return
  2188  }
  2189  
  2190  // GetImageID returns exactly one image matching
  2191  func (s *ScalewayAPI) GetImageID(needle, arch string) (*ScalewayImageIdentifier, error) {
  2192  	// Parses optional type prefix, i.e: "image:name" -> "name"
  2193  	_, needle = parseNeedle(needle)
  2194  
  2195  	images, err := s.ResolveImage(needle)
  2196  	if err != nil {
  2197  		return nil, fmt.Errorf("Unable to resolve image %s: %s", needle, err)
  2198  	}
  2199  	images = FilterImagesByArch(images, arch)
  2200  	images = FilterImagesByRegion(images, s.Region)
  2201  	if len(images) == 1 {
  2202  		return &ScalewayImageIdentifier{
  2203  			Identifier: images[0].Identifier,
  2204  			Arch:       images[0].Arch,
  2205  			// FIXME region, owner hardcoded
  2206  			Region: images[0].Region,
  2207  			Owner:  "",
  2208  		}, nil
  2209  	}
  2210  	if len(images) == 0 {
  2211  		return nil, fmt.Errorf("No such image (zone %s, arch %s) : %s", s.Region, arch, needle)
  2212  	}
  2213  	return nil, showResolverResults(needle, images)
  2214  }
  2215  
  2216  // GetSecurityGroups returns a ScalewaySecurityGroups
  2217  func (s *ScalewayAPI) GetSecurityGroups() (*ScalewayGetSecurityGroups, error) {
  2218  	resp, err := s.GetResponsePaginate(s.computeAPI, "security_groups", url.Values{})
  2219  	if resp != nil {
  2220  		defer resp.Body.Close()
  2221  	}
  2222  	if err != nil {
  2223  		return nil, err
  2224  	}
  2225  
  2226  	body, err := s.handleHTTPError([]int{http.StatusOK}, resp)
  2227  	if err != nil {
  2228  		return nil, err
  2229  	}
  2230  	var securityGroups ScalewayGetSecurityGroups
  2231  
  2232  	if err = json.Unmarshal(body, &securityGroups); err != nil {
  2233  		return nil, err
  2234  	}
  2235  	return &securityGroups, nil
  2236  }
  2237  
  2238  // GetSecurityGroupRules returns a ScalewaySecurityGroupRules
  2239  func (s *ScalewayAPI) GetSecurityGroupRules(groupID string) (*ScalewayGetSecurityGroupRules, error) {
  2240  	resp, err := s.GetResponsePaginate(s.computeAPI, fmt.Sprintf("security_groups/%s/rules", groupID), url.Values{})
  2241  	if resp != nil {
  2242  		defer resp.Body.Close()
  2243  	}
  2244  	if err != nil {
  2245  		return nil, err
  2246  	}
  2247  
  2248  	body, err := s.handleHTTPError([]int{http.StatusOK}, resp)
  2249  	if err != nil {
  2250  		return nil, err
  2251  	}
  2252  	var securityGroupRules ScalewayGetSecurityGroupRules
  2253  
  2254  	if err = json.Unmarshal(body, &securityGroupRules); err != nil {
  2255  		return nil, err
  2256  	}
  2257  	return &securityGroupRules, nil
  2258  }
  2259  
  2260  // GetASecurityGroupRule returns a ScalewaySecurityGroupRule
  2261  func (s *ScalewayAPI) GetASecurityGroupRule(groupID string, rulesID string) (*ScalewayGetSecurityGroupRule, error) {
  2262  	resp, err := s.GetResponsePaginate(s.computeAPI, fmt.Sprintf("security_groups/%s/rules/%s", groupID, rulesID), url.Values{})
  2263  	if resp != nil {
  2264  		defer resp.Body.Close()
  2265  	}
  2266  	if err != nil {
  2267  		return nil, err
  2268  	}
  2269  
  2270  	body, err := s.handleHTTPError([]int{http.StatusOK}, resp)
  2271  	if err != nil {
  2272  		return nil, err
  2273  	}
  2274  	var securityGroupRules ScalewayGetSecurityGroupRule
  2275  
  2276  	if err = json.Unmarshal(body, &securityGroupRules); err != nil {
  2277  		return nil, err
  2278  	}
  2279  	return &securityGroupRules, nil
  2280  }
  2281  
  2282  // GetASecurityGroup returns a ScalewaySecurityGroup
  2283  func (s *ScalewayAPI) GetASecurityGroup(groupsID string) (*ScalewayGetSecurityGroup, error) {
  2284  	resp, err := s.GetResponsePaginate(s.computeAPI, fmt.Sprintf("security_groups/%s", groupsID), url.Values{})
  2285  	if resp != nil {
  2286  		defer resp.Body.Close()
  2287  	}
  2288  	if err != nil {
  2289  		return nil, err
  2290  	}
  2291  
  2292  	body, err := s.handleHTTPError([]int{http.StatusOK}, resp)
  2293  	if err != nil {
  2294  		return nil, err
  2295  	}
  2296  	var securityGroups ScalewayGetSecurityGroup
  2297  
  2298  	if err = json.Unmarshal(body, &securityGroups); err != nil {
  2299  		return nil, err
  2300  	}
  2301  	return &securityGroups, nil
  2302  }
  2303  
  2304  // PostSecurityGroup posts a group on a server
  2305  func (s *ScalewayAPI) PostSecurityGroup(group ScalewayNewSecurityGroup) error {
  2306  	resp, err := s.PostResponse(s.computeAPI, "security_groups", group)
  2307  	if resp != nil {
  2308  		defer resp.Body.Close()
  2309  	}
  2310  	if err != nil {
  2311  		return err
  2312  	}
  2313  
  2314  	_, err = s.handleHTTPError([]int{http.StatusCreated}, resp)
  2315  	return err
  2316  }
  2317  
  2318  // PostSecurityGroupRule posts a rule on a server
  2319  func (s *ScalewayAPI) PostSecurityGroupRule(SecurityGroupID string, rules ScalewayNewSecurityGroupRule) error {
  2320  	resp, err := s.PostResponse(s.computeAPI, fmt.Sprintf("security_groups/%s/rules", SecurityGroupID), rules)
  2321  	if resp != nil {
  2322  		defer resp.Body.Close()
  2323  	}
  2324  	if err != nil {
  2325  		return err
  2326  	}
  2327  
  2328  	_, err = s.handleHTTPError([]int{http.StatusCreated}, resp)
  2329  	return err
  2330  }
  2331  
  2332  // DeleteSecurityGroup deletes a SecurityGroup
  2333  func (s *ScalewayAPI) DeleteSecurityGroup(securityGroupID string) error {
  2334  	resp, err := s.DeleteResponse(s.computeAPI, fmt.Sprintf("security_groups/%s", securityGroupID))
  2335  	if resp != nil {
  2336  		defer resp.Body.Close()
  2337  	}
  2338  	if err != nil {
  2339  		return err
  2340  	}
  2341  
  2342  	_, err = s.handleHTTPError([]int{http.StatusNoContent}, resp)
  2343  	return err
  2344  }
  2345  
  2346  // PutSecurityGroup updates a SecurityGroup
  2347  func (s *ScalewayAPI) PutSecurityGroup(group ScalewayUpdateSecurityGroup, securityGroupID string) error {
  2348  	resp, err := s.PutResponse(s.computeAPI, fmt.Sprintf("security_groups/%s", securityGroupID), group)
  2349  	if resp != nil {
  2350  		defer resp.Body.Close()
  2351  	}
  2352  	if err != nil {
  2353  		return err
  2354  	}
  2355  
  2356  	_, err = s.handleHTTPError([]int{http.StatusOK}, resp)
  2357  	return err
  2358  }
  2359  
  2360  // PutSecurityGroupRule updates a SecurityGroupRule
  2361  func (s *ScalewayAPI) PutSecurityGroupRule(rules ScalewayNewSecurityGroupRule, securityGroupID, RuleID string) error {
  2362  	resp, err := s.PutResponse(s.computeAPI, fmt.Sprintf("security_groups/%s/rules/%s", securityGroupID, RuleID), rules)
  2363  	if resp != nil {
  2364  		defer resp.Body.Close()
  2365  	}
  2366  	if err != nil {
  2367  		return err
  2368  	}
  2369  
  2370  	_, err = s.handleHTTPError([]int{http.StatusOK}, resp)
  2371  	return err
  2372  }
  2373  
  2374  // DeleteSecurityGroupRule deletes a SecurityGroupRule
  2375  func (s *ScalewayAPI) DeleteSecurityGroupRule(SecurityGroupID, RuleID string) error {
  2376  	resp, err := s.DeleteResponse(s.computeAPI, fmt.Sprintf("security_groups/%s/rules/%s", SecurityGroupID, RuleID))
  2377  	if resp != nil {
  2378  		defer resp.Body.Close()
  2379  	}
  2380  	if err != nil {
  2381  		return err
  2382  	}
  2383  
  2384  	_, err = s.handleHTTPError([]int{http.StatusNoContent}, resp)
  2385  	return err
  2386  }
  2387  
  2388  // GetContainers returns a ScalewayGetContainers
  2389  func (s *ScalewayAPI) GetContainers() (*ScalewayGetContainers, error) {
  2390  	resp, err := s.GetResponsePaginate(s.computeAPI, "containers", url.Values{})
  2391  	if resp != nil {
  2392  		defer resp.Body.Close()
  2393  	}
  2394  	if err != nil {
  2395  		return nil, err
  2396  	}
  2397  
  2398  	body, err := s.handleHTTPError([]int{http.StatusOK}, resp)
  2399  	if err != nil {
  2400  		return nil, err
  2401  	}
  2402  	var containers ScalewayGetContainers
  2403  
  2404  	if err = json.Unmarshal(body, &containers); err != nil {
  2405  		return nil, err
  2406  	}
  2407  	return &containers, nil
  2408  }
  2409  
  2410  // GetContainerDatas returns a ScalewayGetContainerDatas
  2411  func (s *ScalewayAPI) GetContainerDatas(container string) (*ScalewayGetContainerDatas, error) {
  2412  	resp, err := s.GetResponsePaginate(s.computeAPI, fmt.Sprintf("containers/%s", container), url.Values{})
  2413  	if resp != nil {
  2414  		defer resp.Body.Close()
  2415  	}
  2416  	if err != nil {
  2417  		return nil, err
  2418  	}
  2419  
  2420  	body, err := s.handleHTTPError([]int{http.StatusOK}, resp)
  2421  	if err != nil {
  2422  		return nil, err
  2423  	}
  2424  	var datas ScalewayGetContainerDatas
  2425  
  2426  	if err = json.Unmarshal(body, &datas); err != nil {
  2427  		return nil, err
  2428  	}
  2429  	return &datas, nil
  2430  }
  2431  
  2432  // GetIPS returns a ScalewayGetIPS
  2433  func (s *ScalewayAPI) GetIPS() (*ScalewayGetIPS, error) {
  2434  	resp, err := s.GetResponsePaginate(s.computeAPI, "ips", url.Values{})
  2435  	if resp != nil {
  2436  		defer resp.Body.Close()
  2437  	}
  2438  	if err != nil {
  2439  		return nil, err
  2440  	}
  2441  
  2442  	body, err := s.handleHTTPError([]int{http.StatusOK}, resp)
  2443  	if err != nil {
  2444  		return nil, err
  2445  	}
  2446  	var ips ScalewayGetIPS
  2447  
  2448  	if err = json.Unmarshal(body, &ips); err != nil {
  2449  		return nil, err
  2450  	}
  2451  	return &ips, nil
  2452  }
  2453  
  2454  // NewIP returns a new IP
  2455  func (s *ScalewayAPI) NewIP() (*ScalewayGetIP, error) {
  2456  	var orga struct {
  2457  		Organization string `json:"organization"`
  2458  	}
  2459  	orga.Organization = s.Organization
  2460  	resp, err := s.PostResponse(s.computeAPI, "ips", orga)
  2461  	if resp != nil {
  2462  		defer resp.Body.Close()
  2463  	}
  2464  	if err != nil {
  2465  		return nil, err
  2466  	}
  2467  
  2468  	body, err := s.handleHTTPError([]int{http.StatusCreated}, resp)
  2469  	if err != nil {
  2470  		return nil, err
  2471  	}
  2472  	var ip ScalewayGetIP
  2473  
  2474  	if err = json.Unmarshal(body, &ip); err != nil {
  2475  		return nil, err
  2476  	}
  2477  	return &ip, nil
  2478  }
  2479  
  2480  // AttachIP attachs an IP to a server
  2481  func (s *ScalewayAPI) AttachIP(ipID, serverID string) error {
  2482  	var update struct {
  2483  		Address      string  `json:"address"`
  2484  		ID           string  `json:"id"`
  2485  		Reverse      *string `json:"reverse"`
  2486  		Organization string  `json:"organization"`
  2487  		Server       string  `json:"server"`
  2488  	}
  2489  
  2490  	ip, err := s.GetIP(ipID)
  2491  	if err != nil {
  2492  		return err
  2493  	}
  2494  	update.Address = ip.IP.Address
  2495  	update.ID = ip.IP.ID
  2496  	update.Organization = ip.IP.Organization
  2497  	update.Server = serverID
  2498  	resp, err := s.PutResponse(s.computeAPI, fmt.Sprintf("ips/%s", ipID), update)
  2499  	if err != nil {
  2500  		return err
  2501  	}
  2502  	_, err = s.handleHTTPError([]int{http.StatusOK}, resp)
  2503  	return err
  2504  }
  2505  
  2506  // DetachIP detaches an IP from a server
  2507  func (s *ScalewayAPI) DetachIP(ipID string) error {
  2508  	ip, err := s.GetIP(ipID)
  2509  	if err != nil {
  2510  		return err
  2511  	}
  2512  	ip.IP.Server = nil
  2513  	resp, err := s.PutResponse(s.computeAPI, fmt.Sprintf("ips/%s", ipID), ip.IP)
  2514  	if resp != nil {
  2515  		defer resp.Body.Close()
  2516  	}
  2517  	if err != nil {
  2518  		return err
  2519  	}
  2520  	_, err = s.handleHTTPError([]int{http.StatusOK}, resp)
  2521  	return err
  2522  }
  2523  
  2524  // DeleteIP deletes an IP
  2525  func (s *ScalewayAPI) DeleteIP(ipID string) error {
  2526  	resp, err := s.DeleteResponse(s.computeAPI, fmt.Sprintf("ips/%s", ipID))
  2527  	if resp != nil {
  2528  		defer resp.Body.Close()
  2529  	}
  2530  	if err != nil {
  2531  		return err
  2532  	}
  2533  	_, err = s.handleHTTPError([]int{http.StatusNoContent}, resp)
  2534  	return err
  2535  }
  2536  
  2537  // GetIP returns a ScalewayGetIP
  2538  func (s *ScalewayAPI) GetIP(ipID string) (*ScalewayGetIP, error) {
  2539  	resp, err := s.GetResponsePaginate(s.computeAPI, fmt.Sprintf("ips/%s", ipID), url.Values{})
  2540  	if resp != nil {
  2541  		defer resp.Body.Close()
  2542  	}
  2543  	if err != nil {
  2544  		return nil, err
  2545  	}
  2546  
  2547  	body, err := s.handleHTTPError([]int{http.StatusOK}, resp)
  2548  	if err != nil {
  2549  		return nil, err
  2550  	}
  2551  	var ip ScalewayGetIP
  2552  
  2553  	if err = json.Unmarshal(body, &ip); err != nil {
  2554  		return nil, err
  2555  	}
  2556  	return &ip, nil
  2557  }
  2558  
  2559  // GetQuotas returns a ScalewayGetQuotas
  2560  func (s *ScalewayAPI) GetQuotas() (*ScalewayGetQuotas, error) {
  2561  	resp, err := s.GetResponsePaginate(AccountAPI, fmt.Sprintf("organizations/%s/quotas", s.Organization), url.Values{})
  2562  	if resp != nil {
  2563  		defer resp.Body.Close()
  2564  	}
  2565  	if err != nil {
  2566  		return nil, err
  2567  	}
  2568  
  2569  	body, err := s.handleHTTPError([]int{http.StatusOK}, resp)
  2570  	if err != nil {
  2571  		return nil, err
  2572  	}
  2573  	var quotas ScalewayGetQuotas
  2574  
  2575  	if err = json.Unmarshal(body, &quotas); err != nil {
  2576  		return nil, err
  2577  	}
  2578  	return &quotas, nil
  2579  }
  2580  
  2581  // GetBootscriptID returns exactly one bootscript matching
  2582  func (s *ScalewayAPI) GetBootscriptID(needle, arch string) (string, error) {
  2583  	// Parses optional type prefix, i.e: "bootscript:name" -> "name"
  2584  	_, needle = parseNeedle(needle)
  2585  
  2586  	bootscripts, err := s.ResolveBootscript(needle)
  2587  	if err != nil {
  2588  		return "", fmt.Errorf("Unable to resolve bootscript %s: %s", needle, err)
  2589  	}
  2590  	bootscripts.FilterByArch(arch)
  2591  	if len(bootscripts) == 1 {
  2592  		return bootscripts[0].Identifier, nil
  2593  	}
  2594  	if len(bootscripts) == 0 {
  2595  		return "", fmt.Errorf("No such bootscript: %s", needle)
  2596  	}
  2597  	return "", showResolverResults(needle, bootscripts)
  2598  }
  2599  
  2600  func rootNetDial(network, addr string) (net.Conn, error) {
  2601  	dialer := net.Dialer{
  2602  		Timeout:   10 * time.Second,
  2603  		KeepAlive: 10 * time.Second,
  2604  	}
  2605  
  2606  	// bruteforce privileged ports
  2607  	var localAddr net.Addr
  2608  	var err error
  2609  	for port := 1; port <= 1024; port++ {
  2610  		localAddr, err = net.ResolveTCPAddr("tcp", fmt.Sprintf(":%d", port))
  2611  
  2612  		// this should never happen
  2613  		if err != nil {
  2614  			return nil, err
  2615  		}
  2616  
  2617  		dialer.LocalAddr = localAddr
  2618  
  2619  		conn, err := dialer.Dial(network, addr)
  2620  
  2621  		// if err is nil, dialer.Dial succeed, so let's go
  2622  		// else, err != nil, but we don't care
  2623  		if err == nil {
  2624  			return conn, nil
  2625  		}
  2626  	}
  2627  	// if here, all privileged ports were tried without success
  2628  	return nil, fmt.Errorf("bind: permission denied, are you root ?")
  2629  }
  2630  
  2631  // SetPassword register the password
  2632  func (s *ScalewayAPI) SetPassword(password string) {
  2633  	s.password = password
  2634  }
  2635  
  2636  // GetMarketPlaceImages returns images from marketplace
  2637  func (s *ScalewayAPI) GetMarketPlaceImages(uuidImage string) (*MarketImages, error) {
  2638  	resp, err := s.GetResponsePaginate(MarketplaceAPI, fmt.Sprintf("images/%s", uuidImage), url.Values{})
  2639  	if resp != nil {
  2640  		defer resp.Body.Close()
  2641  	}
  2642  	if err != nil {
  2643  		return nil, err
  2644  	}
  2645  
  2646  	body, err := s.handleHTTPError([]int{http.StatusOK}, resp)
  2647  	if err != nil {
  2648  		return nil, err
  2649  	}
  2650  	var ret MarketImages
  2651  
  2652  	if uuidImage != "" {
  2653  		ret.Images = make([]MarketImage, 1)
  2654  
  2655  		var img MarketImage
  2656  
  2657  		if err = json.Unmarshal(body, &img); err != nil {
  2658  			return nil, err
  2659  		}
  2660  		ret.Images[0] = img
  2661  	} else {
  2662  		if err = json.Unmarshal(body, &ret); err != nil {
  2663  			return nil, err
  2664  		}
  2665  	}
  2666  	return &ret, nil
  2667  }
  2668  
  2669  // GetMarketPlaceImageVersions returns image version
  2670  func (s *ScalewayAPI) GetMarketPlaceImageVersions(uuidImage, uuidVersion string) (*MarketVersions, error) {
  2671  	resp, err := s.GetResponsePaginate(MarketplaceAPI, fmt.Sprintf("images/%v/versions/%s", uuidImage, uuidVersion), url.Values{})
  2672  	if resp != nil {
  2673  		defer resp.Body.Close()
  2674  	}
  2675  	if err != nil {
  2676  		return nil, err
  2677  	}
  2678  
  2679  	body, err := s.handleHTTPError([]int{http.StatusOK}, resp)
  2680  	if err != nil {
  2681  		return nil, err
  2682  	}
  2683  	var ret MarketVersions
  2684  
  2685  	if uuidImage != "" {
  2686  		var version MarketVersion
  2687  		ret.Versions = make([]MarketVersionDefinition, 1)
  2688  
  2689  		if err = json.Unmarshal(body, &version); err != nil {
  2690  			return nil, err
  2691  		}
  2692  		ret.Versions[0] = version.Version
  2693  	} else {
  2694  		if err = json.Unmarshal(body, &ret); err != nil {
  2695  			return nil, err
  2696  		}
  2697  	}
  2698  	return &ret, nil
  2699  }
  2700  
  2701  // GetMarketPlaceImageCurrentVersion return the image current version
  2702  func (s *ScalewayAPI) GetMarketPlaceImageCurrentVersion(uuidImage string) (*MarketVersion, error) {
  2703  	resp, err := s.GetResponsePaginate(MarketplaceAPI, fmt.Sprintf("images/%v/versions/current", uuidImage), url.Values{})
  2704  	if resp != nil {
  2705  		defer resp.Body.Close()
  2706  	}
  2707  	if err != nil {
  2708  		return nil, err
  2709  	}
  2710  
  2711  	body, err := s.handleHTTPError([]int{http.StatusOK}, resp)
  2712  	if err != nil {
  2713  		return nil, err
  2714  	}
  2715  	var ret MarketVersion
  2716  
  2717  	if err = json.Unmarshal(body, &ret); err != nil {
  2718  		return nil, err
  2719  	}
  2720  	return &ret, nil
  2721  }
  2722  
  2723  // GetMarketPlaceLocalImages returns images from local region
  2724  func (s *ScalewayAPI) GetMarketPlaceLocalImages(uuidImage, uuidVersion, uuidLocalImage string) (*MarketLocalImages, error) {
  2725  	resp, err := s.GetResponsePaginate(MarketplaceAPI, fmt.Sprintf("images/%v/versions/%s/local_images/%s", uuidImage, uuidVersion, uuidLocalImage), url.Values{})
  2726  	if resp != nil {
  2727  		defer resp.Body.Close()
  2728  	}
  2729  	if err != nil {
  2730  		return nil, err
  2731  	}
  2732  	body, err := s.handleHTTPError([]int{http.StatusOK}, resp)
  2733  	if err != nil {
  2734  		return nil, err
  2735  	}
  2736  	var ret MarketLocalImages
  2737  	if uuidLocalImage != "" {
  2738  		var localImage MarketLocalImage
  2739  		ret.LocalImages = make([]MarketLocalImageDefinition, 1)
  2740  
  2741  		if err = json.Unmarshal(body, &localImage); err != nil {
  2742  			return nil, err
  2743  		}
  2744  		ret.LocalImages[0] = localImage.LocalImages
  2745  	} else {
  2746  		if err = json.Unmarshal(body, &ret); err != nil {
  2747  			return nil, err
  2748  		}
  2749  	}
  2750  	return &ret, nil
  2751  }
  2752  
  2753  // PostMarketPlaceImage adds new image
  2754  func (s *ScalewayAPI) PostMarketPlaceImage(images MarketImage) error {
  2755  	resp, err := s.PostResponse(MarketplaceAPI, "images/", images)
  2756  	if resp != nil {
  2757  		defer resp.Body.Close()
  2758  	}
  2759  	if err != nil {
  2760  		return err
  2761  	}
  2762  	_, err = s.handleHTTPError([]int{http.StatusAccepted}, resp)
  2763  	return err
  2764  }
  2765  
  2766  // PostMarketPlaceImageVersion adds new image version
  2767  func (s *ScalewayAPI) PostMarketPlaceImageVersion(uuidImage string, version MarketVersion) error {
  2768  	resp, err := s.PostResponse(MarketplaceAPI, fmt.Sprintf("images/%v/versions", uuidImage), version)
  2769  	if resp != nil {
  2770  		defer resp.Body.Close()
  2771  	}
  2772  	if err != nil {
  2773  		return err
  2774  	}
  2775  	_, err = s.handleHTTPError([]int{http.StatusAccepted}, resp)
  2776  	return err
  2777  }
  2778  
  2779  // PostMarketPlaceLocalImage adds new local image
  2780  func (s *ScalewayAPI) PostMarketPlaceLocalImage(uuidImage, uuidVersion, uuidLocalImage string, local MarketLocalImage) error {
  2781  	resp, err := s.PostResponse(MarketplaceAPI, fmt.Sprintf("images/%v/versions/%s/local_images/%v", uuidImage, uuidVersion, uuidLocalImage), local)
  2782  	if resp != nil {
  2783  		defer resp.Body.Close()
  2784  	}
  2785  	if err != nil {
  2786  		return err
  2787  	}
  2788  	_, err = s.handleHTTPError([]int{http.StatusAccepted}, resp)
  2789  	return err
  2790  }
  2791  
  2792  // PutMarketPlaceImage updates image
  2793  func (s *ScalewayAPI) PutMarketPlaceImage(uudiImage string, images MarketImage) error {
  2794  	resp, err := s.PutResponse(MarketplaceAPI, fmt.Sprintf("images/%v", uudiImage), images)
  2795  	if resp != nil {
  2796  		defer resp.Body.Close()
  2797  	}
  2798  	if err != nil {
  2799  		return err
  2800  	}
  2801  	_, err = s.handleHTTPError([]int{http.StatusOK}, resp)
  2802  	return err
  2803  }
  2804  
  2805  // PutMarketPlaceImageVersion updates image version
  2806  func (s *ScalewayAPI) PutMarketPlaceImageVersion(uuidImage, uuidVersion string, version MarketVersion) error {
  2807  	resp, err := s.PutResponse(MarketplaceAPI, fmt.Sprintf("images/%v/versions/%v", uuidImage, uuidVersion), version)
  2808  	if resp != nil {
  2809  		defer resp.Body.Close()
  2810  	}
  2811  	if err != nil {
  2812  		return err
  2813  	}
  2814  	_, err = s.handleHTTPError([]int{http.StatusOK}, resp)
  2815  	return err
  2816  }
  2817  
  2818  // PutMarketPlaceLocalImage updates local image
  2819  func (s *ScalewayAPI) PutMarketPlaceLocalImage(uuidImage, uuidVersion, uuidLocalImage string, local MarketLocalImage) error {
  2820  	resp, err := s.PostResponse(MarketplaceAPI, fmt.Sprintf("images/%v/versions/%s/local_images/%v", uuidImage, uuidVersion, uuidLocalImage), local)
  2821  	if resp != nil {
  2822  		defer resp.Body.Close()
  2823  	}
  2824  	if err != nil {
  2825  		return err
  2826  	}
  2827  	_, err = s.handleHTTPError([]int{http.StatusOK}, resp)
  2828  	return err
  2829  }
  2830  
  2831  // DeleteMarketPlaceImage deletes image
  2832  func (s *ScalewayAPI) DeleteMarketPlaceImage(uudImage string) error {
  2833  	resp, err := s.DeleteResponse(MarketplaceAPI, fmt.Sprintf("images/%v", uudImage))
  2834  	if resp != nil {
  2835  		defer resp.Body.Close()
  2836  	}
  2837  	if err != nil {
  2838  		return err
  2839  	}
  2840  	_, err = s.handleHTTPError([]int{http.StatusNoContent}, resp)
  2841  	return err
  2842  }
  2843  
  2844  // DeleteMarketPlaceImageVersion delete image version
  2845  func (s *ScalewayAPI) DeleteMarketPlaceImageVersion(uuidImage, uuidVersion string) error {
  2846  	resp, err := s.DeleteResponse(MarketplaceAPI, fmt.Sprintf("images/%v/versions/%v", uuidImage, uuidVersion))
  2847  	if resp != nil {
  2848  		defer resp.Body.Close()
  2849  	}
  2850  	if err != nil {
  2851  		return err
  2852  	}
  2853  	_, err = s.handleHTTPError([]int{http.StatusNoContent}, resp)
  2854  	return err
  2855  }
  2856  
  2857  // DeleteMarketPlaceLocalImage deletes local image
  2858  func (s *ScalewayAPI) DeleteMarketPlaceLocalImage(uuidImage, uuidVersion, uuidLocalImage string) error {
  2859  	resp, err := s.DeleteResponse(MarketplaceAPI, fmt.Sprintf("images/%v/versions/%s/local_images/%v", uuidImage, uuidVersion, uuidLocalImage))
  2860  	if resp != nil {
  2861  		defer resp.Body.Close()
  2862  	}
  2863  	if err != nil {
  2864  		return err
  2865  	}
  2866  	_, err = s.handleHTTPError([]int{http.StatusNoContent}, resp)
  2867  	return err
  2868  }
  2869  
  2870  // ResolveTTYUrl return an URL to get a tty
  2871  func (s *ScalewayAPI) ResolveTTYUrl() string {
  2872  	switch s.Region {
  2873  	case "par1", "":
  2874  		return "https://tty-par1.scaleway.com/v2/"
  2875  	case "ams1":
  2876  		return "https://tty-ams1.scaleway.com"
  2877  	}
  2878  	return ""
  2879  }