-
Notifications
You must be signed in to change notification settings - Fork 9.5k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Add scaleway provider this PR allows the entire scaleway stack to be managed with terraform example usage looks like this: ``` provider "scaleway" { api_key = "snap" organization = "snip" } resource "scaleway_ip" "base" { server = "${scaleway_server.base.id}" } resource "scaleway_server" "base" { name = "test" # ubuntu 14.04 image = "aecaed73-51a5-4439-a127-6d8229847145" type = "C2S" } resource "scaleway_volume" "test" { name = "test" size_in_gb = 20 type = "l_ssd" } resource "scaleway_volume_attachment" "test" { server = "${scaleway_server.base.id}" volume = "${scaleway_volume.test.id}" } resource "scaleway_security_group" "base" { name = "public" description = "public gateway" } resource "scaleway_security_group_rule" "http-ingress" { security_group = "${scaleway_security_group.base.id}" action = "accept" direction = "inbound" ip_range = "0.0.0.0/0" protocol = "TCP" port = 80 } resource "scaleway_security_group_rule" "http-egress" { security_group = "${scaleway_security_group.base.id}" action = "accept" direction = "outbound" ip_range = "0.0.0.0/0" protocol = "TCP" port = 80 } ``` Note that volume attachments require the server to be stopped, which can lead to downtimes of you attach new volumes to already used servers * Update IP read to handle 404 gracefully * Read back resource on update * Ensure IP detachment works as expected Sadly this is not part of the official scaleway api just yet * Adjust detachIP helper based on feedback from @QuentinPerez in scaleway/scaleway-cli#378 * Cleanup documentation * Rename api_key to access_key following @stack72 suggestion and rename the provider api_key for more clarity * Make tests less chatty by using custom logger
- Loading branch information
Showing
41 changed files
with
6,436 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
package main | ||
|
||
import ( | ||
"github.com/hashicorp/terraform/builtin/providers/scaleway" | ||
"github.com/hashicorp/terraform/plugin" | ||
) | ||
|
||
func main() { | ||
plugin.Serve(&plugin.ServeOpts{ | ||
ProviderFunc: scaleway.Provider, | ||
}) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
package scaleway | ||
|
||
import ( | ||
"fmt" | ||
"log" | ||
"net/http" | ||
"os" | ||
|
||
"github.com/scaleway/scaleway-cli/pkg/api" | ||
"github.com/scaleway/scaleway-cli/pkg/scwversion" | ||
) | ||
|
||
// Config contains scaleway configuration values | ||
type Config struct { | ||
Organization string | ||
APIKey string | ||
} | ||
|
||
// Client contains scaleway api clients | ||
type Client struct { | ||
scaleway *api.ScalewayAPI | ||
} | ||
|
||
// Client configures and returns a fully initialized Scaleway client | ||
func (c *Config) Client() (*Client, error) { | ||
api, err := api.NewScalewayAPI( | ||
c.Organization, | ||
c.APIKey, | ||
scwversion.UserAgent(), | ||
func(s *api.ScalewayAPI) { | ||
s.Logger = newTerraformLogger() | ||
}, | ||
) | ||
if err != nil { | ||
return nil, err | ||
} | ||
return &Client{api}, nil | ||
} | ||
|
||
func newTerraformLogger() api.Logger { | ||
return &terraformLogger{} | ||
} | ||
|
||
type terraformLogger struct { | ||
} | ||
|
||
func (l *terraformLogger) LogHTTP(r *http.Request) { | ||
log.Printf("[DEBUG] %s %s\n", r.Method, r.URL.Path) | ||
} | ||
func (l *terraformLogger) Fatalf(format string, v ...interface{}) { | ||
log.Printf("[FATAL] %s\n", fmt.Sprintf(format, v)) | ||
os.Exit(1) | ||
} | ||
func (l *terraformLogger) Debugf(format string, v ...interface{}) { | ||
log.Printf("[DEBUG] %s\n", fmt.Sprintf(format, v)) | ||
} | ||
func (l *terraformLogger) Infof(format string, v ...interface{}) { | ||
log.Printf("[INFO ] %s\n", fmt.Sprintf(format, v)) | ||
} | ||
func (l *terraformLogger) Warnf(format string, v ...interface{}) { | ||
log.Printf("[WARN ] %s\n", fmt.Sprintf(format, v)) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
package scaleway | ||
|
||
import ( | ||
"fmt" | ||
"log" | ||
"net/http" | ||
"time" | ||
|
||
"github.com/scaleway/scaleway-cli/pkg/api" | ||
) | ||
|
||
// Bool returns a pointer to of the bool value passed in. | ||
func Bool(val bool) *bool { | ||
return &val | ||
} | ||
|
||
// String returns a pointer to of the string value passed in. | ||
func String(val string) *string { | ||
return &val | ||
} | ||
|
||
// DetachIP detaches an IP from a server | ||
func DetachIP(s *api.ScalewayAPI, ipID string) error { | ||
var update struct { | ||
Address string `json:"address"` | ||
ID string `json:"id"` | ||
Organization string `json:"organization"` | ||
} | ||
|
||
ip, err := s.GetIP(ipID) | ||
if err != nil { | ||
return err | ||
} | ||
update.Address = ip.IP.Address | ||
update.ID = ip.IP.ID | ||
update.Organization = ip.IP.Organization | ||
|
||
resp, err := s.PutResponse(api.ComputeAPI, fmt.Sprintf("ips/%s", ipID), update) | ||
if err != nil { | ||
return err | ||
} | ||
if resp.StatusCode != http.StatusOK { | ||
return err | ||
} | ||
resp.Body.Close() | ||
return nil | ||
} | ||
|
||
// NOTE copied from github.com/scaleway/scaleway-cli/pkg/api/helpers.go | ||
// the helpers.go file pulls in quite a lot dependencies, and they're just convenience wrappers anyway | ||
|
||
func deleteServerSafe(s *api.ScalewayAPI, serverID string) error { | ||
server, err := s.GetServer(serverID) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
if server.State != "stopped" { | ||
if err := s.PostServerAction(serverID, "poweroff"); err != nil { | ||
return err | ||
} | ||
if err := waitForServerState(s, serverID, "stopped"); err != nil { | ||
return err | ||
} | ||
} | ||
|
||
if err := s.DeleteServer(serverID); err != nil { | ||
return err | ||
} | ||
if rootVolume, ok := server.Volumes["0"]; ok { | ||
if err := s.DeleteVolume(rootVolume.Identifier); err != nil { | ||
return err | ||
} | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func waitForServerState(s *api.ScalewayAPI, serverID string, targetState string) error { | ||
var server *api.ScalewayServer | ||
var err error | ||
|
||
var currentState string | ||
|
||
for { | ||
server, err = s.GetServer(serverID) | ||
if err != nil { | ||
return err | ||
} | ||
if currentState != server.State { | ||
log.Printf("[DEBUG] Server changed state to %q\n", server.State) | ||
currentState = server.State | ||
} | ||
if server.State == targetState { | ||
break | ||
} | ||
time.Sleep(1 * time.Second) | ||
} | ||
|
||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
package scaleway | ||
|
||
import ( | ||
"github.com/hashicorp/terraform/helper/schema" | ||
"github.com/hashicorp/terraform/terraform" | ||
) | ||
|
||
// Provider returns a terraform.ResourceProvider. | ||
func Provider() terraform.ResourceProvider { | ||
return &schema.Provider{ | ||
Schema: map[string]*schema.Schema{ | ||
"access_key": &schema.Schema{ | ||
Type: schema.TypeString, | ||
Required: true, | ||
DefaultFunc: schema.EnvDefaultFunc("SCALEWAY_ACCESS_KEY", nil), | ||
Description: "The API key for Scaleway API operations.", | ||
}, | ||
"organization": &schema.Schema{ | ||
Type: schema.TypeString, | ||
Required: true, | ||
DefaultFunc: schema.EnvDefaultFunc("SCALEWAY_ORGANIZATION", nil), | ||
Description: "The Organization ID for Scaleway API operations.", | ||
}, | ||
}, | ||
|
||
ResourcesMap: map[string]*schema.Resource{ | ||
"scaleway_server": resourceScalewayServer(), | ||
"scaleway_ip": resourceScalewayIP(), | ||
"scaleway_security_group": resourceScalewaySecurityGroup(), | ||
"scaleway_security_group_rule": resourceScalewaySecurityGroupRule(), | ||
"scaleway_volume": resourceScalewayVolume(), | ||
"scaleway_volume_attachment": resourceScalewayVolumeAttachment(), | ||
}, | ||
|
||
ConfigureFunc: providerConfigure, | ||
} | ||
} | ||
|
||
func providerConfigure(d *schema.ResourceData) (interface{}, error) { | ||
config := Config{ | ||
Organization: d.Get("organization").(string), | ||
APIKey: d.Get("access_key").(string), | ||
} | ||
|
||
return config.Client() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
package scaleway | ||
|
||
import ( | ||
"os" | ||
"testing" | ||
|
||
"github.com/hashicorp/terraform/helper/schema" | ||
"github.com/hashicorp/terraform/terraform" | ||
) | ||
|
||
var testAccProviders map[string]terraform.ResourceProvider | ||
var testAccProvider *schema.Provider | ||
|
||
func init() { | ||
testAccProvider = Provider().(*schema.Provider) | ||
testAccProviders = map[string]terraform.ResourceProvider{ | ||
"scaleway": testAccProvider, | ||
} | ||
} | ||
|
||
func TestProvider(t *testing.T) { | ||
if err := Provider().(*schema.Provider).InternalValidate(); err != nil { | ||
t.Fatalf("err: %s", err) | ||
} | ||
} | ||
|
||
func TestProvider_impl(t *testing.T) { | ||
var _ terraform.ResourceProvider = Provider() | ||
} | ||
|
||
func testAccPreCheck(t *testing.T) { | ||
if v := os.Getenv("SCALEWAY_ORGANIZATION"); v == "" { | ||
t.Fatal("SCALEWAY_ORGANIZATION must be set for acceptance tests") | ||
} | ||
if v := os.Getenv("SCALEWAY_ACCESS_KEY"); v == "" { | ||
t.Fatal("SCALEWAY_ACCESS_KEY must be set for acceptance tests") | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
package scaleway | ||
|
||
import ( | ||
"log" | ||
|
||
"github.com/hashicorp/terraform/helper/schema" | ||
"github.com/scaleway/scaleway-cli/pkg/api" | ||
) | ||
|
||
func resourceScalewayIP() *schema.Resource { | ||
return &schema.Resource{ | ||
Create: resourceScalewayIPCreate, | ||
Read: resourceScalewayIPRead, | ||
Update: resourceScalewayIPUpdate, | ||
Delete: resourceScalewayIPDelete, | ||
Schema: map[string]*schema.Schema{ | ||
"server": &schema.Schema{ | ||
Type: schema.TypeString, | ||
Optional: true, | ||
}, | ||
"ip": &schema.Schema{ | ||
Type: schema.TypeString, | ||
Computed: true, | ||
}, | ||
}, | ||
} | ||
} | ||
|
||
func resourceScalewayIPCreate(d *schema.ResourceData, m interface{}) error { | ||
scaleway := m.(*Client).scaleway | ||
resp, err := scaleway.NewIP() | ||
if err != nil { | ||
return err | ||
} | ||
|
||
d.SetId(resp.IP.ID) | ||
return resourceScalewayIPUpdate(d, m) | ||
} | ||
|
||
func resourceScalewayIPRead(d *schema.ResourceData, m interface{}) error { | ||
scaleway := m.(*Client).scaleway | ||
log.Printf("[DEBUG] Reading IP\n") | ||
|
||
resp, err := scaleway.GetIP(d.Id()) | ||
if err != nil { | ||
log.Printf("[DEBUG] Error reading ip: %q\n", err) | ||
if serr, ok := err.(api.ScalewayAPIError); ok { | ||
if serr.StatusCode == 404 { | ||
d.SetId("") | ||
return nil | ||
} | ||
} | ||
return err | ||
} | ||
|
||
d.Set("ip", resp.IP.Address) | ||
d.Set("server", resp.IP.Server.Identifier) | ||
return nil | ||
} | ||
|
||
func resourceScalewayIPUpdate(d *schema.ResourceData, m interface{}) error { | ||
scaleway := m.(*Client).scaleway | ||
if d.HasChange("server") { | ||
if d.Get("server").(string) != "" { | ||
log.Printf("[DEBUG] Attaching IP %q to server %q\n", d.Id(), d.Get("server").(string)) | ||
if err := scaleway.AttachIP(d.Id(), d.Get("server").(string)); err != nil { | ||
return err | ||
} | ||
} else { | ||
log.Printf("[DEBUG] Detaching IP %q\n", d.Id()) | ||
return DetachIP(scaleway, d.Id()) | ||
} | ||
} | ||
|
||
return resourceScalewayIPRead(d, m) | ||
} | ||
|
||
func resourceScalewayIPDelete(d *schema.ResourceData, m interface{}) error { | ||
scaleway := m.(*Client).scaleway | ||
err := scaleway.DeleteIP(d.Id()) | ||
if err != nil { | ||
return err | ||
} | ||
d.SetId("") | ||
return nil | ||
} |
Oops, something went wrong.