-
Notifications
You must be signed in to change notification settings - Fork 213
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #657 from johncoleman83/master
Adds license resource for user management
- Loading branch information
Showing
11 changed files
with
777 additions
and
5 deletions.
There are no files selected for viewing
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,170 @@ | ||
package pagerduty | ||
|
||
import ( | ||
"fmt" | ||
"log" | ||
"strings" | ||
"time" | ||
|
||
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" | ||
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" | ||
"github.com/heimweh/go-pagerduty/pagerduty" | ||
) | ||
|
||
var licenseSchema = map[string]*schema.Schema{ | ||
"id": { | ||
Type: schema.TypeString, | ||
Optional: true, | ||
Computed: true, | ||
}, | ||
"type": { | ||
Type: schema.TypeString, | ||
Optional: true, | ||
Computed: true, | ||
}, | ||
"name": { | ||
Type: schema.TypeString, | ||
Optional: true, | ||
Computed: true, | ||
}, | ||
"summary": { | ||
Type: schema.TypeString, | ||
Optional: true, | ||
Computed: true, | ||
}, | ||
"description": { | ||
Type: schema.TypeString, | ||
Optional: true, | ||
Computed: true, | ||
}, | ||
"role_group": { | ||
Type: schema.TypeString, | ||
Optional: true, | ||
Computed: true, | ||
}, | ||
"current_value": { | ||
Type: schema.TypeInt, | ||
Optional: true, | ||
Computed: true, | ||
}, | ||
"allocations_available": { | ||
Type: schema.TypeInt, | ||
Optional: true, | ||
Computed: true, | ||
}, | ||
"valid_roles": { | ||
Type: schema.TypeList, | ||
Optional: true, | ||
Computed: true, | ||
Elem: &schema.Schema{ | ||
Type: schema.TypeString, | ||
}, | ||
}, | ||
"self": { | ||
Type: schema.TypeString, | ||
Optional: true, | ||
Computed: true, | ||
}, | ||
"html_url": { | ||
Type: schema.TypeString, | ||
Optional: true, | ||
Computed: true, | ||
}, | ||
} | ||
|
||
func dataSourcePagerDutyLicense() *schema.Resource { | ||
return &schema.Resource{ | ||
Read: dataSourcePagerDutyLicenseRead, | ||
Schema: licenseSchema, | ||
} | ||
} | ||
|
||
func dataSourcePagerDutyLicenseRead(d *schema.ResourceData, meta interface{}) error { | ||
client, err := meta.(*Config).Client() | ||
if err != nil { | ||
return err | ||
} | ||
|
||
log.Printf("[INFO] Fetching PagerDuty Licenses") | ||
|
||
return resource.Retry(5*time.Minute, func() *resource.RetryError { | ||
licenses, _, err := client.Licenses.List() | ||
if err != nil { | ||
// Delaying retry by 30s as recommended by PagerDuty | ||
// https://developer.pagerduty.com/docs/rest-api-v2/rate-limiting/#what-are-possible-workarounds-to-the-events-api-rate-limit | ||
time.Sleep(30 * time.Second) | ||
return resource.RetryableError(err) | ||
} | ||
|
||
id, name, description := d.Get("id").(string), d.Get("name").(string), d.Get("description").(string) | ||
found := findBestMatchLicense(licenses, id, name, description) | ||
|
||
if found == nil { | ||
ids := licensesToStringOfIds(licenses) | ||
return resource.NonRetryableError( | ||
fmt.Errorf("Unable to locate any license with ids in [%s] with the configured id: '%s', name: '%s' or description: '%s'", ids, id, name, description)) | ||
} | ||
|
||
d.SetId(found.ID) | ||
d.Set("name", found.Name) | ||
d.Set("description", found.Description) | ||
d.Set("type", found.Type) | ||
d.Set("summary", found.Summary) | ||
d.Set("role_group", found.RoleGroup) | ||
d.Set("allocations_available", found.AllocationsAvailable) | ||
d.Set("current_value", found.CurrentValue) | ||
d.Set("valid_roles", found.ValidRoles) | ||
d.Set("self", found.Self) | ||
d.Set("html_url", found.HTMLURL) | ||
|
||
return nil | ||
}) | ||
} | ||
|
||
func licensesToStringOfIds(licenses []*pagerduty.License) string { | ||
ids := make([]string, len(licenses)) | ||
for i, v := range licenses { | ||
ids[i] = v.ID | ||
} | ||
return strings.Join(ids, ", ") | ||
} | ||
|
||
func findBestMatchLicense(licenses []*pagerduty.License, id, name, description string) *pagerduty.License { | ||
var found *pagerduty.License | ||
for _, license := range licenses { | ||
if licenseIsExactMatch(license, id, name, description) { | ||
found = license | ||
break | ||
} | ||
} | ||
|
||
// If there is no exact match for a license, check for substring matches | ||
// This allows customers to use a term such as "Full User", which is included | ||
// in the names of all licenses that support creating full users. However, | ||
// if id is set then it must match with licenseIsExactMatch | ||
if id == "" && found == nil { | ||
for _, license := range licenses { | ||
if licenseContainsMatch(license, name, description) { | ||
found = license | ||
break | ||
} | ||
} | ||
} | ||
|
||
return found | ||
} | ||
|
||
func licenseIsExactMatch(license *pagerduty.License, id, name, description string) bool { | ||
if id != "" { | ||
return license.ID == id && matchesOrIsUnset(license.Name, name) && matchesOrIsUnset(license.Description, description) | ||
} | ||
return license.Name == name && license.Description == description | ||
} | ||
|
||
func matchesOrIsUnset(licenseAttr, config string) bool { | ||
return config == "" || config == licenseAttr | ||
} | ||
|
||
func licenseContainsMatch(license *pagerduty.License, name, description string) bool { | ||
return strings.Contains(license.Name, name) && strings.Contains(license.Description, description) | ||
} |
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,154 @@ | ||
package pagerduty | ||
|
||
import ( | ||
"fmt" | ||
"os" | ||
"regexp" | ||
"testing" | ||
|
||
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" | ||
"github.com/hashicorp/terraform-plugin-sdk/v2/terraform" | ||
) | ||
|
||
func TestAccDataSourcePagerDutyLicense_Basic(t *testing.T) { | ||
reference := "full_user" | ||
description := "" | ||
name := os.Getenv("PAGERDUTY_ACC_LICENSE_NAME") | ||
|
||
resource.Test(t, resource.TestCase{ | ||
PreCheck: func() { | ||
testAccPreCheck(t) | ||
testAccPreCheckLicenseNameTests(t, name) | ||
}, | ||
Providers: testAccProviders, | ||
Steps: []resource.TestStep{ | ||
{ | ||
Config: testAccDataSourcePagerDutyLicenseConfig(reference, name, description), | ||
Check: resource.ComposeTestCheckFunc( | ||
testAccDataSourcePagerDutyLicense(fmt.Sprintf("data.pagerduty_license.%s", reference)), | ||
), | ||
}, | ||
}, | ||
}) | ||
} | ||
|
||
func TestAccDataSourcePagerDutyLicense_Empty(t *testing.T) { | ||
// Note that this test does not actually set any values for the name or | ||
// description of the license. An accounts license data changes over time and | ||
// per account. So, this test only verifies that a license can be found with | ||
// an empty pagerduty_license datasource | ||
reference := "full_user" | ||
|
||
resource.Test(t, resource.TestCase{ | ||
PreCheck: func() { testAccPreCheck(t) }, | ||
Providers: testAccProviders, | ||
Steps: []resource.TestStep{ | ||
{ | ||
Config: testAccDataSourceEmptyPagerDutyLicenseConfig(reference), | ||
Check: resource.ComposeTestCheckFunc( | ||
testAccDataSourcePagerDutyLicense(fmt.Sprintf("data.pagerduty_license.%s", reference)), | ||
), | ||
}, | ||
}, | ||
}) | ||
} | ||
|
||
func TestAccDataSourcePagerDutyLicense_Error(t *testing.T) { | ||
reference := "testing_reference_missing_license" | ||
expectedErrorString := "Unable to locate any license" | ||
|
||
resource.Test(t, resource.TestCase{ | ||
PreCheck: func() { testAccPreCheck(t) }, | ||
Providers: testAccProviders, | ||
Steps: []resource.TestStep{ | ||
{ | ||
Config: testAccDataSourcePagerDutyLicenseConfigError(reference), | ||
ExpectError: regexp.MustCompile(expectedErrorString), | ||
}, | ||
}, | ||
}) | ||
} | ||
|
||
func TestAccDataSourcePagerDutyLicense_ErrorWithID(t *testing.T) { | ||
reference := "testing_reference_missing_license" | ||
expectedErrorString := "Unable to locate any license" | ||
// Even with an expected name, if the configured ID is not found there | ||
// should be an error | ||
name := "Full User" | ||
|
||
resource.Test(t, resource.TestCase{ | ||
PreCheck: func() { testAccPreCheck(t) }, | ||
Providers: testAccProviders, | ||
Steps: []resource.TestStep{ | ||
{ | ||
Config: testAccDataSourcePagerDutyLicenseConfigErrorWithID(reference, name), | ||
ExpectError: regexp.MustCompile(expectedErrorString), | ||
}, | ||
}, | ||
}) | ||
} | ||
|
||
func testAccPreCheckLicenseNameTests(t *testing.T, name string) { | ||
if name == "" { | ||
t.Skip("PAGERDUTY_ACC_LICENSE_NAME not set. Skipping tests requiring license names") | ||
} | ||
} | ||
|
||
func testAccDataSourcePagerDutyLicense(n string) resource.TestCheckFunc { | ||
return func(s *terraform.State) error { | ||
r := s.RootModule().Resources[n] | ||
a := r.Primary.Attributes | ||
|
||
testAtts := []string{ | ||
"id", | ||
"name", | ||
"summary", | ||
"description", | ||
"role_group", | ||
"current_value", | ||
"allocations_available", | ||
"html_url", | ||
"self", | ||
} | ||
|
||
for _, att := range testAtts { | ||
if _, ok := a[att]; !ok { | ||
return fmt.Errorf("Expected the required attribute %s to exist", att) | ||
} | ||
} | ||
|
||
return nil | ||
} | ||
} | ||
|
||
func testAccDataSourcePagerDutyLicenseConfig(reference string, name string, description string) string { | ||
return fmt.Sprintf(` | ||
data "pagerduty_license" "%s" { | ||
name = "%s" | ||
description = "%s" | ||
} | ||
`, reference, name, description) | ||
} | ||
|
||
func testAccDataSourceEmptyPagerDutyLicenseConfig(reference string) string { | ||
return fmt.Sprintf(` | ||
data "pagerduty_license" "%s" {} | ||
`, reference) | ||
} | ||
|
||
func testAccDataSourcePagerDutyLicenseConfigError(reference string) string { | ||
return fmt.Sprintf(` | ||
data "pagerduty_license" "%s" { | ||
name = "%s" | ||
} | ||
`, reference, reference) | ||
} | ||
|
||
func testAccDataSourcePagerDutyLicenseConfigErrorWithID(reference, name string) string { | ||
return fmt.Sprintf(` | ||
data "pagerduty_license" "%s" { | ||
id = "%s" | ||
name = "%s" | ||
} | ||
`, reference, reference, name) | ||
} |
Oops, something went wrong.