-
Notifications
You must be signed in to change notification settings - Fork 154
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 support for notification configuration resource #86
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,205 @@ | ||
package tfe | ||
|
||
import ( | ||
"fmt" | ||
"log" | ||
|
||
tfe "github.com/hashicorp/go-tfe" | ||
"github.com/hashicorp/terraform/helper/schema" | ||
"github.com/hashicorp/terraform/helper/validation" | ||
) | ||
|
||
func resourceTFENotificationConfiguration() *schema.Resource { | ||
return &schema.Resource{ | ||
Create: resourceTFENotificationConfigurationCreate, | ||
Read: resourceTFENotificationConfigurationRead, | ||
Update: resourceTFENotificationConfigurationUpdate, | ||
Delete: resourceTFENotificationConfigurationDelete, | ||
Importer: &schema.ResourceImporter{ | ||
State: schema.ImportStatePassthrough, | ||
}, | ||
|
||
Schema: map[string]*schema.Schema{ | ||
"name": { | ||
Type: schema.TypeString, | ||
Required: true, | ||
}, | ||
|
||
"destination_type": { | ||
Type: schema.TypeString, | ||
Required: true, | ||
ForceNew: true, | ||
ValidateFunc: validation.StringInSlice( | ||
[]string{ | ||
string(tfe.NotificationDestinationTypeGeneric), | ||
string(tfe.NotificationDestinationTypeSlack), | ||
}, | ||
false, | ||
), | ||
}, | ||
|
||
"enabled": { | ||
Type: schema.TypeBool, | ||
Optional: true, | ||
Default: false, | ||
}, | ||
|
||
"token": { | ||
Type: schema.TypeString, | ||
Optional: true, | ||
Sensitive: true, | ||
}, | ||
|
||
"triggers": { | ||
Type: schema.TypeSet, | ||
Optional: true, | ||
Elem: &schema.Schema{ | ||
Type: schema.TypeString, | ||
ValidateFunc: validation.StringInSlice( | ||
[]string{ | ||
string(tfe.NotificationTriggerCreated), | ||
string(tfe.NotificationTriggerPlanning), | ||
string(tfe.NotificationTriggerNeedsAttention), | ||
string(tfe.NotificationTriggerApplying), | ||
string(tfe.NotificationTriggerCompleted), | ||
string(tfe.NotificationTriggerErrored), | ||
}, | ||
false, | ||
), | ||
}, | ||
}, | ||
|
||
"url": { | ||
Type: schema.TypeString, | ||
Required: true, | ||
}, | ||
|
||
"workspace_external_id": { | ||
Type: schema.TypeString, | ||
Required: true, | ||
ForceNew: true, | ||
}, | ||
}, | ||
} | ||
} | ||
|
||
func resourceTFENotificationConfigurationCreate(d *schema.ResourceData, meta interface{}) error { | ||
tfeClient := meta.(*tfe.Client) | ||
|
||
// Get workspace | ||
workspaceID := d.Get("workspace_external_id").(string) | ||
|
||
// Get attributes | ||
destinationType := tfe.NotificationDestinationType(d.Get("destination_type").(string)) | ||
enabled := d.Get("enabled").(bool) | ||
name := d.Get("name").(string) | ||
token := d.Get("token").(string) | ||
url := d.Get("url").(string) | ||
|
||
// Throw error if token is set with destinationType of slack | ||
if token != "" && destinationType == tfe.NotificationDestinationTypeSlack { | ||
return fmt.Errorf("Token cannot be set with destination_type of %s", destinationType) | ||
} | ||
|
||
// Create a new options struct | ||
options := tfe.NotificationConfigurationCreateOptions{ | ||
DestinationType: tfe.NotificationDestination(destinationType), | ||
Enabled: tfe.Bool(enabled), | ||
Name: tfe.String(name), | ||
Token: tfe.String(token), | ||
URL: tfe.String(url), | ||
} | ||
|
||
// Add triggers set to the options struct | ||
for _, trigger := range d.Get("triggers").(*schema.Set).List() { | ||
options.Triggers = append(options.Triggers, trigger.(string)) | ||
} | ||
|
||
log.Printf("[DEBUG] Create notification configuration: %s", name) | ||
notificationConfiguration, err := tfeClient.NotificationConfigurations.Create(ctx, workspaceID, options) | ||
if err != nil { | ||
return fmt.Errorf("Error creating notification configuration %s: %v", name, err) | ||
} | ||
|
||
d.SetId(notificationConfiguration.ID) | ||
|
||
return resourceTFENotificationConfigurationRead(d, meta) | ||
} | ||
|
||
func resourceTFENotificationConfigurationRead(d *schema.ResourceData, meta interface{}) error { | ||
tfeClient := meta.(*tfe.Client) | ||
|
||
log.Printf("[DEBUG] Read notification configuration: %s", d.Id()) | ||
notificationConfiguration, err := tfeClient.NotificationConfigurations.Read(ctx, d.Id()) | ||
if err != nil { | ||
if err == tfe.ErrResourceNotFound { | ||
log.Printf("[DEBUG] Notification configuration %s no longer exists", d.Id()) | ||
d.SetId("") | ||
return nil | ||
} | ||
return fmt.Errorf("Error reading notification configuration %s: %v", d.Id(), err) | ||
} | ||
|
||
// Update config | ||
d.Set("destination_type", notificationConfiguration.DestinationType) | ||
d.Set("enabled", notificationConfiguration.Enabled) | ||
d.Set("name", notificationConfiguration.Name) | ||
// Don't set token here, as it is write only | ||
// and setting it here would make it blank | ||
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. Thank you for adding this comment! I noticed that resources like 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 pieced this together after a lot of trial and error. It would be nice to get confirmation from someone who has done a lot of work with providers 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. @lafentres I think you're good here. 👍 If the value would be blank, then setting it here would for sure break the state and cause a diff. I'm wondering why we even have a field for this value though? That's not your fault, just curious why it would even be in the SDK 🤔 Unless the notification configuration is shared with other parts of the CRUD, but it doesn't seem to be. 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.
Good point. In this resource the token field should probably be omitted in the response type. In other resources (e.g. team token, organization token) its used to return a newly created token in the create response, so it adds value there. But here its a value that is provided by the caller, so its never used in any of the responses. |
||
d.Set("triggers", notificationConfiguration.Triggers) | ||
d.Set("url", notificationConfiguration.URL) | ||
|
||
return nil | ||
} | ||
|
||
func resourceTFENotificationConfigurationUpdate(d *schema.ResourceData, meta interface{}) error { | ||
tfeClient := meta.(*tfe.Client) | ||
|
||
// Get attributes | ||
destinationType := tfe.NotificationDestinationType(d.Get("destination_type").(string)) | ||
enabled := d.Get("enabled").(bool) | ||
name := d.Get("name").(string) | ||
token := d.Get("token").(string) | ||
url := d.Get("url").(string) | ||
|
||
// Throw error if token is set with destinationType of slack | ||
if token != "" && destinationType == tfe.NotificationDestinationTypeSlack { | ||
return fmt.Errorf("Token cannot be set with destination_type of %s", destinationType) | ||
} | ||
|
||
// Create a new options struct | ||
options := tfe.NotificationConfigurationUpdateOptions{ | ||
Enabled: tfe.Bool(enabled), | ||
Name: tfe.String(name), | ||
Token: tfe.String(token), | ||
URL: tfe.String(url), | ||
} | ||
|
||
// Add triggers set to the options struct | ||
for _, trigger := range d.Get("triggers").(*schema.Set).List() { | ||
options.Triggers = append(options.Triggers, trigger.(string)) | ||
} | ||
|
||
log.Printf("[DEBUG] Update notification configuration: %s", d.Id()) | ||
_, err := tfeClient.NotificationConfigurations.Update(ctx, d.Id(), options) | ||
if err != nil { | ||
return fmt.Errorf("Error updating notification configuration %s: %v", d.Id(), err) | ||
lafentres marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
|
||
return resourceTFENotificationConfigurationRead(d, meta) | ||
} | ||
|
||
func resourceTFENotificationConfigurationDelete(d *schema.ResourceData, meta interface{}) error { | ||
tfeClient := meta.(*tfe.Client) | ||
|
||
log.Printf("[DEBUG] Delete notification configuration: %s", d.Id()) | ||
err := tfeClient.NotificationConfigurations.Delete(ctx, d.Id()) | ||
if err != nil { | ||
if err == tfe.ErrResourceNotFound { | ||
return nil | ||
} | ||
return fmt.Errorf("Error deleting notification configuration %s: %v", d.Id(), err) | ||
} | ||
|
||
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.
@lafentres did this work in your testing? Depending on how hashicorp/terraform#22298 goes, this might need to be removed. Just FYI. Hopefully the primitive-element type case gets allowed though.
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.
Being able to do this (in a supported way) would be nice though.
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.
@vancluever, yep, this worked :) In addition to passing the tests I wrote, it also stopped me from putting in various invalid strings when I was creating notifications with Terraform locally.
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.
Weird. I'm asking @radeksimko to weigh in here as I'm not too sure how this is working given the restrictions in place. Maybe the acceptance testing suite is not using
InternalValidate
after all.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.
This works just fine because you are validating each element, not the whole set. In other words the
ValidateFunc
is associated withTypeString
, notTypeSet
.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.
@radeksimko what I'm wondering about is how it's passingInternalValidate
with the current code in place: https://github.com/hashicorp/terraform/blob/v0.12.6/helper/schema/schema.go#L831-L836Scratch that, I see that now. Good call and great application @lafentres!