-
Notifications
You must be signed in to change notification settings - Fork 1
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
Add opsgenie destination resource #176
base: main
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,37 @@ | ||
--- | ||
# generated by https://github.com/hashicorp/terraform-plugin-docs | ||
page_title: "lightstep_opsgenie_destination Resource - terraform-provider-lightstep" | ||
subcategory: "" | ||
description: |- | ||
|
||
--- | ||
|
||
# lightstep_opsgenie_destination (Resource) | ||
|
||
|
||
|
||
|
||
|
||
<!-- schema generated by tfplugindocs --> | ||
## Schema | ||
|
||
### Required | ||
|
||
- `auth` (Block List, Min: 1, Max: 1) Basic auth used to authenticate with the Opsgenie instance (see [below for nested schema](#nestedblock--auth)) | ||
- `destination_name` (String) Name of the Opsgenie destination | ||
- `project_name` (String) | ||
- `url` (String) Opsgenie instance URL | ||
|
||
### Read-Only | ||
|
||
- `id` (String) The ID of this resource. | ||
|
||
<a id="nestedblock--auth"></a> | ||
### Nested Schema for `auth` | ||
|
||
Required: | ||
|
||
- `password` (String, Sensitive) | ||
- `username` (String) | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -94,3 +94,5 @@ resource "lightstep_user_role_binding" "proj_viewer" { | |
### Read-Only | ||
|
||
- `id` (String) The ID of this resource. | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,128 @@ | ||
package lightstep | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"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/lightstep/terraform-provider-lightstep/client" | ||
) | ||
|
||
func resourceOpsgenieDestination() *schema.Resource { | ||
return &schema.Resource{ | ||
CreateContext: resourceOpsgenieDestinationCreate, | ||
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. do you want to add a note for future readers about why there's no Update entry here? |
||
ReadContext: resourceDestinationRead, | ||
DeleteContext: resourceDestinationDelete, | ||
Importer: &schema.ResourceImporter{ | ||
StateContext: resourceOpsgenieDestinationImport, | ||
}, | ||
Schema: map[string]*schema.Schema{ | ||
"project_name": { | ||
Type: schema.TypeString, | ||
Required: true, | ||
ForceNew: true, | ||
}, | ||
"destination_name": { | ||
Type: schema.TypeString, | ||
Required: true, | ||
ForceNew: true, | ||
Description: "Name of the Opsgenie destination", | ||
}, | ||
"url": { | ||
Type: schema.TypeString, | ||
Required: true, | ||
ForceNew: true, | ||
Description: "Opsgenie instance URL", | ||
ValidateFunc: validation.IsURLWithScheme([]string{"https"}), | ||
}, | ||
"auth": { | ||
Type: schema.TypeList, | ||
MinItems: 1, | ||
MaxItems: 1, | ||
Required: true, | ||
ForceNew: true, | ||
Description: "Basic auth used to authenticate with the Opsgenie instance", | ||
Elem: &schema.Resource{ | ||
Schema: map[string]*schema.Schema{ | ||
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. could you add descriptions for these fields too? it's on my mind since I just went through and did it for a bunch of things |
||
"username": { | ||
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. I thought the username wasn't used with OpsGenie destination? |
||
Type: schema.TypeString, | ||
Required: true, | ||
ForceNew: true, | ||
}, | ||
"password": { | ||
Type: schema.TypeString, | ||
Sensitive: true, | ||
Required: true, | ||
ForceNew: true, | ||
}, | ||
}, | ||
}, | ||
}, | ||
}, | ||
} | ||
} | ||
|
||
func resourceOpsgenieDestinationCreate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { | ||
c := m.(*client.Client) | ||
attrs := client.OpsgenieAttributes{ | ||
Name: d.Get("destination_name").(string), | ||
DestinationType: "opsgenie", | ||
URL: d.Get("url").(string), | ||
} | ||
auth := d.Get("auth").([]interface{})[0].(map[string]interface{}) | ||
attrs.Auth = client.Auth{ | ||
Username: auth["username"].(string), | ||
Password: auth["password"].(string), | ||
} | ||
dest := client.Destination{ | ||
Type: "destination", | ||
Attributes: attrs, | ||
} | ||
|
||
destination, err := c.CreateDestination(ctx, d.Get("project_name").(string), dest) | ||
if err != nil { | ||
return diag.FromErr(fmt.Errorf("failed to create Opsgenie destination %v: %v", attrs.Name, err)) | ||
} | ||
|
||
d.SetId(destination.ID) | ||
return resourceDestinationRead(ctx, d, m) | ||
} | ||
|
||
func resourceOpsgenieDestinationImport(ctx context.Context, d *schema.ResourceData, m interface{}) ([]*schema.ResourceData, 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. ooc, how do we test this sort of thing? are the acceptance tests sufficient or do you have to actually try it using the terraform import command on a live system? |
||
c := m.(*client.Client) | ||
|
||
ids := strings.Split(d.Id(), ".") | ||
if len(ids) != 2 { | ||
return []*schema.ResourceData{}, fmt.Errorf("error importing lightstep_opsgenie_destination. Expecting an ID formed as '<lightstep_project>.<lightstep_destination_ID>'") | ||
} | ||
|
||
project, id := ids[0], ids[1] | ||
dest, err := c.GetDestination(ctx, project, id) | ||
if err != nil { | ||
return []*schema.ResourceData{}, fmt.Errorf("failed to get Opsgenie destination: %v", err) | ||
} | ||
|
||
d.SetId(dest.ID) | ||
if err := d.Set("project_name", project); err != nil { | ||
return []*schema.ResourceData{}, fmt.Errorf("unable to set project_name resource field: %v", err) | ||
} | ||
|
||
attributes := dest.Attributes.(map[string]interface{}) | ||
if err := d.Set("destination_name", attributes["name"]); err != nil { | ||
return []*schema.ResourceData{}, fmt.Errorf("unable to set destination_name resource field: %v", err) | ||
} | ||
|
||
if err := d.Set("url", attributes["url"]); err != nil { | ||
return []*schema.ResourceData{}, fmt.Errorf("unable to set url resource field: %v", err) | ||
} | ||
|
||
if err := d.Set("auth", []interface{}{attributes["auth"]}); err != nil { | ||
return []*schema.ResourceData{}, fmt.Errorf("unable to set auth resource field: %v", err) | ||
} | ||
|
||
return []*schema.ResourceData{d}, nil | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,150 @@ | ||
package lightstep | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"regexp" | ||
"testing" | ||
|
||
"github.com/lightstep/terraform-provider-lightstep/client" | ||
|
||
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" | ||
"github.com/hashicorp/terraform-plugin-sdk/v2/terraform" | ||
) | ||
|
||
func TestAccOpsgenieDestination(t *testing.T) { | ||
var destination client.Destination | ||
|
||
missingUrlConfig := ` | ||
resource "lightstep_opsgenie_destination" "missing_url" { | ||
project_name = ` + fmt.Sprintf("\"%s\"", testProject) + ` | ||
destination_name = "my-destination" | ||
auth { | ||
username = "" | ||
password = "pass123" | ||
} | ||
} | ||
` | ||
missingAuthConfig := ` | ||
resource "lightstep_opsgenie_destination" "missing_auth" { | ||
project_name = ` + fmt.Sprintf("\"%s\"", testProject) + ` | ||
destination_name = "my-destination" | ||
url = "https://example.com" | ||
} | ||
` | ||
|
||
destinationConfig := ` | ||
resource "lightstep_opsgenie_destination" "opsgenie" { | ||
project_name = ` + fmt.Sprintf("\"%s\"", testProject) + ` | ||
destination_name = "my-destination" | ||
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. nit: just to make this more realistic, maybe add some spaces in the name? |
||
url = "https://example.com" | ||
auth { | ||
username = "" | ||
password = "pass123" | ||
} | ||
} | ||
` | ||
resource.Test(t, resource.TestCase{ | ||
PreCheck: func() { testAccPreCheck(t) }, | ||
Providers: testAccProviders, | ||
CheckDestroy: testAccOpsgenieDestinationDestroy, | ||
Steps: []resource.TestStep{ | ||
{ | ||
Config: missingUrlConfig, | ||
Check: resource.ComposeTestCheckFunc( | ||
testAccCheckOpsgenieDestinationExists("lightstep_opsgenie_destination.missing_url", &destination), | ||
), | ||
ExpectError: regexp.MustCompile("The argument \"url\" is required, but no definition was found."), | ||
}, | ||
{ | ||
Config: missingAuthConfig, | ||
Check: resource.ComposeTestCheckFunc( | ||
testAccCheckOpsgenieDestinationExists("lightstep_opsgenie_destination.missing_auth", &destination), | ||
), | ||
ExpectError: regexp.MustCompile("Insufficient auth blocks"), | ||
}, | ||
{ | ||
Config: destinationConfig, | ||
Check: resource.ComposeTestCheckFunc( | ||
testAccCheckWebhookDestinationExists("lightstep_opsgenie_destination.opsgenie", &destination), | ||
resource.TestCheckResourceAttr("lightstep_opsgenie_destination.opsgenie", "destination_name", "my-destination"), | ||
resource.TestCheckResourceAttr("lightstep_opsgenie_destination.opsgenie", "url", "https://example.com"), | ||
resource.TestCheckResourceAttr("lightstep_opsgenie_destination.opsgenie", "auth.0.username", ""), | ||
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. what happens if they do specify a username? is that an option we even want to expose if it will never be needed? 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. I waffled on this. On one hand opsgenie is accepting a basic auth token so it makes sense to support username if they ever start using it. On the other hand we could hide this from the user in terraform just like we do in the UI. (It wouldn't be hidden in the public API though. 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. Do you have a preference? 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. I'd vote to hide it in terraform, assuming that doesn't create more work for you, in the interest of making terraform as minimally confusing as possible. |
||
resource.TestCheckResourceAttr("lightstep_opsgenie_destination.opsgenie", "auth.0.password", "pass123"), | ||
), | ||
}, | ||
}, | ||
}) | ||
|
||
} | ||
|
||
func TestAccOpsgenieDestinationImport(t *testing.T) { | ||
resource.Test(t, resource.TestCase{ | ||
PreCheck: func() { testAccPreCheck(t) }, | ||
Providers: testAccProviders, | ||
Steps: []resource.TestStep{ | ||
{ | ||
Config: ` | ||
resource "lightstep_opsgenie_destination" "opsgenie" { | ||
project_name = ` + fmt.Sprintf("\"%s\"", testProject) + ` | ||
destination_name = "do-not-delete-opsgenie" | ||
url = "https://example.com" | ||
auth { | ||
username = "" | ||
password = "pass123" | ||
} | ||
} | ||
`, | ||
}, | ||
{ | ||
ResourceName: "lightstep_opsgenie_destination.opsgenie", | ||
ImportState: true, | ||
ImportStateVerify: true, | ||
ImportStateVerifyIgnore: []string{"auth.0.password"}, | ||
ImportStateIdPrefix: fmt.Sprintf("%s.", testProject), | ||
}, | ||
}, | ||
}) | ||
} | ||
|
||
func testAccCheckOpsgenieDestinationExists(resourceName string, destination *client.Destination) resource.TestCheckFunc { | ||
return func(s *terraform.State) error { | ||
// get destination from TF state | ||
tfDestination, ok := s.RootModule().Resources[resourceName] | ||
if !ok { | ||
return fmt.Errorf("not found: %s", resourceName) | ||
} | ||
|
||
if tfDestination.Primary.ID == "" { | ||
return fmt.Errorf("id is not set") | ||
} | ||
|
||
// get destination from LS | ||
client := testAccProvider.Meta().(*client.Client) | ||
d, err := client.GetDestination(context.Background(), testProject, tfDestination.Primary.ID) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
destination = d | ||
return nil | ||
} | ||
} | ||
|
||
func testAccOpsgenieDestinationDestroy(s *terraform.State) error { | ||
conn := testAccProvider.Meta().(*client.Client) | ||
for _, resource := range s.RootModule().Resources { | ||
if resource.Type != "lightstep_opsgenie_destination" { | ||
continue | ||
} | ||
|
||
s, err := conn.GetDestination(context.Background(), testProject, resource.Primary.ID) | ||
if err == nil { | ||
if s.ID == resource.Primary.ID { | ||
return fmt.Errorf("destination with ID (%v) still exists.", resource.Primary.ID) | ||
} | ||
} | ||
|
||
} | ||
return nil | ||
} |
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.
thank you! I didn't realize this was already set automatically, I had just made an env variable