provider/gitlab: add gitlab_project_hook resource

Here we add a new resource type `gitlab_project_hook`.  It allows for
management of custom hooks for a gitlab project.

This is a relatively simple resource as a project hook is a simple
association between a project, and a url to hit when one of the flagged
events occurs on that project.

Hooks (called Webhooks in some user documentation, but simply Hooks
in the api documentation) are covered here for users
https://docs.gitlab.com/ce/user/project/integrations/webhooks.html and
in the API documentation at
https://docs.gitlab.com/ce/api/projects.html#hooks
This commit is contained in:
Richard Clamp 2017-04-28 11:15:10 +01:00
parent ece4b13305
commit dcbe8a4736
4 changed files with 466 additions and 1 deletions

View File

@ -25,7 +25,8 @@ func Provider() terraform.ResourceProvider {
},
},
ResourcesMap: map[string]*schema.Resource{
"gitlab_project": resourceGitlabProject(),
"gitlab_project": resourceGitlabProject(),
"gitlab_project_hook": resourceGitlabProjectHook(),
},
ConfigureFunc: providerConfigure,

View File

@ -0,0 +1,185 @@
package gitlab
import (
"fmt"
"log"
"strconv"
"github.com/hashicorp/terraform/helper/schema"
gitlab "github.com/xanzy/go-gitlab"
)
func resourceGitlabProjectHook() *schema.Resource {
return &schema.Resource{
Create: resourceGitlabProjectHookCreate,
Read: resourceGitlabProjectHookRead,
Update: resourceGitlabProjectHookUpdate,
Delete: resourceGitlabProjectHookDelete,
Schema: map[string]*schema.Schema{
"project": {
Type: schema.TypeString,
Required: true,
},
"url": {
Type: schema.TypeString,
Required: true,
},
"token": {
Type: schema.TypeString,
Optional: true,
Sensitive: true,
},
"push_events": {
Type: schema.TypeBool,
Optional: true,
Default: true,
},
"issues_events": {
Type: schema.TypeBool,
Optional: true,
},
"merge_requests_events": {
Type: schema.TypeBool,
Optional: true,
},
"tag_push_events": {
Type: schema.TypeBool,
Optional: true,
},
"note_events": {
Type: schema.TypeBool,
Optional: true,
},
"build_events": {
Type: schema.TypeBool,
Optional: true,
},
"pipeline_events": {
Type: schema.TypeBool,
Optional: true,
},
"wiki_page_events": {
Type: schema.TypeBool,
Optional: true,
},
"enable_ssl_verification": {
Type: schema.TypeBool,
Optional: true,
Default: true,
},
},
}
}
func resourceGitlabProjectHookCreate(d *schema.ResourceData, meta interface{}) error {
client := meta.(*gitlab.Client)
project := d.Get("project").(string)
options := &gitlab.AddProjectHookOptions{
URL: gitlab.String(d.Get("url").(string)),
PushEvents: gitlab.Bool(d.Get("push_events").(bool)),
IssuesEvents: gitlab.Bool(d.Get("issues_events").(bool)),
MergeRequestsEvents: gitlab.Bool(d.Get("merge_requests_events").(bool)),
TagPushEvents: gitlab.Bool(d.Get("tag_push_events").(bool)),
NoteEvents: gitlab.Bool(d.Get("note_events").(bool)),
BuildEvents: gitlab.Bool(d.Get("build_events").(bool)),
PipelineEvents: gitlab.Bool(d.Get("pipeline_events").(bool)),
WikiPageEvents: gitlab.Bool(d.Get("wiki_page_events").(bool)),
EnableSSLVerification: gitlab.Bool(d.Get("enable_ssl_verification").(bool)),
}
if v, ok := d.GetOk("token"); ok {
options.Token = gitlab.String(v.(string))
}
log.Printf("[DEBUG] create gitlab project hook %q", options.URL)
hook, _, err := client.Projects.AddProjectHook(project, options)
if err != nil {
return err
}
d.SetId(fmt.Sprintf("%d", hook.ID))
return resourceGitlabProjectHookRead(d, meta)
}
func resourceGitlabProjectHookRead(d *schema.ResourceData, meta interface{}) error {
client := meta.(*gitlab.Client)
project := d.Get("project").(string)
hookId, err := strconv.Atoi(d.Id())
if err != nil {
return err
}
log.Printf("[DEBUG] read gitlab project hook %s/%d", project, hookId)
hook, response, err := client.Projects.GetProjectHook(project, hookId)
if err != nil {
if response.StatusCode == 404 {
log.Printf("[WARN] removing project hook %d from state because it no longer exists in gitlab", hookId)
d.SetId("")
return nil
}
return err
}
d.Set("url", hook.URL)
d.Set("push_events", hook.PushEvents)
d.Set("issues_events", hook.IssuesEvents)
d.Set("merge_requests_events", hook.MergeRequestsEvents)
d.Set("tag_push_events", hook.TagPushEvents)
d.Set("note_events", hook.NoteEvents)
d.Set("build_events", hook.BuildEvents)
d.Set("pipeline_events", hook.PipelineEvents)
d.Set("wiki_page_events", hook.WikiPageEvents)
d.Set("enable_ssl_verification", hook.EnableSSLVerification)
return nil
}
func resourceGitlabProjectHookUpdate(d *schema.ResourceData, meta interface{}) error {
client := meta.(*gitlab.Client)
project := d.Get("project").(string)
hookId, err := strconv.Atoi(d.Id())
if err != nil {
return err
}
options := &gitlab.EditProjectHookOptions{
URL: gitlab.String(d.Get("url").(string)),
PushEvents: gitlab.Bool(d.Get("push_events").(bool)),
IssuesEvents: gitlab.Bool(d.Get("issues_events").(bool)),
MergeRequestsEvents: gitlab.Bool(d.Get("merge_requests_events").(bool)),
TagPushEvents: gitlab.Bool(d.Get("tag_push_events").(bool)),
NoteEvents: gitlab.Bool(d.Get("note_events").(bool)),
BuildEvents: gitlab.Bool(d.Get("build_events").(bool)),
PipelineEvents: gitlab.Bool(d.Get("pipeline_events").(bool)),
WikiPageEvents: gitlab.Bool(d.Get("wiki_page_events").(bool)),
EnableSSLVerification: gitlab.Bool(d.Get("enable_ssl_verification").(bool)),
}
if d.HasChange("token") {
options.Token = gitlab.String(d.Get("token").(string))
}
log.Printf("[DEBUG] update gitlab project hook %s", d.Id())
_, _, err = client.Projects.EditProjectHook(project, hookId, options)
if err != nil {
return err
}
return resourceGitlabProjectHookRead(d, meta)
}
func resourceGitlabProjectHookDelete(d *schema.ResourceData, meta interface{}) error {
client := meta.(*gitlab.Client)
project := d.Get("project").(string)
hookId, err := strconv.Atoi(d.Id())
if err != nil {
return err
}
log.Printf("[DEBUG] Delete gitlab project hook %s", d.Id())
_, err = client.Projects.DeleteProjectHook(project, hookId)
return err
}

