-
Notifications
You must be signed in to change notification settings - Fork 136
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: implement name server resource for domains #669
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,220 @@ | ||
package ovh | ||
|
||
import ( | ||
"fmt" | ||
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" | ||
"github.com/ovh/go-ovh/ovh" | ||
"log" | ||
"strings" | ||
) | ||
|
||
type OvhDomainNameServerUpdate struct { | ||
NameServers []OvhDomainNameServer `json:"nameServers,omitempty"` | ||
} | ||
|
||
type OvhDomainNameServerUpdateResult struct { | ||
CanAccelerate bool `json:"canAccelerate,omitempty"` | ||
CanCancel bool `json:"canCancel,omitempty"` | ||
CanRelaunch bool `json:"canRelaunch,omitempty"` | ||
Comment string `json:"comment,omitempty"` | ||
CreationDate string `json:"creationDate,omitempty"` | ||
Domain string `json:"domain,omitempty"` | ||
DoneDate string `json:"doneDate,omitempty"` | ||
Function string `json:"function,omitempty"` | ||
Id int64 `json:"id,omitempty"` | ||
LastUpdate string `json:"lastUpdate,omitempty"` | ||
Status string `json:"status,omitempty"` | ||
TodoDate string `json:"todoDate,omitempty"` | ||
} | ||
|
||
type OvhDomainNameServer struct { | ||
Id int64 `json:"id,omitempty"` | ||
Host string `json:"host,omitempty"` | ||
Ip string `json:"ip,omitempty"` | ||
IsUsed bool `json:"isUsed,omitempty"` | ||
ToDelete bool `json:"toDelete,omitempty"` | ||
} | ||
|
||
type OvhDomainNameServers struct { | ||
Id string `json:"id,omitempty"` | ||
Domain string `json:"domain,omitempty"` | ||
Servers []OvhDomainNameServer `json:"servers,omitempty"` | ||
} | ||
|
||
func (r *OvhDomainNameServers) String() string { | ||
domains := make([]string, 0) | ||
for _, server := range r.Servers { | ||
domains = append(domains, fmt.Sprintf( | ||
"server[id: %v, host: %s, ip: %s, isUsed: %v, toDelete: %v]", | ||
server.Id, | ||
server.Host, | ||
server.Ip, | ||
server.IsUsed, | ||
server.ToDelete, | ||
)) | ||
} | ||
return fmt.Sprintf( | ||
"nameservers[id: %v, domain: %s, servers: [%s]]", | ||
r.Id, | ||
r.Domain, | ||
strings.Join(domains, ", "), | ||
) | ||
} | ||
|
||
func resourceOvhDomainNameServersImportState( | ||
d *schema.ResourceData, | ||
meta interface{}) ([]*schema.ResourceData, error) { | ||
givenId := d.Id() | ||
d.SetId(givenId) | ||
d.Set("domain", d.Id()) | ||
results := make([]*schema.ResourceData, 1) | ||
results[0] = d | ||
return results, nil | ||
} | ||
|
||
func resourceOvhDomainNameServers() *schema.Resource { | ||
return &schema.Resource{ | ||
Create: resourceOvhDomainNameServersCreate, | ||
Read: resourceOvhDomainNameServersRead, | ||
Update: resourceOvhDomainNameServersCreate, // Update is the same as create | ||
Delete: resourceOvhDomainNameServersDelete, | ||
Importer: &schema.ResourceImporter{ | ||
State: resourceOvhDomainNameServersImportState, | ||
}, | ||
|
||
Schema: map[string]*schema.Schema{ | ||
"domain": { | ||
Type: schema.TypeString, | ||
Required: true, | ||
ForceNew: true, | ||
}, | ||
"servers": { | ||
Type: schema.TypeList, | ||
Required: true, | ||
Elem: &schema.Resource{ | ||
Schema: map[string]*schema.Schema{ | ||
"host": { | ||
Type: schema.TypeString, | ||
Required: true, | ||
rbeuque74 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
}, | ||
"ip": { | ||
Type: schema.TypeString, | ||
Optional: true, | ||
Default: "", | ||
}, | ||
}, | ||
}, | ||
MinItems: 1, | ||
}, | ||
}, | ||
} | ||
} | ||
|
||
func resourceOvhDomainNameServersCreate(d *schema.ResourceData, meta interface{}) error { | ||
config := meta.(*Config) | ||
serviceName := d.Get("domain").(string) | ||
|
||
// Servers to put | ||
servers := &OvhDomainNameServerUpdate{ | ||
NameServers: make([]OvhDomainNameServer, 0), | ||
} | ||
// Loop due to rename of the field in the API | ||
for _, server := range d.Get("servers").([]interface{}) { | ||
s := server.(map[string]interface{}) | ||
servers.NameServers = append(servers.NameServers, OvhDomainNameServer{ | ||
Host: s["host"].(string), | ||
Ip: s["ip"].(string), | ||
}) | ||
} | ||
log.Printf("[DEBUG] OVH Record create configuration: %#v", servers) | ||
err := config.OVHClient.Post(fmt.Sprintf("/domain/%s/nameServers/update", url.PathEscape(serviceName)), servers, nil) | ||
|
||
if err != nil { | ||
return fmt.Errorf("failed to register OVH Nameservers: %s", err) | ||
} | ||
|
||
d.SetId(serviceName) | ||
|
||
return resourceOvhDomainNameServersRead(d, meta) | ||
} | ||
|
||
func resourceOvhDomainNameServersRead(d *schema.ResourceData, meta interface{}) error { | ||
config := meta.(*Config) | ||
|
||
record, err := ovhDomainNameServers(config.OVHClient, d) | ||
|
||
if err != nil { | ||
return err | ||
} | ||
|
||
if record == nil { | ||
return fmt.Errorf("domain %v has been deleted", d.Id()) | ||
} | ||
|
||
d.SetId(record.Id) | ||
d.Set("domain", record.Domain) | ||
d.Set("servers", flattenOvhDomainNameServers(record.Servers)) | ||
|
||
return nil | ||
} | ||
|
||
func flattenOvhDomainNameServers(servers []OvhDomainNameServer) []interface{} { | ||
result := make([]interface{}, 0) | ||
for _, server := range servers { | ||
result = append(result, map[string]interface{}{ | ||
"host": server.Host, | ||
"ip": server.Ip, | ||
}) | ||
} | ||
return result | ||
} | ||
|
||
func resourceOvhDomainNameServersDelete(d *schema.ResourceData, meta interface{}) error { | ||
// Note that NameServers can not be deleted, only updated | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. when deleting, shouldn't we call |
||
d.SetId("") | ||
return nil | ||
} | ||
|
||
func ovhDomainNameServers(client *ovh.Client, d *schema.ResourceData) (*OvhDomainNameServers, error) { | ||
domain := d.Get("domain").(string) | ||
nameServers := &OvhDomainNameServers{ | ||
Servers: make([]OvhDomainNameServer, 0), | ||
Domain: domain, | ||
Id: domain, | ||
} | ||
rec := &([]int{}) | ||
|
||
endpoint := fmt.Sprintf("/domain/%s/nameServer", domain) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. please |
||
|
||
err := client.Get(endpoint, rec) | ||
|
||
if err != nil { | ||
if err.(*ovh.APIError).Code == 404 { | ||
return nil, nil | ||
} | ||
return nil, err | ||
} | ||
|
||
// Read each name server | ||
for _, id := range *rec { | ||
server, err := ovhDomainNameServer(client, domain, id) | ||
if err != nil { | ||
return nil, err | ||
} | ||
nameServers.Servers = append(nameServers.Servers, *server) | ||
} | ||
|
||
return nameServers, nil | ||
} | ||
|
||
func ovhDomainNameServer(client *ovh.Client, domain string, id int) (*OvhDomainNameServer, error) { | ||
rec := &OvhDomainNameServer{} | ||
|
||
endpoint := fmt.Sprintf("/domain/%s/nameServer/%d", domain, id) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. same comment, to |
||
err := client.Get(endpoint, rec) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
return rec, nil | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
package ovh | ||
|
||
import ( | ||
"fmt" | ||
"github.com/ovh/go-ovh/ovh" | ||
"log" | ||
"os" | ||
"testing" | ||
|
||
"github.com/hashicorp/terraform-plugin-testing/helper/resource" | ||
) | ||
|
||
func init() { | ||
resource.AddTestSweepers("ovh_domain_name_servers", &resource.Sweeper{ | ||
Name: "ovh_domain_name_servers", | ||
F: testSweepOvhDomainNameServers, | ||
}) | ||
} | ||
|
||
func testSweepOvhDomainNameServers(region string) error { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this func seems useless as it doesn't remove anything, or am I missing something ? |
||
client, err := sharedClientForRegion(region) | ||
if err != nil { | ||
return fmt.Errorf("error getting client: %s", err) | ||
} | ||
|
||
domain := os.Getenv("OVH_DOMAIN_TEST") | ||
if domain == "" { | ||
log.Print("[DEBUG] OVH_DOMAIN_TEST is not set. No domain to sweep") | ||
return nil | ||
} | ||
|
||
// Check we have access to the domain | ||
err = client.Get(fmt.Sprintf("/domain/%s", domain), nil) | ||
|
||
if err != nil { | ||
if err.(*ovh.APIError).Code == 404 { | ||
log.Printf("[DEBUG] OVH domain %s does not exist. No domain to sweep", domain) | ||
return nil | ||
} | ||
return fmt.Errorf("error getting domain: %s", err) | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func TestAccDomainNameServers_Basic(t *testing.T) { | ||
domain := os.Getenv("OVH_DOMAIN_TEST") | ||
|
||
resource.Test(t, resource.TestCase{ | ||
PreCheck: func() { testAccPreCheckDomain(t) }, | ||
Providers: testAccProviders, | ||
Steps: []resource.TestStep{ | ||
// provider shall send an error if the TTL is less than 60 | ||
{ | ||
Config: testAccCheckOvhDomainNameServersConfig(domain, []string{"dns104.ovh.net", "ns104.ovh.net"}), | ||
Check: resource.ComposeTestCheckFunc( | ||
resource.TestCheckResourceAttr( | ||
"ovh_domain_name_servers.foobar", "domain", domain), | ||
), | ||
}, | ||
}, | ||
}) | ||
} | ||
|
||
func testAccCheckOvhDomainNameServersConfig(domain string, nameServers []string) string { | ||
return ` | ||
resource "ovh_domain_name_servers" "foobar" { | ||
domain = "` + domain + `" | ||
` + testAccCheckOvhDomainNameServersConfigNameServers(nameServers) + ` | ||
} | ||
` | ||
} | ||
|
||
func testAccCheckOvhDomainNameServersConfigNameServers(nameServers []string) string { | ||
var config string | ||
for _, nameServer := range nameServers { | ||
config += ` | ||
servers { | ||
host = "` + nameServer + `" | ||
} | ||
` | ||
} | ||
return config | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
could you place the structs in a file
type_domain.go
, to keep consistency with other resources ?