diff --git a/docs/resources/logdna_key.md b/docs/resources/logdna_key.md index 2432650..b7ae16f 100644 --- a/docs/resources/logdna_key.md +++ b/docs/resources/logdna_key.md @@ -11,6 +11,7 @@ provider "logdna" { resource "logdna_key" "service-key" { type = "service" + name = "terraform-my_service_key" lifecycle { create_before_destroy = true @@ -19,6 +20,7 @@ resource "logdna_key" "service-key" { resource "logdna_key" "ingestion-key" { type = "ingestion" + name = "terraform-my_ingestion_key" lifecycle { create_before_destroy = true @@ -26,7 +28,9 @@ resource "logdna_key" "ingestion-key" { } ``` -The `create_before_destroy` and `lifecycle` meta-argument are not required, but ensure a valid key is always available so there's no disruption of service. +The `create_before_destroy` and `lifecycle` meta-argument are not required; however, these options ensure a valid key is always available when a key is being recreated. This helps avoid any disruptions in service. + +~> **NOTE:** We recommend prefixing the name of your terraform resources so they can be distinguished from other resources in the UI. ## Key Rotation @@ -41,7 +45,8 @@ $ terraform apply -replace="logdna_key.my_key" The following arguments are supported: -- `type`: **string** _(Required)_ The type of key to be used. Should be either `service` or `ingestion`. +- `type`: **string** _(Required)_ The type of key to be used. Can be one of either `service` or `ingestion`. +- `name`: **string** _(Optional)_ A non-unique name for the key. If not supplied, a default one is generated. ## Attributes Reference @@ -49,7 +54,8 @@ In addition to all the arguments above, the following attributes are exported: - `id`: **string** The unique identifier of this key. - `key`: **string** The actual key value. -- `type`: **string** The type of key. +- `name`: **string** The name of the key. +- `type`: **string** The type of key. Can be one of either `service` or `ingestion`. - `created`: **int** The date the key was created in Unix time milliseconds. ## Import diff --git a/logdna/request_types.go b/logdna/request_types.go index 12a4b60..0c010d6 100644 --- a/logdna/request_types.go +++ b/logdna/request_types.go @@ -50,6 +50,10 @@ type categoryRequest struct { Type string `json:"type,omitempty"` } +type keyRequest struct { + Name string `json:"name,omitempty"` +} + func (view *viewRequest) CreateRequestBody(d *schema.ResourceData) diag.Diagnostics { // This function pulls from the schema in preparation to JSON marshal var diags diag.Diagnostics @@ -85,11 +89,20 @@ func (alert *alertRequest) CreateRequestBody(d *schema.ResourceData) diag.Diagno return diags } -func (categoty *categoryRequest) CreateRequestBody(d *schema.ResourceData) diag.Diagnostics { +func (category *categoryRequest) CreateRequestBody(d *schema.ResourceData) diag.Diagnostics { + var diags diag.Diagnostics + + // Scalars + category.Name = d.Get("name").(string) + + return diags +} + +func (key *keyRequest) CreateRequestBody(d *schema.ResourceData) diag.Diagnostics { var diags diag.Diagnostics // Scalars - categoty.Name = d.Get("name").(string) + key.Name = d.Get("name").(string) return diags } diff --git a/logdna/resource_key.go b/logdna/resource_key.go index af30a51..bbd5088 100644 --- a/logdna/resource_key.go +++ b/logdna/resource_key.go @@ -8,18 +8,25 @@ import ( "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" ) func resourceKeyCreate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + var diags diag.Diagnostics pc := m.(*providerConfig) keyType := d.Get("type").(string) + key := keyRequest{} + + if diags = key.CreateRequestBody(d); diags.HasError() { + return diags + } req := newRequestConfig( pc, "POST", fmt.Sprintf("/v1/config/keys?type=%s", keyType), - nil, + key, ) body, err := req.MakeRequest() @@ -41,6 +48,35 @@ func resourceKeyCreate(ctx context.Context, d *schema.ResourceData, m interface{ return resourceKeyRead(ctx, d, m) } +func resourceKeyUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + var diags diag.Diagnostics + pc := m.(*providerConfig) + keyID := d.Id() + + key := keyRequest{} + if diags = key.CreateRequestBody(d); diags.HasError() { + return diags + } + + req := newRequestConfig( + pc, + "PUT", + fmt.Sprintf("/v1/config/keys/%s", keyID), + key, + ) + + body, err := req.MakeRequest() + log.Printf("[DEBUG] %s %s, payload is: %s", req.method, req.apiURL, body) + + if err != nil { + return diag.FromErr(err) + } + + log.Printf("[DEBUG] %s %s SUCCESS. Remote resource updated.", req.method, req.apiURL) + + return resourceKeyRead(ctx, d, m) +} + func resourceKeyRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { var diags diag.Diagnostics @@ -80,6 +116,7 @@ func resourceKeyRead(ctx context.Context, d *schema.ResourceData, m interface{}) // Top level keys can be set directly appendError(d.Set("type", key.Type), &diags) + appendError(d.Set("name", key.Name), &diags) appendError(d.Set("id", key.KeyID), &diags) appendError(d.Set("key", key.Key), &diags) appendError(d.Set("created", key.Created), &diags) @@ -112,6 +149,7 @@ func resourceKeyDelete(ctx context.Context, d *schema.ResourceData, m interface{ func resourceKey() *schema.Resource { return &schema.Resource{ CreateContext: resourceKeyCreate, + UpdateContext: resourceKeyUpdate, ReadContext: resourceKeyRead, DeleteContext: resourceKeyDelete, Importer: &schema.ResourceImporter{ @@ -120,9 +158,17 @@ func resourceKey() *schema.Resource { Schema: map[string]*schema.Schema{ "type": { + Type: schema.TypeString, + ForceNew: true, + Required: true, + ValidateFunc: validation.StringInSlice([]string{"ingestion", "service"}, false), + }, + "name": { Type: schema.TypeString, - ForceNew: true, - Required: true, + Optional: true, + DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool { + return new == "" + }, }, "id": { Type: schema.TypeString, diff --git a/logdna/resource_key_test.go b/logdna/resource_key_test.go index 81911e4..8887a03 100644 --- a/logdna/resource_key_test.go +++ b/logdna/resource_key_test.go @@ -32,7 +32,7 @@ func TestKey_ErrorResourceTypeInvalid(t *testing.T) { Steps: []resource.TestStep{ { Config: fmtTestConfigResource("key", "new", []string{serviceKey, apiHostUrl}, args, nilOpt, nilLst), - ExpectError: regexp.MustCompile("Error: POST .+?, status 400 NOT OK!"), + ExpectError: regexp.MustCompile(`Error: expected type to be one of \[ingestion service\], got incorrect`), }, }, }) @@ -41,10 +41,22 @@ func TestKey_ErrorResourceTypeInvalid(t *testing.T) { func TestKey_Basic(t *testing.T) { serviceArgs := map[string]string{ "type": `"service"`, + "name": `"my first name"`, + } + + serviceUpdateArgs := map[string]string{ + "type": `"service"`, + "name": `"my new name"`, } ingestionArgs := map[string]string{ "type": `"ingestion"`, + "name": `"my first name"`, + } + + ingestionUpdateArgs := map[string]string{ + "type": `"ingestion"`, + "name": `"my new name"`, } resource.Test(t, resource.TestCase{ @@ -56,6 +68,19 @@ func TestKey_Basic(t *testing.T) { Check: resource.ComposeTestCheckFunc( testResourceExists("key", "new-service-key"), resource.TestCheckResourceAttr("logdna_key.new-service-key", "type", strings.Replace(serviceArgs["type"], "\"", "", 2)), + resource.TestCheckResourceAttr("logdna_key.new-service-key", "name", strings.Replace(serviceArgs["name"], "\"", "", 2)), + resource.TestCheckResourceAttrSet("logdna_key.new-service-key", "id"), + resource.TestCheckResourceAttrSet("logdna_key.new-service-key", "key"), + resource.TestCheckResourceAttrSet("logdna_key.new-service-key", "created"), + ), + }, + { + // NOTE It tests a service key update operation + Config: fmtTestConfigResource("key", "new-service-key", []string{serviceKey, apiHostUrl}, serviceUpdateArgs, nilOpt, nilLst), + Check: resource.ComposeTestCheckFunc( + testResourceExists("key", "new-service-key"), + resource.TestCheckResourceAttr("logdna_key.new-service-key", "type", strings.Replace(serviceUpdateArgs["type"], "\"", "", 2)), + resource.TestCheckResourceAttr("logdna_key.new-service-key", "name", strings.Replace(serviceUpdateArgs["name"], "\"", "", 2)), resource.TestCheckResourceAttrSet("logdna_key.new-service-key", "id"), resource.TestCheckResourceAttrSet("logdna_key.new-service-key", "key"), resource.TestCheckResourceAttrSet("logdna_key.new-service-key", "created"), @@ -72,6 +97,19 @@ func TestKey_Basic(t *testing.T) { Check: resource.ComposeTestCheckFunc( testResourceExists("key", "new-ingestion-key"), resource.TestCheckResourceAttr("logdna_key.new-ingestion-key", "type", strings.Replace(ingestionArgs["type"], "\"", "", 2)), + resource.TestCheckResourceAttr("logdna_key.new-ingestion-key", "name", strings.Replace(ingestionArgs["name"], "\"", "", 2)), + resource.TestCheckResourceAttrSet("logdna_key.new-ingestion-key", "id"), + resource.TestCheckResourceAttrSet("logdna_key.new-ingestion-key", "key"), + resource.TestCheckResourceAttrSet("logdna_key.new-ingestion-key", "created"), + ), + }, + { + // NOTE It tests an ingestion key update operation + Config: fmtTestConfigResource("key", "new-ingestion-key", []string{serviceKey, apiHostUrl}, ingestionUpdateArgs, nilOpt, nilLst), + Check: resource.ComposeTestCheckFunc( + testResourceExists("key", "new-ingestion-key"), + resource.TestCheckResourceAttr("logdna_key.new-ingestion-key", "type", strings.Replace(ingestionUpdateArgs["type"], "\"", "", 2)), + resource.TestCheckResourceAttr("logdna_key.new-ingestion-key", "name", strings.Replace(ingestionUpdateArgs["name"], "\"", "", 2)), resource.TestCheckResourceAttrSet("logdna_key.new-ingestion-key", "id"), resource.TestCheckResourceAttrSet("logdna_key.new-ingestion-key", "key"), resource.TestCheckResourceAttrSet("logdna_key.new-ingestion-key", "created"), diff --git a/logdna/resource_view_test.go b/logdna/resource_view_test.go index ebf4f64..ba4c100 100644 --- a/logdna/resource_view_test.go +++ b/logdna/resource_view_test.go @@ -567,7 +567,7 @@ func TestView_PresetAlert(t *testing.T) { { Config: iniCfg, Check: resource.ComposeTestCheckFunc( - testResourceExists("view", "test-view"), + testResourceExists("view", "test_view"), resource.TestCheckResourceAttr("logdna_view.test_view", "name", "test"), resource.TestCheckResourceAttr("logdna_view.test_view", "query", "test"), resource.TestCheckResourceAttr("logdna_view.test_view", "apps.#", "2"), @@ -595,7 +595,7 @@ func TestView_PresetAlert(t *testing.T) { { Config: updCfg, Check: resource.ComposeTestCheckFunc( - testResourceExists("view", "test-view"), + testResourceExists("view", "test_view"), resource.TestCheckResourceAttr("logdna_view.test_view", "name", "test2"), resource.TestCheckResourceAttr("logdna_view.test_view", "query", "query2"), resource.TestCheckResourceAttr("logdna_view.test_view", "apps.0", "app3"), @@ -634,10 +634,10 @@ func TestView_ErrorsConflictPresetId(t *testing.T) { } rsArgs := cloneDefaults(rsDefaults["view"]) - rsArgs["apps"] = `["app1", "app2"]` - rsArgs["hosts"] = `["host1", "host2"]` - rsArgs["levels"] = `["fatal", "critical"]` - rsArgs["tags"] = `["tags1", "tags2"]` + rsArgs["apps"] = `["app1", "app2"]` + rsArgs["hosts"] = `["host1", "host2"]` + rsArgs["levels"] = `["fatal", "critical"]` + rsArgs["tags"] = `["tags1", "tags2"]` rsArgs["presetid"] = `"1q2w3e4r5t"` incCfg := fmtTestConfigResource("view", "test_view", globalPcArgs, rsArgs, chArgs, nilLst) @@ -646,7 +646,7 @@ func TestView_ErrorsConflictPresetId(t *testing.T) { Providers: testAccProviders, Steps: []resource.TestStep{ { - Config: incCfg, + Config: incCfg, ExpectError: regexp.MustCompile("Error: Conflicting configuration arguments"), }, }, diff --git a/logdna/response_types.go b/logdna/response_types.go index d39bd81..18d38f7 100644 --- a/logdna/response_types.go +++ b/logdna/response_types.go @@ -34,6 +34,7 @@ type alertResponse struct { type keyResponse struct { KeyID string `json:"id"` Key string `json:"key"` + Name string `json:"name"` Type string `json:"type"` Created int `json:"created,omitempty"` }