diff --git a/example/V2Changes.md b/example/V2Changes.md index cb7849bf..12b16d0d 100644 --- a/example/V2Changes.md +++ b/example/V2Changes.md @@ -16,6 +16,7 @@ The v2 Vultr Terraform plugin is backed by our new [V2 API](https://www.vultr.co - Startup Scripts - SSH Keys - Firewall Groups +- Container Registry #### 2. Newly created resources will return a uuid and you must use uuids to interact with the v2 plugin - Data Sources, when filtered by ID or any other resource ID, must use the uuid of the resource diff --git a/example/main.tf b/example/main.tf index 381ac152..e084ab2b 100644 --- a/example/main.tf +++ b/example/main.tf @@ -43,8 +43,8 @@ resource "vultr_firewall_rule" "tcp" { } resource "vultr_dns_domain" "my_domain" { - domain = "tf-domain.com" - ip = vultr_instance.my_instance.main_ip + domain = "tf-domain.com" + ip = vultr_instance.my_instance.main_ip } resource "vultr_dns_record" "a-record" { @@ -78,3 +78,10 @@ resource "vultr_load_balancer" "lb" { } } + +resource "vultr_container_registry" "vcr1" { + name = "im6hvcr1" + region = "sjc" + plan = "start_up" + public = false +} diff --git a/example/outputs.tf b/example/outputs.tf index 3dbbad2f..dda420b7 100644 --- a/example/outputs.tf +++ b/example/outputs.tf @@ -4,4 +4,4 @@ output "ip" { output "fwg_id" { value = vultr_firewall_group.fwg.id -} \ No newline at end of file +} diff --git a/vultr/provider.go b/vultr/provider.go index 2dd2d6e7..9287656c 100644 --- a/vultr/provider.go +++ b/vultr/provider.go @@ -90,6 +90,7 @@ func Provider() *schema.Provider { "vultr_user": resourceVultrUsers(), "vultr_vpc": resourceVultrVPC(), "vultr_vpc2": resourceVultrVPC2(), + "vultr_container_registry": resourceVultrContainerRegistry(), }, ConfigureFunc: providerConfigure, diff --git a/vultr/resource_vultr_container_registry.go b/vultr/resource_vultr_container_registry.go new file mode 100644 index 00000000..50b4bce3 --- /dev/null +++ b/vultr/resource_vultr_container_registry.go @@ -0,0 +1,181 @@ +package vultr + +import ( + "context" + "fmt" + "log" + "strconv" + "strings" + + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" + "github.com/vultr/govultr/v3" +) + +func resourceVultrContainerRegistry() *schema.Resource { + return &schema.Resource{ + CreateContext: resourceVultrContainerRegistryCreate, + ReadContext: resourceVultrContainerRegistryRead, + UpdateContext: resourceVultrContainerRegistryUpdate, + DeleteContext: resourceVultrContainerRegistryDelete, + Importer: &schema.ResourceImporter{ + StateContext: schema.ImportStatePassthroughContext, + }, + + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + }, + "plan": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{"start_up", "business", "premium", "enterprise"}, false), + }, + "region": { + Type: schema.TypeString, + Required: true, + }, + "public": { + Type: schema.TypeBool, + Required: true, + }, + "urn": { + Type: schema.TypeString, + Computed: true, + Optional: false, + }, + "storage": { + Type: schema.TypeMap, + Computed: true, + Optional: false, + }, + "root_user": { + Type: schema.TypeMap, + Computed: true, + Optional: false, + }, + "date_created": { + Type: schema.TypeString, + Computed: true, + Optional: false, + }, + }, + } +} + +func resourceVultrContainerRegistryCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + client := meta.(*Client).govultrClient() + + crReq := &govultr.ContainerRegistryReq{ + Name: strings.ToLower(d.Get("name").(string)), + Region: d.Get("region").(string), + Public: d.Get("public").(bool), + Plan: d.Get("plan").(string), + } + + cr, _, err := client.ContainerRegistry.Create(ctx, crReq) + if err != nil { + return diag.Errorf("error creating container registry: %v", err) + } + + d.SetId(cr.ID) + log.Printf("[INFO] Container Registry ID: %s", d.Id()) + + return resourceVultrContainerRegistryRead(ctx, d, meta) +} + +func resourceVultrContainerRegistryRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + client := meta.(*Client).govultrClient() + + cr, _, err := client.ContainerRegistry.Get(ctx, d.Id()) + if err != nil { + if strings.Contains(err.Error(), "Invalid container registry ID") { + log.Printf("[WARN] Vultr container registry (%s) not found", d.Id()) + d.SetId("") + return nil + } + return diag.Errorf("error getting container registry: %v", err) + } + + if err := d.Set("name", cr.Name); err != nil { + return diag.Errorf("unable to set resource container registry `name` read value: %v", err) + } + if err := d.Set("urn", cr.URN); err != nil { + return diag.Errorf("unable to set resource container registry `urn` read value: %v", err) + } + if err := d.Set("storage", flattenCRStorage(cr)); err != nil { + return diag.Errorf("unable to set resource container registry `storage` read value: %v", err) + } + if err := d.Set("root_user", flattenCRRootUser(cr)); err != nil { + return diag.Errorf("unable to set resource container registry `root_user` read value: %v", err) + } + if err := d.Set("public", cr.Public); err != nil { + return diag.Errorf("unable to set resource container `public` read value: %v", err) + } + if err := d.Set("date_created", cr.DateCreated); err != nil { + return diag.Errorf("unable to set resource container `date_created` read value: %v", err) + } + + return nil +} + +func resourceVultrContainerRegistryUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + client := meta.(*Client).govultrClient() + + vcr := &govultr.ContainerRegistryUpdateReq{} + + if d.HasChange("plan") { + vcr.Plan = govultr.StringToStringPtr(d.Get("plan").(string)) + } + + if d.HasChange("public") { + vcr.Public = govultr.BoolToBoolPtr(d.Get("public").(bool)) + } + + log.Printf("[INFO] Updating container registry: %s", d.Id()) + + cr, _, err := client.ContainerRegistry.Update(ctx, d.Id(), vcr) + if err != nil { + return diag.Errorf("error updating container registry: %v", err) + } + + d.SetId(cr.ID) + log.Printf("[INFO] Container Registry ID: %s", d.Id()) + + return resourceVultrContainerRegistryRead(ctx, d, meta) +} + +func resourceVultrContainerRegistryDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + client := meta.(*Client).govultrClient() + log.Printf("[INFO] Deleting container: %s", d.Id()) + + err := client.ContainerRegistry.Delete(ctx, d.Id()) + + if err != nil { + return diag.Errorf("error destroying Container Registry (%s): %v", d.Id(), err) + } + + return nil +} + +func flattenCRStorage(cr *govultr.ContainerRegistry) map[string]interface{} { + f := map[string]interface{}{ + "allowed": fmt.Sprintf("%.2f GB", cr.Storage.Allowed.GigaBytes), + "used": fmt.Sprintf("%.2f GB", cr.Storage.Used.GigaBytes), + } + return f +} + +func flattenCRRootUser(cr *govultr.ContainerRegistry) map[string]interface{} { + f := map[string]interface{}{ + "id": strconv.Itoa(cr.RootUser.ID), + "username": cr.RootUser.UserName, + "password": cr.RootUser.Password, + "root": strconv.FormatBool(cr.RootUser.Root), + "date_created": cr.RootUser.DateCreated, + "date_modified": cr.RootUser.DateModified, + } + return f +} diff --git a/vultr/resource_vultr_container_registry_test.go b/vultr/resource_vultr_container_registry_test.go new file mode 100644 index 00000000..150f7070 --- /dev/null +++ b/vultr/resource_vultr_container_registry_test.go @@ -0,0 +1,60 @@ +package vultr + +import ( + "context" + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" +) + +func TestAccVultrContainerRegistry(t *testing.T) { + crName := acctest.RandomWithPrefix("tf-cr-cr") + + name := "vultr_container_registry.foo" + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + CheckDestroy: testAccCheckVultrContainerRegistryDestroy, + ProviderFactories: testAccProviderFactories, + Steps: []resource.TestStep{ + { + Config: testAccVultrContainerRegistryBase(crName), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(name, "name", crName), + resource.TestCheckResourceAttrSet(name, "plan"), + resource.TestCheckResourceAttrSet(name, "region"), + resource.TestCheckResourceAttrSet(name, "public"), + ), + }, + }, + }) +} + +func testAccCheckVultrContainerRegistryDestroy(s *terraform.State) error { + for _, rs := range s.RootModule().Resources { + if rs.Type != "vultr_container_registry" { + continue + } + + crID := rs.Primary.ID + client := testAccProvider.Meta().(*Client).govultrClient() + + _, _, err := client.ContainerRegistry.Get(context.Background(), crID) + if err == nil { + return fmt.Errorf("vpc still exists: %s", crID) + } + } + return nil +} + +func testAccVultrContainerRegistryBase(name string) string { + return fmt.Sprintf(` + resource "vultr_container_registry" "foo" { + name = "%s" + region = "alt" + public = true + plan = "start_up" + } `, name) +}