View File

@ -0,0 +1,220 @@
package gitlab
import (
"fmt"
"strconv"
"testing"
"github.com/hashicorp/terraform/helper/acctest"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/terraform"
"github.com/xanzy/go-gitlab"
)
func TestAccGitlabProjectHook_basic(t *testing.T) {
var hook gitlab.ProjectHook
rInt := acctest.RandInt()
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckGitlabProjectHookDestroy,
Steps: []resource.TestStep{
// Create a project and hook with default options
{
Config: testAccGitlabProjectHookConfig(rInt),
Check: resource.ComposeTestCheckFunc(
testAccCheckGitlabProjectHookExists("gitlab_project_hook.foo", &hook),
testAccCheckGitlabProjectHookAttributes(&hook, &testAccGitlabProjectHookExpectedAttributes{
URL: fmt.Sprintf("https://example.com/hook-%d", rInt),
PushEvents: true,
EnableSSLVerification: true,
}),
),
},
// Update the project hook to toggle all the values to their inverse
{
Config: testAccGitlabProjectHookUpdateConfig(rInt),
Check: resource.ComposeTestCheckFunc(
testAccCheckGitlabProjectHookExists("gitlab_project_hook.foo", &hook),
testAccCheckGitlabProjectHookAttributes(&hook, &testAccGitlabProjectHookExpectedAttributes{
URL: fmt.Sprintf("https://example.com/hook-%d", rInt),
PushEvents: false,
IssuesEvents: true,
MergeRequestsEvents: true,
TagPushEvents: true,
NoteEvents: true,
BuildEvents: true,
PipelineEvents: true,
WikiPageEvents: true,
EnableSSLVerification: false,
}),
),
},
// Update the project hook to toggle the options back
{
Config: testAccGitlabProjectHookConfig(rInt),
Check: resource.ComposeTestCheckFunc(
testAccCheckGitlabProjectHookExists("gitlab_project_hook.foo", &hook),
testAccCheckGitlabProjectHookAttributes(&hook, &testAccGitlabProjectHookExpectedAttributes{
URL: fmt.Sprintf("https://example.com/hook-%d", rInt),
PushEvents: true,
EnableSSLVerification: true,
}),
),
},
},
})
}
func testAccCheckGitlabProjectHookExists(n string, hook *gitlab.ProjectHook) resource.TestCheckFunc {
return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[n]
if !ok {
return fmt.Errorf("Not Found: %s", n)
}
hookID, err := strconv.Atoi(rs.Primary.ID)
if err != nil {
return err
}
repoName := rs.Primary.Attributes["project"]
if repoName == "" {
return fmt.Errorf("No project ID is set")
}
conn := testAccProvider.Meta().(*gitlab.Client)
gotHook, _, err := conn.Projects.GetProjectHook(repoName, hookID)
if err != nil {
return err
}
*hook = *gotHook
return nil
}
}
type testAccGitlabProjectHookExpectedAttributes struct {
URL string
PushEvents bool
IssuesEvents bool
MergeRequestsEvents bool
TagPushEvents bool
NoteEvents bool
BuildEvents bool
PipelineEvents bool
WikiPageEvents bool
EnableSSLVerification bool
}
func testAccCheckGitlabProjectHookAttributes(hook *gitlab.ProjectHook, want *testAccGitlabProjectHookExpectedAttributes) resource.TestCheckFunc {
return func(s *terraform.State) error {
if hook.URL != want.URL {
return fmt.Errorf("got url %q; want %q", hook.URL, want.URL)
}
if hook.EnableSSLVerification != want.EnableSSLVerification {
return fmt.Errorf("got enable_ssl_verification %t; want %t", hook.EnableSSLVerification, want.EnableSSLVerification)
}
if hook.PushEvents != want.PushEvents {
return fmt.Errorf("got push_events %t; want %t", hook.PushEvents, want.PushEvents)
}
if hook.IssuesEvents != want.IssuesEvents {
return fmt.Errorf("got issues_events %t; want %t", hook.IssuesEvents, want.IssuesEvents)
}
if hook.MergeRequestsEvents != want.MergeRequestsEvents {
return fmt.Errorf("got merge_requests_events %t; want %t", hook.MergeRequestsEvents, want.MergeRequestsEvents)
}
if hook.TagPushEvents != want.TagPushEvents {
return fmt.Errorf("got tag_push_events %t; want %t", hook.TagPushEvents, want.TagPushEvents)
}
if hook.NoteEvents != want.NoteEvents {
return fmt.Errorf("got note_events %t; want %t", hook.NoteEvents, want.NoteEvents)
}
if hook.BuildEvents != want.BuildEvents {
return fmt.Errorf("got build_events %t; want %t", hook.BuildEvents, want.BuildEvents)
}
if hook.PipelineEvents != want.PipelineEvents {
return fmt.Errorf("got pipeline_events %t; want %t", hook.PipelineEvents, want.PipelineEvents)
}
if hook.WikiPageEvents != want.WikiPageEvents {
return fmt.Errorf("got wiki_page_events %t; want %t", hook.WikiPageEvents, want.WikiPageEvents)
}
return nil
}
}
func testAccCheckGitlabProjectHookDestroy(s *terraform.State) error {
conn := testAccProvider.Meta().(*gitlab.Client)
for _, rs := range s.RootModule().Resources {
if rs.Type != "gitlab_project" {
continue
}
gotRepo, resp, err := conn.Projects.GetProject(rs.Primary.ID)
if err == nil {
if gotRepo != nil && fmt.Sprintf("%d", gotRepo.ID) == rs.Primary.ID {
return fmt.Errorf("Repository still exists")
}
}
if resp.StatusCode != 404 {
return err
}
return nil
}
return nil
}
func testAccGitlabProjectHookConfig(rInt int) string {
return fmt.Sprintf(`
resource "gitlab_project" "foo" {
name = "foo-%d"
description = "Terraform acceptance tests"
# So that acceptance tests can be run in a gitlab organization
# with no billing
visibility_level = "public"
}
resource "gitlab_project_hook" "foo" {
project = "${gitlab_project.foo.id}"
url = "https://example.com/hook-%d"
}
`, rInt, rInt)
}
func testAccGitlabProjectHookUpdateConfig(rInt int) string {
return fmt.Sprintf(`
resource "gitlab_project" "foo" {
name = "foo-%d"
description = "Terraform acceptance tests"
# So that acceptance tests can be run in a gitlab organization
# with no billing
visibility_level = "public"
}
resource "gitlab_project_hook" "foo" {
project = "${gitlab_project.foo.id}"
url = "https://example.com/hook-%d"
enable_ssl_verification = false
push_events = false
issues_events = true
merge_requests_events = true
tag_push_events = true
note_events = true
build_events = true
pipeline_events = true
wiki_page_events = true
}
`, rInt, rInt)
}

