-
Notifications
You must be signed in to change notification settings - Fork 281
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
refactor service hook webhook resource, add acceptance tests and docs…
… for azuredevops_servicehook_webhook resource
- Loading branch information
Showing
5 changed files
with
323 additions
and
5 deletions.
There are no files selected for viewing
169 changes: 169 additions & 0 deletions
169
azuredevops/internal/acceptancetests/resource_servicehook_webhook_test.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,169 @@ | ||
// +build all resource_servicehook_webhook | ||
// +build !exclude_servicehook | ||
|
||
package acceptancetests | ||
|
||
import ( | ||
"fmt" | ||
"strings" | ||
"testing" | ||
|
||
"github.com/hashicorp/terraform-plugin-sdk/helper/resource" | ||
"github.com/microsoft/terraform-provider-azuredevops/azuredevops/internal/acceptancetests/testutils" | ||
) | ||
|
||
func TestAccServiceHookWebhook_basic(t *testing.T) { | ||
projectName := testutils.GenerateResourceName() | ||
eventType := "git.push" | ||
url := "https://webhooks.org" | ||
|
||
resourceType := "azuredevops_servicehook_webhook" | ||
tfSvcHookNode := resourceType + ".test" | ||
resource.ParallelTest(t, resource.TestCase{ | ||
PreCheck: func() { testutils.PreCheck(t, nil) }, | ||
Providers: testutils.GetProviders(), | ||
CheckDestroy: testutils.CheckServiceHookWebhookDestroyed(resourceType), | ||
Steps: []resource.TestStep{ | ||
{ | ||
Config: hclSvcHookWebhookResourceBasic(projectName, eventType, url), | ||
Check: resource.ComposeTestCheckFunc( | ||
testutils.CheckServiceHookWebhookExistsWithEventTypeAndUrl(tfSvcHookNode, eventType, url), | ||
resource.TestCheckResourceAttrSet(tfSvcHookNode, "project_id"), | ||
resource.TestCheckResourceAttr(tfSvcHookNode, "event_type", eventType), | ||
resource.TestCheckResourceAttr(tfSvcHookNode, "url", url), | ||
), | ||
}, | ||
}, | ||
}) | ||
} | ||
|
||
func TestAccServiceHookWebhook_complete(t *testing.T) { | ||
projectName := testutils.GenerateResourceName() | ||
repositoryName := testutils.GenerateResourceName() | ||
eventType := "git.push" | ||
url := "https://webhooks.org" | ||
basicAuth := BasicAuth{ | ||
Username: "some_username", | ||
Password: "some_password", | ||
} | ||
httpHeaders := map[string]string{ | ||
"Authorization": "Bearer bearing", | ||
"X-My-Secret-Token": "whatever", | ||
} | ||
|
||
resourceType := "azuredevops_servicehook_webhook" | ||
tfSvcHookNode := resourceType + ".test" | ||
resource.ParallelTest(t, resource.TestCase{ | ||
PreCheck: func() { testutils.PreCheck(t, nil) }, | ||
Providers: testutils.GetProviders(), | ||
CheckDestroy: testutils.CheckServiceHookWebhookDestroyed(resourceType), | ||
Steps: []resource.TestStep{ | ||
{ | ||
Config: hclSvcHookWebhookResourceComplete(projectName, repositoryName, eventType, url, basicAuth, httpHeaders), | ||
Check: resource.ComposeTestCheckFunc( | ||
testutils.CheckServiceHookWebhookExistsWithEventTypeAndUrl(tfSvcHookNode, eventType, url), | ||
resource.TestCheckResourceAttrSet(tfSvcHookNode, "project_id"), | ||
resource.TestCheckResourceAttr(tfSvcHookNode, "event_type", eventType), | ||
resource.TestCheckResourceAttr(tfSvcHookNode, "url", url), | ||
), | ||
}, | ||
}, | ||
}) | ||
} | ||
|
||
func TestAccServiceHookWebhook_update(t *testing.T) { | ||
projectName := testutils.GenerateResourceName() | ||
eventType := "git.push" | ||
url := "https://webhooks.org" | ||
|
||
updatedEventType := "git.pullrequest.created" | ||
updatedUrl := "https://webhooks2.org" | ||
|
||
resourceType := "azuredevops_servicehook_webhook" | ||
tfSvcHookNode := resourceType + ".test" | ||
resource.ParallelTest(t, resource.TestCase{ | ||
PreCheck: func() { testutils.PreCheck(t, nil) }, | ||
Providers: testutils.GetProviders(), | ||
CheckDestroy: testutils.CheckServiceHookWebhookDestroyed(resourceType), | ||
Steps: []resource.TestStep{ | ||
{ | ||
Config: hclSvcHookWebhookResourceBasic(projectName, eventType, url), | ||
Check: resource.ComposeTestCheckFunc( | ||
testutils.CheckServiceHookWebhookExistsWithEventTypeAndUrl(tfSvcHookNode, eventType, url), | ||
resource.TestCheckResourceAttrSet(tfSvcHookNode, "project_id"), | ||
resource.TestCheckResourceAttr(tfSvcHookNode, "event_type", eventType), | ||
resource.TestCheckResourceAttr(tfSvcHookNode, "url", url), | ||
), | ||
}, | ||
{ | ||
Config: hclSvcHookWebhookResourceUpdate(projectName, updatedEventType, updatedUrl), | ||
Check: resource.ComposeTestCheckFunc( | ||
testutils.CheckServiceHookWebhookExistsWithEventTypeAndUrl(tfSvcHookNode, updatedEventType, updatedUrl), | ||
resource.TestCheckResourceAttrSet(tfSvcHookNode, "project_id"), | ||
resource.TestCheckResourceAttr(tfSvcHookNode, "event_type", updatedEventType), | ||
resource.TestCheckResourceAttr(tfSvcHookNode, "url", updatedUrl), | ||
), | ||
}, | ||
}, | ||
}) | ||
} | ||
|
||
func hclSvcHookWebhookResourceBasic(projectName string, eventType string, url string) string { | ||
serviceHookResource := fmt.Sprintf(` | ||
resource "azuredevops_servicehook_webhook" "test" { | ||
project_id = azuredevops_project.project.id | ||
event_type = "%s" | ||
url = "%s" | ||
}`, eventType, url) | ||
|
||
projectResource := testutils.HclProjectResource(projectName) | ||
return fmt.Sprintf("%s\n%s", projectResource, serviceHookResource) | ||
} | ||
|
||
type BasicAuth struct { | ||
Username string | ||
Password string | ||
} | ||
|
||
func hclSvcHookWebhookResourceComplete(projectName string, repositoryName string, eventType string, url string, basicAuth BasicAuth, httpHeaders map[string]string) string { | ||
headers := []string{} | ||
for key, val := range httpHeaders { | ||
headers = append(headers, fmt.Sprintf("%s = \"%s\"", key, val)) | ||
} | ||
|
||
serviceHookResource := fmt.Sprintf(` | ||
resource "azuredevops_servicehook_webhook" "test" { | ||
project_id = azuredevops_project.project.id | ||
event_type = "%s" | ||
url = "%s" | ||
basic_auth { | ||
username = "%s" | ||
password = "%s" | ||
} | ||
filters = { | ||
repository = azuredevops_git_repository.repository.id | ||
} | ||
http_headers = { | ||
%s | ||
} | ||
}`, eventType, url, basicAuth.Username, basicAuth.Password, strings.Join(headers, "\n")) | ||
|
||
projectAndRepositoryResource := testutils.HclGitRepoResource(projectName, repositoryName, "Clean") | ||
return fmt.Sprintf("%s\n%s", projectAndRepositoryResource, serviceHookResource) | ||
} | ||
|
||
func hclSvcHookWebhookResourceUpdate(projectName string, eventType string, url string) string { | ||
serviceHookResource := fmt.Sprintf(` | ||
resource "azuredevops_servicehook_webhook" "test" { | ||
project_id = azuredevops_project.project.id | ||
event_type = "%s" | ||
url = "%s" | ||
}`, eventType, url) | ||
|
||
projectResource := testutils.HclProjectResource(projectName) | ||
return fmt.Sprintf("%s\n%s", projectResource, serviceHookResource) | ||
} |
69 changes: 69 additions & 0 deletions
69
azuredevops/internal/acceptancetests/testutils/servicehook.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
package testutils | ||
|
||
import ( | ||
"fmt" | ||
|
||
"github.com/google/uuid" | ||
"github.com/hashicorp/terraform-plugin-sdk/helper/resource" | ||
"github.com/hashicorp/terraform-plugin-sdk/terraform" | ||
"github.com/microsoft/azure-devops-go-api/azuredevops/servicehooks" | ||
"github.com/microsoft/terraform-provider-azuredevops/azuredevops/internal/client" | ||
) | ||
|
||
// CheckServiceHookWebhookExistsWithEventTypeAndUrl verifies that a service hook webhook exists in the state, | ||
// and that it has the expected event type and url when compared against the data in Azure DevOps. | ||
func CheckServiceHookWebhookExistsWithEventTypeAndUrl(tfNode string, expectedEventType string, expectedUrl string) resource.TestCheckFunc { | ||
return func(s *terraform.State) error { | ||
resourceState, ok := s.RootModule().Resources[tfNode] | ||
if !ok { | ||
return fmt.Errorf("Did not find a service hook webhook in the state") | ||
} | ||
|
||
subscription, err := getSvcHookWebhookFromState(resourceState) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
if *subscription.EventType != expectedEventType { | ||
return fmt.Errorf("Service Hook webhook has event type=%s, but expected event type=%s", *subscription.EventType, expectedEventType) | ||
} | ||
|
||
if (*subscription.ConsumerInputs)["url"] != expectedUrl { | ||
return fmt.Errorf("Service Hook webhook has url=%s, but expected url=%s", *subscription.Url, expectedUrl) | ||
} | ||
|
||
return nil | ||
} | ||
} | ||
|
||
// CheckServiceHookWebhookDestroyed verifies that all service hook webhoks the state are destroyed. | ||
// This will be invoked *after* terraform destroys the resource but *before* the state is wiped clean. | ||
func CheckServiceHookWebhookDestroyed(resourceType string) resource.TestCheckFunc { | ||
return func(s *terraform.State) error { | ||
for _, resource := range s.RootModule().Resources { | ||
if resource.Type != resourceType { | ||
continue | ||
} | ||
|
||
// indicates the resource exists - this should fail the test | ||
if _, err := getSvcHookWebhookFromState(resource); err == nil { | ||
return fmt.Errorf("Unexpectedly found a service hook webhook that should have been deleted") | ||
} | ||
} | ||
|
||
return nil | ||
} | ||
} | ||
|
||
// given a resource from the state, return a service hook webhook (and error) | ||
func getSvcHookWebhookFromState(resource *terraform.ResourceState) (*servicehooks.Subscription, error) { | ||
serviceHookWebhookDefID, err := uuid.Parse(resource.Primary.ID) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
clients := GetProvider().Meta().(*client.AggregatedClient) | ||
return clients.ServiceHooksClient.GetSubscription(clients.Ctx, servicehooks.GetSubscriptionArgs{ | ||
SubscriptionId: &serviceHookWebhookDefID, | ||
}) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
--- | ||
layout: "azuredevops" | ||
page_title: "AzureDevops: azuredevops_servicehook_webhook" | ||
description: |- | ||
Manages a Webhook service hook Azure DevOps organization. | ||
--- | ||
|
||
# azuredevops_servicehook_webhook (Resource) | ||
|
||
Manages a Webhook service hook Azure DevOps organization. | ||
|
||
## Example Usage | ||
|
||
```hcl | ||
resource "azuredevops_project" "project" { | ||
name = "Sample Project" | ||
visibility = "private" | ||
version_control = "Git" | ||
work_item_template = "Agile" | ||
} | ||
resource "azuredevops_git_repository" "repo" { | ||
project_id = azuredevops_project.project.id | ||
name = "Sample Empty Git Repository" | ||
initialization { | ||
init_type = "Clean" | ||
} | ||
} | ||
resource "azuredevops_servicehook_webhook" "webhook" { | ||
project_id = azuredevops_project.project.id | ||
event_type = "git.push" | ||
url = "https://my-webhooks.org" | ||
# optional | ||
basic_auth { | ||
username = "my_username" | ||
password = "my_password" | ||
} | ||
# optional | ||
filters = { | ||
repository = azuredevops_git_repository.repo.id | ||
} | ||
# optional | ||
http_headers = { | ||
Authorization = "Bearer bearing" | ||
X-My-Header = "header value" | ||
} | ||
} | ||
``` | ||
|
||
## Argument Reference | ||
|
||
The following arguments are supported: | ||
|
||
- `project_id` - (Required) The project ID or project name. | ||
- `event_type` - (Required) Event type. | ||
- `url` - (Required) The url of the hook to invoke. | ||
- `basic_auth` - (Optional) Basic authentication. | ||
- `username` | ||
- `password` | ||
- `filters` - (Optional) Filters that depend on event type. | ||
- `http_headers` - (Optional) HTTP headers included in request. | ||
|
||
## Attributes Reference | ||
|
||
The following attributes are exported: | ||
|
||
- `id` - The ID of the service hook webhook. | ||
- `project_id` - (Required) The project ID or project name. | ||
- `event_type` - (Required) Event type. | ||
- `url` - (Required) The url of the hook to invoke. | ||
|
||
## Relevant Links | ||
|
||
- [Azure DevOps Service REST API 5.1 - Service hooks](https://docs.microsoft.com/en-us/rest/api/azure/devops/hooks/?view=azure-devops-rest-5.1) |