provider/scaleway: Expose IPv6 support, improve documentation (#7784)
* provider/scaleway: update api version * provider/scaleway: expose ipv6 support, rename ip attributes since it can be both ipv4 and ipv6, choose a more generic name. * provider/scaleway: allow servers in different SGs * provider/scaleway: update documentation * provider/scaleway: Update docs with security group * provider/scaleway: add testcase for server security groups * provider/scaleway: make deleting of security rules more resilient * provider/scaleway: make deletion of security group more resilient * provider/scaleway: guard against missing server
This commit is contained in:
parent
f8575f1edd
commit
9f314a3c29
|
@ -54,7 +54,9 @@ func resourceScalewayIPRead(d *schema.ResourceData, m interface{}) error {
|
|||
}
|
||||
|
||||
d.Set("ip", resp.IP.Address)
|
||||
if resp.IP.Server != nil {
|
||||
d.Set("server", resp.IP.Server.Identifier)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
@ -112,7 +112,11 @@ func testAccCheckScalewayIPAttachment(n string, check func(string) bool, msg str
|
|||
return err
|
||||
}
|
||||
|
||||
if !check(ip.IP.Server.Identifier) {
|
||||
var serverID = ""
|
||||
if ip.IP.Server != nil {
|
||||
serverID = ip.IP.Server.Identifier
|
||||
}
|
||||
if !check(serverID) {
|
||||
return fmt.Errorf("IP check failed: %q", msg)
|
||||
}
|
||||
|
||||
|
|
|
@ -90,7 +90,7 @@ func resourceScalewaySecurityGroupRead(d *schema.ResourceData, m interface{}) er
|
|||
func resourceScalewaySecurityGroupUpdate(d *schema.ResourceData, m interface{}) error {
|
||||
scaleway := m.(*Client).scaleway
|
||||
|
||||
var req = api.ScalewayNewSecurityGroup{
|
||||
var req = api.ScalewayUpdateSecurityGroup{
|
||||
Organization: scaleway.Organization,
|
||||
Name: d.Get("name").(string),
|
||||
Description: d.Get("description").(string),
|
||||
|
@ -110,6 +110,15 @@ func resourceScalewaySecurityGroupDelete(d *schema.ResourceData, m interface{})
|
|||
|
||||
err := scaleway.DeleteSecurityGroup(d.Id())
|
||||
if err != nil {
|
||||
if serr, ok := err.(api.ScalewayAPIError); ok {
|
||||
log.Printf("[DEBUG] error reading Security Group Rule: %q\n", serr.APIMessage)
|
||||
|
||||
if serr.StatusCode == 404 {
|
||||
d.SetId("")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
|
|
|
@ -154,6 +154,15 @@ func resourceScalewaySecurityGroupRuleDelete(d *schema.ResourceData, m interface
|
|||
|
||||
err := scaleway.DeleteSecurityGroupRule(d.Get("security_group").(string), d.Id())
|
||||
if err != nil {
|
||||
if serr, ok := err.(api.ScalewayAPIError); ok {
|
||||
log.Printf("[DEBUG] error reading Security Group Rule: %q\n", serr.APIMessage)
|
||||
|
||||
if serr.StatusCode == 404 {
|
||||
d.SetId("")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
|
|
|
@ -40,11 +40,24 @@ func resourceScalewayServer() *schema.Resource {
|
|||
},
|
||||
Optional: true,
|
||||
},
|
||||
"ipv4_address_private": &schema.Schema{
|
||||
"enable_ipv6": &schema.Schema{
|
||||
Type: schema.TypeBool,
|
||||
Optional: true,
|
||||
Default: false,
|
||||
},
|
||||
"dynamic_ip_required": &schema.Schema{
|
||||
Type: schema.TypeBool,
|
||||
Optional: true,
|
||||
},
|
||||
"security_group": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
},
|
||||
"private_ip": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
},
|
||||
"ipv4_address_public": &schema.Schema{
|
||||
"public_ip": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
},
|
||||
|
@ -53,10 +66,6 @@ func resourceScalewayServer() *schema.Resource {
|
|||
Optional: true,
|
||||
Computed: true,
|
||||
},
|
||||
"dynamic_ip_required": &schema.Schema{
|
||||
Type: schema.TypeBool,
|
||||
Optional: true,
|
||||
},
|
||||
"state_detail": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
|
@ -73,6 +82,8 @@ func resourceScalewayServerCreate(d *schema.ResourceData, m interface{}) error {
|
|||
Name: d.Get("name").(string),
|
||||
Image: String(image),
|
||||
Organization: scaleway.Organization,
|
||||
EnableIPV6: d.Get("enable_ipv6").(bool),
|
||||
SecurityGroup: d.Get("security_group").(string),
|
||||
}
|
||||
|
||||
server.DynamicIPRequired = Bool(d.Get("dynamic_ip_required").(bool))
|
||||
|
@ -127,8 +138,9 @@ func resourceScalewayServerRead(d *schema.ResourceData, m interface{}) error {
|
|||
return err
|
||||
}
|
||||
|
||||
d.Set("ipv4_address_private", server.PrivateIP)
|
||||
d.Set("ipv4_address_public", server.PublicAddress.IP)
|
||||
d.Set("private_ip", server.PrivateIP)
|
||||
d.Set("public_ip", server.PublicAddress.IP)
|
||||
|
||||
d.Set("state", server.State)
|
||||
d.Set("state_detail", server.StateDetail)
|
||||
d.Set("tags", server.Tags)
|
||||
|
@ -161,10 +173,20 @@ func resourceScalewayServerUpdate(d *schema.ResourceData, m interface{}) error {
|
|||
}
|
||||
}
|
||||
|
||||
if d.HasChange("enable_ipv6") {
|
||||
req.EnableIPV6 = Bool(d.Get("enable_ipv6").(bool))
|
||||
}
|
||||
|
||||
if d.HasChange("dynamic_ip_required") {
|
||||
req.DynamicIPRequired = Bool(d.Get("dynamic_ip_required").(bool))
|
||||
}
|
||||
|
||||
if d.HasChange("security_group") {
|
||||
req.SecurityGroup = &api.ScalewaySecurityGroup{
|
||||
Identifier: d.Get("security_group").(string),
|
||||
}
|
||||
}
|
||||
|
||||
if err := scaleway.PatchServer(d.Id(), req); err != nil {
|
||||
return fmt.Errorf("Failed patching scaleway server: %q", err)
|
||||
}
|
||||
|
|
|
@ -31,6 +31,30 @@ func TestAccScalewayServer_Basic(t *testing.T) {
|
|||
})
|
||||
}
|
||||
|
||||
func TestAccScalewayServer_SecurityGroup(t *testing.T) {
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckScalewayServerDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
Config: testAccCheckScalewayServerConfig_SecurityGroup,
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckScalewayServerExists("scaleway_server.base"),
|
||||
testAccCheckScalewayServerSecurityGroup("scaleway_server.base", "blue"),
|
||||
),
|
||||
},
|
||||
resource.TestStep{
|
||||
Config: testAccCheckScalewayServerConfig_SecurityGroup_Update,
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckScalewayServerExists("scaleway_server.base"),
|
||||
testAccCheckScalewayServerSecurityGroup("scaleway_server.base", "red"),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func testAccCheckScalewayServerDestroy(s *terraform.State) error {
|
||||
client := testAccProvider.Meta().(*Client).scaleway
|
||||
|
||||
|
@ -77,6 +101,28 @@ func testAccCheckScalewayServerAttributes(n string) resource.TestCheckFunc {
|
|||
}
|
||||
}
|
||||
|
||||
func testAccCheckScalewayServerSecurityGroup(n, securityGroupName string) resource.TestCheckFunc {
|
||||
return func(s *terraform.State) error {
|
||||
rs, ok := s.RootModule().Resources[n]
|
||||
if !ok {
|
||||
return fmt.Errorf("Unknown resource: %s", n)
|
||||
}
|
||||
|
||||
client := testAccProvider.Meta().(*Client).scaleway
|
||||
server, err := client.GetServer(rs.Primary.ID)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if server.SecurityGroup.Name != securityGroupName {
|
||||
return fmt.Errorf("Server has wrong security_group")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func testAccCheckScalewayServerExists(n string) resource.TestCheckFunc {
|
||||
return func(s *terraform.State) error {
|
||||
rs, ok := s.RootModule().Resources[n]
|
||||
|
@ -114,3 +160,43 @@ resource "scaleway_server" "base" {
|
|||
type = "C1"
|
||||
tags = [ "terraform-test" ]
|
||||
}`, armImageIdentifier)
|
||||
|
||||
var testAccCheckScalewayServerConfig_SecurityGroup = fmt.Sprintf(`
|
||||
resource "scaleway_security_group" "blue" {
|
||||
name = "blue"
|
||||
description = "blue"
|
||||
}
|
||||
|
||||
resource "scaleway_security_group" "red" {
|
||||
name = "red"
|
||||
description = "red"
|
||||
}
|
||||
|
||||
resource "scaleway_server" "base" {
|
||||
name = "test"
|
||||
# ubuntu 14.04
|
||||
image = "%s"
|
||||
type = "C1"
|
||||
tags = [ "terraform-test" ]
|
||||
security_group = "${scaleway_security_group.blue.id}"
|
||||
}`, armImageIdentifier)
|
||||
|
||||
var testAccCheckScalewayServerConfig_SecurityGroup_Update = fmt.Sprintf(`
|
||||
resource "scaleway_security_group" "blue" {
|
||||
name = "blue"
|
||||
description = "blue"
|
||||
}
|
||||
|
||||
resource "scaleway_security_group" "red" {
|
||||
name = "red"
|
||||
description = "red"
|
||||
}
|
||||
|
||||
resource "scaleway_server" "base" {
|
||||
name = "test"
|
||||
# ubuntu 14.04
|
||||
image = "%s"
|
||||
type = "C1"
|
||||
tags = [ "terraform-test" ]
|
||||
security_group = "${scaleway_security_group.red.id}"
|
||||
}`, armImageIdentifier)
|
||||
|
|
|
@ -98,7 +98,7 @@ func (e ScalewayAPIError) Error() string {
|
|||
"Message": e.Message,
|
||||
"APIMessage": e.APIMessage,
|
||||
} {
|
||||
fmt.Fprintf(&b, " %-30s %s", fmt.Sprintf("%s: ", k), v)
|
||||
fmt.Fprintf(&b, "%s: %v ", k, v)
|
||||
}
|
||||
return b.String()
|
||||
}
|
||||
|
@ -420,12 +420,12 @@ type ScalewayGetSecurityGroup struct {
|
|||
// ScalewayIPDefinition represents the IP's fields
|
||||
type ScalewayIPDefinition struct {
|
||||
Organization string `json:"organization"`
|
||||
Reverse string `json:"reverse"`
|
||||
Reverse *string `json:"reverse"`
|
||||
ID string `json:"id"`
|
||||
Server struct {
|
||||
Server *struct {
|
||||
Identifier string `json:"id,omitempty"`
|
||||
Name string `json:"name,omitempty"`
|
||||
} `json:"server,omitempty"`
|
||||
} `json:"server"`
|
||||
Address string `json:"address"`
|
||||
}
|
||||
|
||||
|
@ -448,11 +448,20 @@ type ScalewaySecurityGroup struct {
|
|||
Name string `json:"name,omitempty"`
|
||||
}
|
||||
|
||||
// ScalewayNewSecurityGroup definition POST/PUT request /security_groups
|
||||
// ScalewayNewSecurityGroup definition POST request /security_groups
|
||||
type ScalewayNewSecurityGroup struct {
|
||||
Organization string `json:"organization"`
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description"`
|
||||
OrganizationDefault bool `json:"organization_default"`
|
||||
}
|
||||
|
||||
// ScalewayUpdateSecurityGroup definition PUT request /security_groups
|
||||
type ScalewayUpdateSecurityGroup struct {
|
||||
Organization string `json:"organization"`
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description"`
|
||||
OrganizationDefault bool `json:"organization_default"`
|
||||
}
|
||||
|
||||
// ScalewayServer represents a Scaleway server
|
||||
|
@ -584,6 +593,8 @@ type ScalewayServerDefinition struct {
|
|||
PublicIP string `json:"public_ip,omitempty"`
|
||||
|
||||
EnableIPV6 bool `json:"enable_ipv6,omitempty"`
|
||||
|
||||
SecurityGroup string `json:"security_group,omitempty"`
|
||||
}
|
||||
|
||||
// ScalewayOneServer represents the response of a GET /servers/UUID API call
|
||||
|
@ -832,27 +843,26 @@ type MarketImages struct {
|
|||
|
||||
// NewScalewayAPI creates a ready-to-use ScalewayAPI client
|
||||
func NewScalewayAPI(organization, token, userAgent string, options ...func(*ScalewayAPI)) (*ScalewayAPI, error) {
|
||||
cache, err := NewScalewayCache()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
s := &ScalewayAPI{
|
||||
// exposed
|
||||
Organization: organization,
|
||||
Token: token,
|
||||
Cache: cache,
|
||||
Logger: NewDefaultLogger(),
|
||||
verbose: os.Getenv("SCW_VERBOSE_API") != "",
|
||||
password: "",
|
||||
userAgent: userAgent,
|
||||
|
||||
// internal
|
||||
client: &http.Client{},
|
||||
verbose: os.Getenv("SCW_VERBOSE_API") != "",
|
||||
password: "",
|
||||
userAgent: userAgent,
|
||||
}
|
||||
for _, option := range options {
|
||||
option(s)
|
||||
}
|
||||
|
||||
cache, err := NewScalewayCache(func() { s.Logger.Debugf("Writing cache file to disk") })
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
s.Cache = cache
|
||||
if os.Getenv("SCW_TLSVERIFY") == "0" {
|
||||
s.client.Transport = &http.Transport{
|
||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
|
||||
|
@ -1273,62 +1283,77 @@ func (s *ScalewayAPI) PutVolume(volumeID string, definition ScalewayVolumePutDef
|
|||
|
||||
// ResolveServer attempts to find a matching Identifier for the input string
|
||||
func (s *ScalewayAPI) ResolveServer(needle string) (ScalewayResolverResults, error) {
|
||||
servers := s.Cache.LookUpServers(needle, true)
|
||||
servers, err := s.Cache.LookUpServers(needle, true)
|
||||
if err != nil {
|
||||
return servers, err
|
||||
}
|
||||
if len(servers) == 0 {
|
||||
if _, err := s.GetServers(true, 0); err != nil {
|
||||
if _, err = s.GetServers(true, 0); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
servers = s.Cache.LookUpServers(needle, true)
|
||||
servers, err = s.Cache.LookUpServers(needle, true)
|
||||
}
|
||||
return servers, nil
|
||||
return servers, err
|
||||
}
|
||||
|
||||
// ResolveVolume attempts to find a matching Identifier for the input string
|
||||
func (s *ScalewayAPI) ResolveVolume(needle string) (ScalewayResolverResults, error) {
|
||||
volumes := s.Cache.LookUpVolumes(needle, true)
|
||||
volumes, err := s.Cache.LookUpVolumes(needle, true)
|
||||
if err != nil {
|
||||
return volumes, err
|
||||
}
|
||||
if len(volumes) == 0 {
|
||||
if _, err := s.GetVolumes(); err != nil {
|
||||
if _, err = s.GetVolumes(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
volumes = s.Cache.LookUpVolumes(needle, true)
|
||||
volumes, err = s.Cache.LookUpVolumes(needle, true)
|
||||
}
|
||||
return volumes, nil
|
||||
return volumes, err
|
||||
}
|
||||
|
||||
// ResolveSnapshot attempts to find a matching Identifier for the input string
|
||||
func (s *ScalewayAPI) ResolveSnapshot(needle string) (ScalewayResolverResults, error) {
|
||||
snapshots := s.Cache.LookUpSnapshots(needle, true)
|
||||
snapshots, err := s.Cache.LookUpSnapshots(needle, true)
|
||||
if err != nil {
|
||||
return snapshots, err
|
||||
}
|
||||
if len(snapshots) == 0 {
|
||||
if _, err := s.GetSnapshots(); err != nil {
|
||||
if _, err = s.GetSnapshots(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
snapshots = s.Cache.LookUpSnapshots(needle, true)
|
||||
snapshots, err = s.Cache.LookUpSnapshots(needle, true)
|
||||
}
|
||||
return snapshots, nil
|
||||
return snapshots, err
|
||||
}
|
||||
|
||||
// ResolveImage attempts to find a matching Identifier for the input string
|
||||
func (s *ScalewayAPI) ResolveImage(needle string) (ScalewayResolverResults, error) {
|
||||
images := s.Cache.LookUpImages(needle, true)
|
||||
images, err := s.Cache.LookUpImages(needle, true)
|
||||
if err != nil {
|
||||
return images, err
|
||||
}
|
||||
if len(images) == 0 {
|
||||
if _, err := s.GetImages(); err != nil {
|
||||
if _, err = s.GetImages(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
images = s.Cache.LookUpImages(needle, true)
|
||||
images, err = s.Cache.LookUpImages(needle, true)
|
||||
}
|
||||
return images, nil
|
||||
return images, err
|
||||
}
|
||||
|
||||
// ResolveBootscript attempts to find a matching Identifier for the input string
|
||||
func (s *ScalewayAPI) ResolveBootscript(needle string) (ScalewayResolverResults, error) {
|
||||
bootscripts := s.Cache.LookUpBootscripts(needle, true)
|
||||
bootscripts, err := s.Cache.LookUpBootscripts(needle, true)
|
||||
if err != nil {
|
||||
return bootscripts, err
|
||||
}
|
||||
if len(bootscripts) == 0 {
|
||||
if _, err := s.GetBootscripts(); err != nil {
|
||||
if _, err = s.GetBootscripts(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
bootscripts = s.Cache.LookUpBootscripts(needle, true)
|
||||
bootscripts, err = s.Cache.LookUpBootscripts(needle, true)
|
||||
}
|
||||
return bootscripts, nil
|
||||
return bootscripts, err
|
||||
}
|
||||
|
||||
// GetImages gets the list of images from the ScalewayAPI
|
||||
|
@ -2154,7 +2179,7 @@ func (s *ScalewayAPI) DeleteSecurityGroup(securityGroupID string) error {
|
|||
}
|
||||
|
||||
// PutSecurityGroup updates a SecurityGroup
|
||||
func (s *ScalewayAPI) PutSecurityGroup(group ScalewayNewSecurityGroup, securityGroupID string) error {
|
||||
func (s *ScalewayAPI) PutSecurityGroup(group ScalewayUpdateSecurityGroup, securityGroupID string) error {
|
||||
resp, err := s.PutResponse(ComputeAPI, fmt.Sprintf("security_groups/%s", securityGroupID), group)
|
||||
if resp != nil {
|
||||
defer resp.Body.Close()
|
||||
|
@ -2313,6 +2338,24 @@ func (s *ScalewayAPI) AttachIP(ipID, serverID string) error {
|
|||
return err
|
||||
}
|
||||
|
||||
// DetachIP detaches an IP from a server
|
||||
func (s *ScalewayAPI) DetachIP(ipID string) error {
|
||||
ip, err := s.GetIP(ipID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ip.IP.Server = nil
|
||||
resp, err := s.PutResponse(ComputeAPI, fmt.Sprintf("ips/%s", ipID), ip.IP)
|
||||
if resp != nil {
|
||||
defer resp.Body.Close()
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = s.handleHTTPError([]int{200}, resp)
|
||||
return err
|
||||
}
|
||||
|
||||
// DeleteIP deletes an IP
|
||||
func (s *ScalewayAPI) DeleteIP(ipID string) error {
|
||||
resp, err := s.DeleteResponse(ComputeAPI, fmt.Sprintf("ips/%s", ipID))
|
||||
|
@ -2322,11 +2365,8 @@ func (s *ScalewayAPI) DeleteIP(ipID string) error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := s.handleHTTPError([]int{204}, resp); err != nil {
|
||||
_, err = s.handleHTTPError([]int{204}, resp)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetIP returns a ScalewayGetIP
|
||||
|
|
|
@ -8,7 +8,6 @@ import (
|
|||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
|
@ -60,6 +59,8 @@ type ScalewayCache struct {
|
|||
|
||||
// Lock allows ScalewayCache to be used concurrently
|
||||
Lock sync.Mutex `json:"-"`
|
||||
|
||||
hookSave func()
|
||||
}
|
||||
|
||||
const (
|
||||
|
@ -92,16 +93,16 @@ type ScalewayResolverResult struct {
|
|||
type ScalewayResolverResults []ScalewayResolverResult
|
||||
|
||||
// NewScalewayResolverResult returns a new ScalewayResolverResult
|
||||
func NewScalewayResolverResult(Identifier, Name, Arch string, Type int) ScalewayResolverResult {
|
||||
func NewScalewayResolverResult(Identifier, Name, Arch string, Type int) (ScalewayResolverResult, error) {
|
||||
if err := anonuuid.IsUUID(Identifier); err != nil {
|
||||
log.Fatal(err)
|
||||
return ScalewayResolverResult{}, err
|
||||
}
|
||||
return ScalewayResolverResult{
|
||||
Identifier: Identifier,
|
||||
Type: Type,
|
||||
Name: Name,
|
||||
Arch: Arch,
|
||||
}
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s ScalewayResolverResults) Len() int {
|
||||
|
@ -160,7 +161,10 @@ REDO:
|
|||
}
|
||||
|
||||
// NewScalewayCache loads a per-user cache
|
||||
func NewScalewayCache() (*ScalewayCache, error) {
|
||||
func NewScalewayCache(hookSave func()) (*ScalewayCache, error) {
|
||||
var cache ScalewayCache
|
||||
|
||||
cache.hookSave = hookSave
|
||||
homeDir := os.Getenv("HOME") // *nix
|
||||
if homeDir == "" { // Windows
|
||||
homeDir = os.Getenv("USERPROFILE")
|
||||
|
@ -169,7 +173,6 @@ func NewScalewayCache() (*ScalewayCache, error) {
|
|||
homeDir = "/tmp"
|
||||
}
|
||||
cachePath := filepath.Join(homeDir, ".scw-cache.db")
|
||||
var cache ScalewayCache
|
||||
cache.Path = cachePath
|
||||
_, err := os.Stat(cachePath)
|
||||
if os.IsNotExist(err) {
|
||||
|
@ -210,13 +213,13 @@ func NewScalewayCache() (*ScalewayCache, error) {
|
|||
}
|
||||
|
||||
// Clear removes all information from the cache
|
||||
func (s *ScalewayCache) Clear() {
|
||||
s.Images = make(map[string][CacheMaxfield]string)
|
||||
s.Snapshots = make(map[string][CacheMaxfield]string)
|
||||
s.Volumes = make(map[string][CacheMaxfield]string)
|
||||
s.Bootscripts = make(map[string][CacheMaxfield]string)
|
||||
s.Servers = make(map[string][CacheMaxfield]string)
|
||||
s.Modified = true
|
||||
func (c *ScalewayCache) Clear() {
|
||||
c.Images = make(map[string][CacheMaxfield]string)
|
||||
c.Snapshots = make(map[string][CacheMaxfield]string)
|
||||
c.Volumes = make(map[string][CacheMaxfield]string)
|
||||
c.Bootscripts = make(map[string][CacheMaxfield]string)
|
||||
c.Servers = make(map[string][CacheMaxfield]string)
|
||||
c.Modified = true
|
||||
}
|
||||
|
||||
// Flush flushes the cache database
|
||||
|
@ -229,8 +232,7 @@ func (c *ScalewayCache) Save() error {
|
|||
c.Lock.Lock()
|
||||
defer c.Lock.Unlock()
|
||||
|
||||
log.Printf("Writing cache file to disk")
|
||||
|
||||
c.hookSave()
|
||||
if c.Modified {
|
||||
file, err := ioutil.TempFile(filepath.Dir(c.Path), filepath.Base(c.Path))
|
||||
if err != nil {
|
||||
|
@ -259,15 +261,19 @@ func (s *ScalewayResolverResult) ComputeRankMatch(needle string) {
|
|||
}
|
||||
|
||||
// LookUpImages attempts to return identifiers matching a pattern
|
||||
func (c *ScalewayCache) LookUpImages(needle string, acceptUUID bool) ScalewayResolverResults {
|
||||
func (c *ScalewayCache) LookUpImages(needle string, acceptUUID bool) (ScalewayResolverResults, error) {
|
||||
c.Lock.Lock()
|
||||
defer c.Lock.Unlock()
|
||||
|
||||
var res ScalewayResolverResults
|
||||
var exactMatches ScalewayResolverResults
|
||||
|
||||
if acceptUUID && anonuuid.IsUUID(needle) == nil {
|
||||
if fields, ok := c.Images[needle]; ok {
|
||||
entry := NewScalewayResolverResult(needle, fields[CacheTitle], fields[CacheArch], IdentifierImage)
|
||||
entry, err := NewScalewayResolverResult(needle, fields[CacheTitle], fields[CacheArch], IdentifierImage)
|
||||
if err != nil {
|
||||
return ScalewayResolverResults{}, err
|
||||
}
|
||||
entry.ComputeRankMatch(needle)
|
||||
res = append(res, entry)
|
||||
}
|
||||
|
@ -276,41 +282,53 @@ func (c *ScalewayCache) LookUpImages(needle string, acceptUUID bool) ScalewayRes
|
|||
needle = regexp.MustCompile(`^user/`).ReplaceAllString(needle, "")
|
||||
// FIXME: if 'user/' is in needle, only watch for a user image
|
||||
nameRegex := regexp.MustCompile(`(?i)` + regexp.MustCompile(`[_-]`).ReplaceAllString(needle, ".*"))
|
||||
var exactMatches ScalewayResolverResults
|
||||
for identifier, fields := range c.Images {
|
||||
if fields[CacheTitle] == needle {
|
||||
entry := NewScalewayResolverResult(identifier, fields[CacheTitle], fields[CacheArch], IdentifierImage)
|
||||
entry, err := NewScalewayResolverResult(identifier, fields[CacheTitle], fields[CacheArch], IdentifierImage)
|
||||
if err != nil {
|
||||
return ScalewayResolverResults{}, err
|
||||
}
|
||||
entry.ComputeRankMatch(needle)
|
||||
exactMatches = append(exactMatches, entry)
|
||||
}
|
||||
if strings.HasPrefix(identifier, needle) || nameRegex.MatchString(fields[CacheTitle]) {
|
||||
entry := NewScalewayResolverResult(identifier, fields[CacheTitle], fields[CacheArch], IdentifierImage)
|
||||
entry, err := NewScalewayResolverResult(identifier, fields[CacheTitle], fields[CacheArch], IdentifierImage)
|
||||
if err != nil {
|
||||
return ScalewayResolverResults{}, err
|
||||
}
|
||||
entry.ComputeRankMatch(needle)
|
||||
res = append(res, entry)
|
||||
} else if strings.HasPrefix(fields[CacheMarketPlaceUUID], needle) || nameRegex.MatchString(fields[CacheMarketPlaceUUID]) {
|
||||
entry := NewScalewayResolverResult(identifier, fields[CacheTitle], fields[CacheArch], IdentifierImage)
|
||||
entry, err := NewScalewayResolverResult(identifier, fields[CacheTitle], fields[CacheArch], IdentifierImage)
|
||||
if err != nil {
|
||||
return ScalewayResolverResults{}, err
|
||||
}
|
||||
entry.ComputeRankMatch(needle)
|
||||
res = append(res, entry)
|
||||
}
|
||||
}
|
||||
|
||||
if len(exactMatches) == 1 {
|
||||
return exactMatches
|
||||
return exactMatches, nil
|
||||
}
|
||||
|
||||
return removeDuplicatesResults(res)
|
||||
return removeDuplicatesResults(res), nil
|
||||
}
|
||||
|
||||
// LookUpSnapshots attempts to return identifiers matching a pattern
|
||||
func (c *ScalewayCache) LookUpSnapshots(needle string, acceptUUID bool) ScalewayResolverResults {
|
||||
func (c *ScalewayCache) LookUpSnapshots(needle string, acceptUUID bool) (ScalewayResolverResults, error) {
|
||||
c.Lock.Lock()
|
||||
defer c.Lock.Unlock()
|
||||
|
||||
var res ScalewayResolverResults
|
||||
var exactMatches ScalewayResolverResults
|
||||
|
||||
if acceptUUID && anonuuid.IsUUID(needle) == nil {
|
||||
if fields, ok := c.Snapshots[needle]; ok {
|
||||
entry := NewScalewayResolverResult(needle, fields[CacheTitle], fields[CacheArch], IdentifierSnapshot)
|
||||
entry, err := NewScalewayResolverResult(needle, fields[CacheTitle], fields[CacheArch], IdentifierSnapshot)
|
||||
if err != nil {
|
||||
return ScalewayResolverResults{}, err
|
||||
}
|
||||
entry.ComputeRankMatch(needle)
|
||||
res = append(res, entry)
|
||||
}
|
||||
|
@ -318,136 +336,168 @@ func (c *ScalewayCache) LookUpSnapshots(needle string, acceptUUID bool) Scaleway
|
|||
|
||||
needle = regexp.MustCompile(`^user/`).ReplaceAllString(needle, "")
|
||||
nameRegex := regexp.MustCompile(`(?i)` + regexp.MustCompile(`[_-]`).ReplaceAllString(needle, ".*"))
|
||||
var exactMatches ScalewayResolverResults
|
||||
for identifier, fields := range c.Snapshots {
|
||||
if fields[CacheTitle] == needle {
|
||||
entry := NewScalewayResolverResult(identifier, fields[CacheTitle], fields[CacheArch], IdentifierSnapshot)
|
||||
entry, err := NewScalewayResolverResult(identifier, fields[CacheTitle], fields[CacheArch], IdentifierSnapshot)
|
||||
if err != nil {
|
||||
return ScalewayResolverResults{}, err
|
||||
}
|
||||
entry.ComputeRankMatch(needle)
|
||||
exactMatches = append(exactMatches, entry)
|
||||
}
|
||||
if strings.HasPrefix(identifier, needle) || nameRegex.MatchString(fields[CacheTitle]) {
|
||||
entry := NewScalewayResolverResult(identifier, fields[CacheTitle], fields[CacheArch], IdentifierSnapshot)
|
||||
entry, err := NewScalewayResolverResult(identifier, fields[CacheTitle], fields[CacheArch], IdentifierSnapshot)
|
||||
if err != nil {
|
||||
return ScalewayResolverResults{}, err
|
||||
}
|
||||
entry.ComputeRankMatch(needle)
|
||||
res = append(res, entry)
|
||||
}
|
||||
}
|
||||
|
||||
if len(exactMatches) == 1 {
|
||||
return exactMatches
|
||||
return exactMatches, nil
|
||||
}
|
||||
|
||||
return removeDuplicatesResults(res)
|
||||
return removeDuplicatesResults(res), nil
|
||||
}
|
||||
|
||||
// LookUpVolumes attempts to return identifiers matching a pattern
|
||||
func (c *ScalewayCache) LookUpVolumes(needle string, acceptUUID bool) ScalewayResolverResults {
|
||||
func (c *ScalewayCache) LookUpVolumes(needle string, acceptUUID bool) (ScalewayResolverResults, error) {
|
||||
c.Lock.Lock()
|
||||
defer c.Lock.Unlock()
|
||||
|
||||
var res ScalewayResolverResults
|
||||
var exactMatches ScalewayResolverResults
|
||||
|
||||
if acceptUUID && anonuuid.IsUUID(needle) == nil {
|
||||
if fields, ok := c.Volumes[needle]; ok {
|
||||
entry := NewScalewayResolverResult(needle, fields[CacheTitle], fields[CacheArch], IdentifierVolume)
|
||||
entry, err := NewScalewayResolverResult(needle, fields[CacheTitle], fields[CacheArch], IdentifierVolume)
|
||||
if err != nil {
|
||||
return ScalewayResolverResults{}, err
|
||||
}
|
||||
entry.ComputeRankMatch(needle)
|
||||
res = append(res, entry)
|
||||
}
|
||||
}
|
||||
|
||||
nameRegex := regexp.MustCompile(`(?i)` + regexp.MustCompile(`[_-]`).ReplaceAllString(needle, ".*"))
|
||||
var exactMatches ScalewayResolverResults
|
||||
for identifier, fields := range c.Volumes {
|
||||
if fields[CacheTitle] == needle {
|
||||
entry := NewScalewayResolverResult(identifier, fields[CacheTitle], fields[CacheArch], IdentifierVolume)
|
||||
entry, err := NewScalewayResolverResult(identifier, fields[CacheTitle], fields[CacheArch], IdentifierVolume)
|
||||
if err != nil {
|
||||
return ScalewayResolverResults{}, err
|
||||
}
|
||||
entry.ComputeRankMatch(needle)
|
||||
exactMatches = append(exactMatches, entry)
|
||||
}
|
||||
if strings.HasPrefix(identifier, needle) || nameRegex.MatchString(fields[CacheTitle]) {
|
||||
entry := NewScalewayResolverResult(identifier, fields[CacheTitle], fields[CacheArch], IdentifierVolume)
|
||||
entry, err := NewScalewayResolverResult(identifier, fields[CacheTitle], fields[CacheArch], IdentifierVolume)
|
||||
if err != nil {
|
||||
return ScalewayResolverResults{}, err
|
||||
}
|
||||
entry.ComputeRankMatch(needle)
|
||||
res = append(res, entry)
|
||||
}
|
||||
}
|
||||
|
||||
if len(exactMatches) == 1 {
|
||||
return exactMatches
|
||||
return exactMatches, nil
|
||||
}
|
||||
|
||||
return removeDuplicatesResults(res)
|
||||
return removeDuplicatesResults(res), nil
|
||||
}
|
||||
|
||||
// LookUpBootscripts attempts to return identifiers matching a pattern
|
||||
func (c *ScalewayCache) LookUpBootscripts(needle string, acceptUUID bool) ScalewayResolverResults {
|
||||
func (c *ScalewayCache) LookUpBootscripts(needle string, acceptUUID bool) (ScalewayResolverResults, error) {
|
||||
c.Lock.Lock()
|
||||
defer c.Lock.Unlock()
|
||||
|
||||
var res ScalewayResolverResults
|
||||
var exactMatches ScalewayResolverResults
|
||||
|
||||
if acceptUUID && anonuuid.IsUUID(needle) == nil {
|
||||
if fields, ok := c.Bootscripts[needle]; ok {
|
||||
entry := NewScalewayResolverResult(needle, fields[CacheTitle], fields[CacheArch], IdentifierBootscript)
|
||||
entry, err := NewScalewayResolverResult(needle, fields[CacheTitle], fields[CacheArch], IdentifierBootscript)
|
||||
if err != nil {
|
||||
return ScalewayResolverResults{}, err
|
||||
}
|
||||
entry.ComputeRankMatch(needle)
|
||||
res = append(res, entry)
|
||||
}
|
||||
}
|
||||
|
||||
nameRegex := regexp.MustCompile(`(?i)` + regexp.MustCompile(`[_-]`).ReplaceAllString(needle, ".*"))
|
||||
var exactMatches ScalewayResolverResults
|
||||
for identifier, fields := range c.Bootscripts {
|
||||
if fields[CacheTitle] == needle {
|
||||
entry := NewScalewayResolverResult(identifier, fields[CacheTitle], fields[CacheArch], IdentifierBootscript)
|
||||
entry, err := NewScalewayResolverResult(identifier, fields[CacheTitle], fields[CacheArch], IdentifierBootscript)
|
||||
if err != nil {
|
||||
return ScalewayResolverResults{}, err
|
||||
}
|
||||
entry.ComputeRankMatch(needle)
|
||||
exactMatches = append(exactMatches, entry)
|
||||
}
|
||||
if strings.HasPrefix(identifier, needle) || nameRegex.MatchString(fields[CacheTitle]) {
|
||||
entry := NewScalewayResolverResult(identifier, fields[CacheTitle], fields[CacheArch], IdentifierBootscript)
|
||||
entry, err := NewScalewayResolverResult(identifier, fields[CacheTitle], fields[CacheArch], IdentifierBootscript)
|
||||
if err != nil {
|
||||
return ScalewayResolverResults{}, err
|
||||
}
|
||||
entry.ComputeRankMatch(needle)
|
||||
res = append(res, entry)
|
||||
}
|
||||
}
|
||||
|
||||
if len(exactMatches) == 1 {
|
||||
return exactMatches
|
||||
return exactMatches, nil
|
||||
}
|
||||
|
||||
return removeDuplicatesResults(res)
|
||||
return removeDuplicatesResults(res), nil
|
||||
}
|
||||
|
||||
// LookUpServers attempts to return identifiers matching a pattern
|
||||
func (c *ScalewayCache) LookUpServers(needle string, acceptUUID bool) ScalewayResolverResults {
|
||||
func (c *ScalewayCache) LookUpServers(needle string, acceptUUID bool) (ScalewayResolverResults, error) {
|
||||
c.Lock.Lock()
|
||||
defer c.Lock.Unlock()
|
||||
|
||||
var res ScalewayResolverResults
|
||||
var exactMatches ScalewayResolverResults
|
||||
|
||||
if acceptUUID && anonuuid.IsUUID(needle) == nil {
|
||||
if fields, ok := c.Servers[needle]; ok {
|
||||
entry := NewScalewayResolverResult(needle, fields[CacheTitle], fields[CacheArch], IdentifierServer)
|
||||
entry, err := NewScalewayResolverResult(needle, fields[CacheTitle], fields[CacheArch], IdentifierServer)
|
||||
if err != nil {
|
||||
return ScalewayResolverResults{}, err
|
||||
}
|
||||
entry.ComputeRankMatch(needle)
|
||||
res = append(res, entry)
|
||||
}
|
||||
}
|
||||
|
||||
nameRegex := regexp.MustCompile(`(?i)` + regexp.MustCompile(`[_-]`).ReplaceAllString(needle, ".*"))
|
||||
var exactMatches ScalewayResolverResults
|
||||
for identifier, fields := range c.Servers {
|
||||
if fields[CacheTitle] == needle {
|
||||
entry := NewScalewayResolverResult(identifier, fields[CacheTitle], fields[CacheArch], IdentifierServer)
|
||||
entry, err := NewScalewayResolverResult(identifier, fields[CacheTitle], fields[CacheArch], IdentifierServer)
|
||||
if err != nil {
|
||||
return ScalewayResolverResults{}, err
|
||||
}
|
||||
entry.ComputeRankMatch(needle)
|
||||
exactMatches = append(exactMatches, entry)
|
||||
}
|
||||
if strings.HasPrefix(identifier, needle) || nameRegex.MatchString(fields[CacheTitle]) {
|
||||
entry := NewScalewayResolverResult(identifier, fields[CacheTitle], fields[CacheArch], IdentifierServer)
|
||||
entry, err := NewScalewayResolverResult(identifier, fields[CacheTitle], fields[CacheArch], IdentifierServer)
|
||||
if err != nil {
|
||||
return ScalewayResolverResults{}, err
|
||||
}
|
||||
entry.ComputeRankMatch(needle)
|
||||
res = append(res, entry)
|
||||
}
|
||||
}
|
||||
|
||||
if len(exactMatches) == 1 {
|
||||
return exactMatches
|
||||
return exactMatches, nil
|
||||
}
|
||||
|
||||
return removeDuplicatesResults(res)
|
||||
return removeDuplicatesResults(res), nil
|
||||
}
|
||||
|
||||
// removeDuplicatesResults transforms an array into a unique array
|
||||
|
@ -492,52 +542,86 @@ func parseNeedle(input string) (identifierType int, needle string) {
|
|||
}
|
||||
|
||||
// LookUpIdentifiers attempts to return identifiers matching a pattern
|
||||
func (c *ScalewayCache) LookUpIdentifiers(needle string) ScalewayResolverResults {
|
||||
func (c *ScalewayCache) LookUpIdentifiers(needle string) (ScalewayResolverResults, error) {
|
||||
results := ScalewayResolverResults{}
|
||||
|
||||
identifierType, needle := parseNeedle(needle)
|
||||
|
||||
if identifierType&(IdentifierUnknown|IdentifierServer) > 0 {
|
||||
for _, result := range c.LookUpServers(needle, false) {
|
||||
entry := NewScalewayResolverResult(result.Identifier, result.Name, result.Arch, IdentifierServer)
|
||||
servers, err := c.LookUpServers(needle, false)
|
||||
if err != nil {
|
||||
return ScalewayResolverResults{}, err
|
||||
}
|
||||
for _, result := range servers {
|
||||
entry, err := NewScalewayResolverResult(result.Identifier, result.Name, result.Arch, IdentifierServer)
|
||||
if err != nil {
|
||||
return ScalewayResolverResults{}, err
|
||||
}
|
||||
entry.ComputeRankMatch(needle)
|
||||
results = append(results, entry)
|
||||
}
|
||||
}
|
||||
|
||||
if identifierType&(IdentifierUnknown|IdentifierImage) > 0 {
|
||||
for _, result := range c.LookUpImages(needle, false) {
|
||||
entry := NewScalewayResolverResult(result.Identifier, result.Name, result.Arch, IdentifierImage)
|
||||
images, err := c.LookUpImages(needle, false)
|
||||
if err != nil {
|
||||
return ScalewayResolverResults{}, err
|
||||
}
|
||||
for _, result := range images {
|
||||
entry, err := NewScalewayResolverResult(result.Identifier, result.Name, result.Arch, IdentifierImage)
|
||||
if err != nil {
|
||||
return ScalewayResolverResults{}, err
|
||||
}
|
||||
entry.ComputeRankMatch(needle)
|
||||
results = append(results, entry)
|
||||
}
|
||||
}
|
||||
|
||||
if identifierType&(IdentifierUnknown|IdentifierSnapshot) > 0 {
|
||||
for _, result := range c.LookUpSnapshots(needle, false) {
|
||||
entry := NewScalewayResolverResult(result.Identifier, result.Name, result.Arch, IdentifierSnapshot)
|
||||
snapshots, err := c.LookUpSnapshots(needle, false)
|
||||
if err != nil {
|
||||
return ScalewayResolverResults{}, err
|
||||
}
|
||||
for _, result := range snapshots {
|
||||
entry, err := NewScalewayResolverResult(result.Identifier, result.Name, result.Arch, IdentifierSnapshot)
|
||||
if err != nil {
|
||||
return ScalewayResolverResults{}, err
|
||||
}
|
||||
entry.ComputeRankMatch(needle)
|
||||
results = append(results, entry)
|
||||
}
|
||||
}
|
||||
|
||||
if identifierType&(IdentifierUnknown|IdentifierVolume) > 0 {
|
||||
for _, result := range c.LookUpVolumes(needle, false) {
|
||||
entry := NewScalewayResolverResult(result.Identifier, result.Name, result.Arch, IdentifierVolume)
|
||||
volumes, err := c.LookUpVolumes(needle, false)
|
||||
if err != nil {
|
||||
return ScalewayResolverResults{}, err
|
||||
}
|
||||
for _, result := range volumes {
|
||||
entry, err := NewScalewayResolverResult(result.Identifier, result.Name, result.Arch, IdentifierVolume)
|
||||
if err != nil {
|
||||
return ScalewayResolverResults{}, err
|
||||
}
|
||||
entry.ComputeRankMatch(needle)
|
||||
results = append(results, entry)
|
||||
}
|
||||
}
|
||||
|
||||
if identifierType&(IdentifierUnknown|IdentifierBootscript) > 0 {
|
||||
for _, result := range c.LookUpBootscripts(needle, false) {
|
||||
entry := NewScalewayResolverResult(result.Identifier, result.Name, result.Arch, IdentifierBootscript)
|
||||
bootscripts, err := c.LookUpBootscripts(needle, false)
|
||||
if err != nil {
|
||||
return ScalewayResolverResults{}, err
|
||||
}
|
||||
for _, result := range bootscripts {
|
||||
entry, err := NewScalewayResolverResult(result.Identifier, result.Name, result.Arch, IdentifierBootscript)
|
||||
if err != nil {
|
||||
return ScalewayResolverResults{}, err
|
||||
}
|
||||
entry.ComputeRankMatch(needle)
|
||||
results = append(results, entry)
|
||||
}
|
||||
}
|
||||
|
||||
return results
|
||||
return results, nil
|
||||
}
|
||||
|
||||
// InsertServer registers a server in the cache
|
||||
|
|
|
@ -32,18 +32,46 @@ type defaultLogger struct {
|
|||
}
|
||||
|
||||
func (l *defaultLogger) LogHTTP(r *http.Request) {
|
||||
l.Printf("%s %s\n", r.Method, r.URL.Path)
|
||||
l.Printf("%s %s\n", r.Method, r.URL.RawPath)
|
||||
}
|
||||
|
||||
func (l *defaultLogger) Fatalf(format string, v ...interface{}) {
|
||||
l.Printf("[FATAL] %s\n", fmt.Sprintf(format, v))
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
func (l *defaultLogger) Debugf(format string, v ...interface{}) {
|
||||
l.Printf("[DEBUG] %s\n", fmt.Sprintf(format, v))
|
||||
}
|
||||
|
||||
func (l *defaultLogger) Infof(format string, v ...interface{}) {
|
||||
l.Printf("[INFO ] %s\n", fmt.Sprintf(format, v))
|
||||
}
|
||||
|
||||
func (l *defaultLogger) Warnf(format string, v ...interface{}) {
|
||||
l.Printf("[WARN ] %s\n", fmt.Sprintf(format, v))
|
||||
}
|
||||
|
||||
type disableLogger struct {
|
||||
}
|
||||
|
||||
// NewDisableLogger returns a logger which is configured to do nothing
|
||||
func NewDisableLogger() Logger {
|
||||
return &disableLogger{}
|
||||
}
|
||||
|
||||
func (d *disableLogger) LogHTTP(r *http.Request) {
|
||||
}
|
||||
|
||||
func (d *disableLogger) Fatalf(format string, v ...interface{}) {
|
||||
panic(fmt.Sprintf(format, v))
|
||||
}
|
||||
|
||||
func (d *disableLogger) Debugf(format string, v ...interface{}) {
|
||||
}
|
||||
|
||||
func (d *disableLogger) Infof(format string, v ...interface{}) {
|
||||
}
|
||||
|
||||
func (d *disableLogger) Warnf(format string, v ...interface{}) {
|
||||
}
|
||||
|
|
|
@ -28,11 +28,18 @@ The following arguments are supported:
|
|||
* `name` - (Required) name of ARM server
|
||||
* `image` - (Required) base image of ARM server
|
||||
* `type` - (Required) type of ARM server
|
||||
* `bootscript` - (Optional) server bootscript
|
||||
* `tags` - (Optional) list of tags for server
|
||||
* `enable_ipv6` - (Optional) enable ipv6
|
||||
* `dynamic_ip_required` - (Optional) make server publicly available
|
||||
* `security_group` - (Optional) assign security group to server
|
||||
|
||||
Field `name`, `type` are editable.
|
||||
Field `name`, `type`, `tags`, `dynamic_ip_required`, `security_group` are editable.
|
||||
|
||||
## Attributes Reference
|
||||
|
||||
The following attributes are exported:
|
||||
|
||||
* `id` - id of the new resource
|
||||
* `private_ip` - private ip of the new resource
|
||||
* `public_ip` - public ip of the new resource
|
||||
|
|
Loading…
Reference in New Issue