View File

@ -0,0 +1,59 @@
---
layout: "gitlab"
page_title: "GitLab: gitlab_project_hook"
sidebar_current: "docs-gitlab-resource-project-hook"
description: |-
Creates and manages hooks for GitLab projects
---
# gitlab\_project\_hook
This resource allows you to create and manage hooks for your GitLab projects.
For further information on hooks, consult the [gitlab
documentation](https://docs.gitlab.com/ce/user/project/integrations/webhooks.html).
## Example Usage
```hcl
resource "gitlab_project_hook" "example" {
project = "example/hooked"
url = "https://example.com/hook/example"
merge_requests_events = true
}
```
## Argument Reference
The following arguments are supported:
* `project` - (Required) The name or id of the project to add the hook to.
* `url` - (Required) The url of the hook to invoke.
* `token` - (Optional) A token to present when invoking the hook.
* `enable_ssl_verification` - (Optional) Enable ssl verification when invoking
the hook.
* `push_events` - (Optional) Invoke the hook for push events.
* `issues_events` - (Optional) Invoke the hook for issues events.
* `merge_requests_events` - (Optional) Invoke the hook for merge requests.
* `tag_push_events` - (Optional) Invoke the hook for tag push events.
* `note_events` - (Optional) Invoke the hook for tag push events.
* `build_events` - (Optional) Invoke the hook for build events.
* `pipeline_events` - (Optional) Invoke the hook for pipeline events.
* `wiki_page_events` - (Optional) Invoke the hook for wiki page events.
## Attributes Reference
The resource exports the following attributes:
* `id` - The unique id assigned to the hook by the GitLab server.