From 329b3751a8090d9d037d20ab238b9d2fa5c5f4d0 Mon Sep 17 00:00:00 2001 From: Doug Ayers Date: Sun, 2 Oct 2022 21:39:03 -0500 Subject: [PATCH 01/12] reusable org ip allow list functions --- github/util_v4_organization.go | 88 ++++++++++++++++++++++++++++++++++ 1 file changed, 88 insertions(+) create mode 100644 github/util_v4_organization.go diff --git a/github/util_v4_organization.go b/github/util_v4_organization.go new file mode 100644 index 0000000000..fde95ee9f8 --- /dev/null +++ b/github/util_v4_organization.go @@ -0,0 +1,88 @@ +package github + +import ( + "context" + + "github.com/shurcooL/githubv4" +) + +type IpAllowListEntry struct { + id githubv4.String + name githubv4.String + allow_list_value githubv4.String + is_active githubv4.Boolean + created_at githubv4.String + updated_at githubv4.String +} + +/** + * Returns all IP allow list entries for an organization. + * This util function is used by both data_source and resource elements. + */ +func getOrganizationIpAllowListEntries(meta interface{}) ([]IpAllowListEntry, error) { + err := checkOrganization(meta) + if err != nil { + return nil, err + } + + ctx := context.Background() + client := meta.(*Owner).v4client + orgName := meta.(*Owner).name + + type IpAllowListEntryGql struct { + ID githubv4.String + Name githubv4.String + AllowListValue githubv4.String + IsActive githubv4.Boolean + CreatedAt githubv4.String + UpdatedAt githubv4.String + } + + type IpAllowListEntriesGql struct { + Nodes []IpAllowListEntryGql + PageInfo PageInfo + TotalCount githubv4.Int + } + + var query struct { + Organization struct { + ID githubv4.String + IpAllowListEntries IpAllowListEntriesGql `graphql:"ipAllowListEntries(first: 100, after: $entriesCursor)"` + } `graphql:"organization(login: $login)"` + } + + variables := map[string]interface{}{ + "login": githubv4.String(orgName), + "entriesCursor": (*githubv4.String)(nil), + } + + var ipAllowListEntriesGql []IpAllowListEntryGql + + for { + err := client.Query(ctx, &query, variables) + if err != nil { + return nil, err + } + + ipAllowListEntriesGql = append(ipAllowListEntriesGql, query.Organization.IpAllowListEntries.Nodes...) + if !query.Organization.IpAllowListEntries.PageInfo.HasNextPage { + break + } + variables["entriesCursor"] = githubv4.NewString(query.Organization.IpAllowListEntries.PageInfo.EndCursor) + } + + // Translate the graphql response to terraform state. + var ipAllowListEntries []IpAllowListEntry + for index := range ipAllowListEntriesGql { + ipAllowListEntries = append(ipAllowListEntries, IpAllowListEntry{ + id: ipAllowListEntriesGql[index].ID, + name: ipAllowListEntriesGql[index].Name, + allow_list_value: ipAllowListEntriesGql[index].AllowListValue, + is_active: ipAllowListEntriesGql[index].IsActive, + created_at: ipAllowListEntriesGql[index].CreatedAt, + updated_at: ipAllowListEntriesGql[index].UpdatedAt, + }) + } + + return ipAllowListEntries, nil +} From 8c461b5bc207bd83509453d064857e42bfbe865c Mon Sep 17 00:00:00 2001 From: Doug Ayers Date: Sun, 2 Oct 2022 21:39:17 -0500 Subject: [PATCH 02/12] refactored to use util function --- ...ource_github_organization_ip_allow_list.go | 75 ++----------------- 1 file changed, 5 insertions(+), 70 deletions(-) diff --git a/github/data_source_github_organization_ip_allow_list.go b/github/data_source_github_organization_ip_allow_list.go index c7a3dff494..079e898cb8 100644 --- a/github/data_source_github_organization_ip_allow_list.go +++ b/github/data_source_github_organization_ip_allow_list.go @@ -1,10 +1,7 @@ package github import ( - "context" - "github.com/hashicorp/terraform-plugin-sdk/helper/schema" - "github.com/shurcooL/githubv4" ) func dataSourceGithubOrganizationIpAllowList() *schema.Resource { @@ -49,77 +46,15 @@ func dataSourceGithubOrganizationIpAllowList() *schema.Resource { } func dataSourceGithubOrganizationIpAllowListRead(d *schema.ResourceData, meta interface{}) error { - err := checkOrganization(meta) + orgId := meta.(*Owner).id + + ipAllowListEntries, err := getOrganizationIpAllowListEntries(meta) if err != nil { return err } - ctx := context.Background() - client := meta.(*Owner).v4client - orgName := meta.(*Owner).name - - type PageInfo struct { - StartCursor githubv4.String - EndCursor githubv4.String - HasNextPage githubv4.Boolean - HasPreviousPage githubv4.Boolean - } - - type IpAllowListEntry struct { - ID githubv4.String - Name githubv4.String - AllowListValue githubv4.String - IsActive githubv4.Boolean - CreatedAt githubv4.String - UpdatedAt githubv4.String - } - - type IpAllowListEntries struct { - Nodes []IpAllowListEntry - PageInfo PageInfo - TotalCount githubv4.Int - } - - var query struct { - Organization struct { - ID githubv4.String - IpAllowListEntries IpAllowListEntries `graphql:"ipAllowListEntries(first: 100, after: $entriesCursor)"` - } `graphql:"organization(login: $login)"` - } - - variables := map[string]interface{}{ - "login": githubv4.String(orgName), - "entriesCursor": (*githubv4.String)(nil), - } - - var ipAllowList []interface{} - var ipAllowListEntries []IpAllowListEntry - - for { - err := client.Query(ctx, &query, variables) - if err != nil { - return err - } - - ipAllowListEntries = append(ipAllowListEntries, query.Organization.IpAllowListEntries.Nodes...) - if !query.Organization.IpAllowListEntries.PageInfo.HasNextPage { - break - } - variables["entriesCursor"] = githubv4.NewString(query.Organization.IpAllowListEntries.PageInfo.EndCursor) - } - for index := range ipAllowListEntries { - ipAllowList = append(ipAllowList, map[string]interface{}{ - "id": ipAllowListEntries[index].ID, - "name": ipAllowListEntries[index].Name, - "allow_list_value": ipAllowListEntries[index].AllowListValue, - "is_active": ipAllowListEntries[index].IsActive, - "created_at": ipAllowListEntries[index].CreatedAt, - "updated_at": ipAllowListEntries[index].UpdatedAt, - }) - } - - d.SetId(string(query.Organization.ID)) - d.Set("ip_allow_list", ipAllowList) + d.SetId(string(orgId)) + d.Set("ip_allow_list", ipAllowListEntries) return nil } From 376d231692e629c9b70aab8251044ae03bdbe864 Mon Sep 17 00:00:00 2001 From: Doug Ayers Date: Sun, 2 Oct 2022 21:39:38 -0500 Subject: [PATCH 03/12] resource to manage ip allow list entries --- github/provider.go | 1 + ...ource_github_organization_ip_allow_list.go | 225 ++++++++++++++++++ 2 files changed, 226 insertions(+) create mode 100644 github/resource_github_organization_ip_allow_list.go diff --git a/github/provider.go b/github/provider.go index 7990b420c6..53f1a4b7ec 100644 --- a/github/provider.go +++ b/github/provider.go @@ -108,6 +108,7 @@ func Provider() terraform.ResourceProvider { "github_issue_label": resourceGithubIssueLabel(), "github_membership": resourceGithubMembership(), "github_organization_block": resourceOrganizationBlock(), + "github_organization_ip_allow_list": resourceGithubOrganizationIpAllowList(), "github_organization_project": resourceGithubOrganizationProject(), "github_organization_settings": resouceGithubOrganizationSettings(), "github_organization_webhook": resourceGithubOrganizationWebhook(), diff --git a/github/resource_github_organization_ip_allow_list.go b/github/resource_github_organization_ip_allow_list.go new file mode 100644 index 0000000000..842cd57d55 --- /dev/null +++ b/github/resource_github_organization_ip_allow_list.go @@ -0,0 +1,225 @@ +package github + +import ( + "context" + "fmt" + "log" + "net/http" + + "github.com/google/go-github/v47/github" + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" + "github.com/shurcooL/githubv4" +) + +func resourceGithubOrganizationIpAllowList() *schema.Resource { + return &schema.Resource{ + Read: resourceGithubOrganizationIpAllowListRead, + Create: resourceGithubOrganizationIpAllowListCreate, + Update: resourceGithubOrganizationIpAllowListUpdate, + Delete: resourceGithubOrganizationIpAllowListDelete, + + Importer: &schema.ResourceImporter{ + State: resourceGithubOrganizationIpAllowListImport, + }, + + SchemaVersion: 1, + Schema: map[string]*schema.Schema{ + "id": { + Type: schema.TypeString, + Computed: true, + }, + "name": { + Type: schema.TypeString, + Optional: true, + Description: "The name of the IP allow list entry.", + }, + "allow_list_value": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: "A single IP address or range of IP addresses in CIDR notation.", + }, + "is_active": { + Type: schema.TypeBool, + Optional: true, + Default: true, + Description: "Whether the entry is currently active. Default is true.", + }, + "created_at": { + Type: schema.TypeString, + Computed: true, + Description: "Identifies the date and time when the object was created.", + }, + "updated_at": { + Type: schema.TypeString, + Computed: true, + Description: "Identifies the date and time when the object was last updated.", + }, + }, + } +} + +func resourceGithubOrganizationIpAllowListImport(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { + orgName := meta.(*Owner).name + + // Fetch all IP allow list entries for the org. + ipAllowListEntries, err := getOrganizationIpAllowListEntries(meta) + if err != nil { + return nil, err + } + + // We support importing by the actual ip allow list entry id or + // by the ip range itself because it must be unique. + valueToImport := githubv4.String(d.Id()) + ipAllowListEntryId := "" + + for index := range ipAllowListEntries { + ipAllowListEntry := ipAllowListEntries[index] + if ipAllowListEntry.id == valueToImport || ipAllowListEntry.allow_list_value == valueToImport { + ipAllowListEntryId = string(ipAllowListEntry.id) + break + } + } + + d.SetId(ipAllowListEntryId) + err = resourceGithubOrganizationIpAllowListRead(d, meta) + if err != nil { + return nil, err + } + + // resourceGithubOrganizationIpAllowListRead calls d.SetId("") if the ip entry does not exist + if d.Id() == "" { + return nil, fmt.Errorf("Organization %s does not have an IP allow list entry for %s.", orgName, valueToImport) + } + + return []*schema.ResourceData{d}, nil +} + +func resourceGithubOrganizationIpAllowListRead(d *schema.ResourceData, meta interface{}) error { + client := meta.(*Owner).v4client + ctx := context.Background() + + var query struct { + Node struct { + IpAllowListEntry struct { + ID githubv4.String + Name githubv4.String + AllowListValue githubv4.String + IsActive githubv4.Boolean + CreatedAt githubv4.String + UpdatedAt githubv4.String + } `graphql:"... on IpAllowListEntry"` + } `graphql:"node(id: $id)"` + } + + variables := map[string]interface{}{ + "id": d.Id(), + } + + err := client.Query(ctx, &query, variables) + if err != nil { + if ghErr, ok := err.(*github.ErrorResponse); ok { + if ghErr.Response.StatusCode == http.StatusNotFound { + log.Printf("[INFO] Removing ip allow list entry %s from state because it no longer exists in GitHub", d.Id()) + d.SetId("") + return nil + } + } + return err + } + + d.Set("name", query.Node.IpAllowListEntry.Name) + d.Set("allow_list_value", query.Node.IpAllowListEntry.AllowListValue) + d.Set("is_active", query.Node.IpAllowListEntry.IsActive) + d.Set("created_at", query.Node.IpAllowListEntry.CreatedAt) + d.Set("updated_at", query.Node.IpAllowListEntry.UpdatedAt) + + return nil +} + +func resourceGithubOrganizationIpAllowListCreate(d *schema.ResourceData, meta interface{}) error { + client := meta.(*Owner).v4client + ctx := context.Background() + + orgId := meta.(*Owner).id + name := d.Get("name").(string) + allowListValue := d.Get("allow_list_value").(string) + isActive := d.Get("is_active").(bool) + + var mutation struct { + CreateIpAllowListEntryInput struct { + IpAllowListEntry struct { + ID githubv4.String + } + } `graphql:"createIpAllowListEntryInput(input: $input)"` + } + + input := githubv4.CreateIpAllowListEntryInput{ + OwnerID: githubv4.NewID(orgId), + Name: (*githubv4.String)(&name), + AllowListValue: githubv4.String(allowListValue), + IsActive: githubv4.Boolean(isActive), + } + + err := client.Mutate(ctx, &mutation, input, nil) + if err != nil { + return err + } + + d.SetId(fmt.Sprintf("%s", mutation.CreateIpAllowListEntryInput.IpAllowListEntry.ID)) + + return resourceGithubOrganizationIpAllowListRead(d, meta) +} + +func resourceGithubOrganizationIpAllowListUpdate(d *schema.ResourceData, meta interface{}) error { + client := meta.(*Owner).v4client + ctx := context.Background() + + name := d.Get("name").(string) + allowListValue := d.Get("allow_list_value").(string) + isActive := d.Get("is_active").(bool) + + var mutation struct { + UpdateIpAllowListEntryInput struct { + IpAllowListEntry struct { + ID githubv4.String + } + } `graphql:"updateIpAllowListEntryInput(input: $input)"` + } + + input := githubv4.UpdateIpAllowListEntryInput{ + IPAllowListEntryID: d.Id(), + Name: (*githubv4.String)(&name), + AllowListValue: githubv4.String(allowListValue), + IsActive: githubv4.Boolean(isActive), + } + + err := client.Mutate(ctx, &mutation, input, nil) + if err != nil { + return err + } + + d.SetId(fmt.Sprintf("%s", mutation.UpdateIpAllowListEntryInput.IpAllowListEntry.ID)) + + return resourceGithubOrganizationIpAllowListRead(d, meta) +} + +func resourceGithubOrganizationIpAllowListDelete(d *schema.ResourceData, meta interface{}) error { + client := meta.(*Owner).v4client + ctx := context.Background() + + var mutation struct { + DeleteIpAllowListEntryInput struct { + IpAllowListEntry struct { + ID githubv4.String + } + } `graphql:"deleteIpAllowListEntryInput(input: $input)"` + } + + input := githubv4.DeleteIpAllowListEntryInput{ + IPAllowListEntryID: d.Id(), + } + + err := client.Mutate(ctx, &mutation, input, nil) + return err +} From 486fae810bb5874510f058618672d05eeb523c3a Mon Sep 17 00:00:00 2001 From: Doug Ayers Date: Sun, 2 Oct 2022 21:54:36 -0500 Subject: [PATCH 04/12] id is a reserved terraform property --- github/resource_github_organization_ip_allow_list.go | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/github/resource_github_organization_ip_allow_list.go b/github/resource_github_organization_ip_allow_list.go index 842cd57d55..394bc0ee3f 100644 --- a/github/resource_github_organization_ip_allow_list.go +++ b/github/resource_github_organization_ip_allow_list.go @@ -24,10 +24,6 @@ func resourceGithubOrganizationIpAllowList() *schema.Resource { SchemaVersion: 1, Schema: map[string]*schema.Schema{ - "id": { - Type: schema.TypeString, - Computed: true, - }, "name": { Type: schema.TypeString, Optional: true, @@ -166,7 +162,7 @@ func resourceGithubOrganizationIpAllowListCreate(d *schema.ResourceData, meta in return err } - d.SetId(fmt.Sprintf("%s", mutation.CreateIpAllowListEntryInput.IpAllowListEntry.ID)) + d.SetId(string(mutation.CreateIpAllowListEntryInput.IpAllowListEntry.ID)) return resourceGithubOrganizationIpAllowListRead(d, meta) } @@ -199,7 +195,7 @@ func resourceGithubOrganizationIpAllowListUpdate(d *schema.ResourceData, meta in return err } - d.SetId(fmt.Sprintf("%s", mutation.UpdateIpAllowListEntryInput.IpAllowListEntry.ID)) + d.SetId(string(mutation.UpdateIpAllowListEntryInput.IpAllowListEntry.ID)) return resourceGithubOrganizationIpAllowListRead(d, meta) } From 197218d251ef08a73fd81d5182f810d8eef01dab Mon Sep 17 00:00:00 2001 From: Doug Ayers Date: Mon, 3 Oct 2022 00:36:58 -0500 Subject: [PATCH 05/12] add githubv4IsNodeNotFoundError --- github/util.go | 11 +++++++++++ github/util_v4.go | 24 ++++++++++++------------ 2 files changed, 23 insertions(+), 12 deletions(-) diff --git a/github/util.go b/github/util.go index 2b17a5cc54..4f8ca2b433 100644 --- a/github/util.go +++ b/github/util.go @@ -91,6 +91,17 @@ func buildChecksumID(v []string) string { return fmt.Sprintf("%x", bs) } +func expandNestedSet(m map[string]interface{}, target string) []string { + res := make([]string, 0) + if v, ok := m[target]; ok { + vL := v.(*schema.Set).List() + for _, v := range vL { + res = append(res, v.(string)) + } + } + return res +} + func expandStringList(configured []interface{}) []string { vs := make([]string, 0, len(configured)) for _, v := range configured { diff --git a/github/util_v4.go b/github/util_v4.go index 035a94dbbe..1e66cbe655 100644 --- a/github/util_v4.go +++ b/github/util_v4.go @@ -1,7 +1,8 @@ package github import ( - "github.com/hashicorp/terraform-plugin-sdk/helper/schema" + "strings" + "github.com/shurcooL/githubv4" ) @@ -10,17 +11,6 @@ type PageInfo struct { HasNextPage bool } -func expandNestedSet(m map[string]interface{}, target string) []string { - res := make([]string, 0) - if v, ok := m[target]; ok { - vL := v.(*schema.Set).List() - for _, v := range vL { - res = append(res, v.(string)) - } - } - return res -} - func githubv4StringSlice(ss []string) []githubv4.String { var vGh4 []githubv4.String for _, s := range ss { @@ -48,3 +38,13 @@ func githubv4IDSliceEmpty(ss []string) []githubv4.ID { func githubv4NewStringSlice(v []githubv4.String) *[]githubv4.String { return &v } func githubv4NewIDSlice(v []githubv4.ID) *[]githubv4.ID { return &v } + +/** + * Checks if the error message represents + * that graphql query did not find node by its global id. + * Graphql error responses return 200 OK http codes + * so we have to inspect the messages =/ + */ +func githubv4IsNodeNotFoundError(err error) bool { + return err != nil && strings.HasPrefix(err.Error(), "Could not resolve to a node with the global id") +} From 5bd1d4442bec0f45f2c9c458683a25fccaf3a95d Mon Sep 17 00:00:00 2001 From: Doug Ayers Date: Mon, 3 Oct 2022 00:37:19 -0500 Subject: [PATCH 06/12] clarify the resource manages a single ip entry --- github/provider.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/github/provider.go b/github/provider.go index 53f1a4b7ec..e567f7f8fe 100644 --- a/github/provider.go +++ b/github/provider.go @@ -108,7 +108,7 @@ func Provider() terraform.ResourceProvider { "github_issue_label": resourceGithubIssueLabel(), "github_membership": resourceGithubMembership(), "github_organization_block": resourceOrganizationBlock(), - "github_organization_ip_allow_list": resourceGithubOrganizationIpAllowList(), + "github_organization_ip_allow_list_entry": resourceGithubOrganizationIpAllowList(), "github_organization_project": resourceGithubOrganizationProject(), "github_organization_settings": resouceGithubOrganizationSettings(), "github_organization_webhook": resourceGithubOrganizationWebhook(), From 2d89704c169c50b3c57658d3dc6dbe463d4143e6 Mon Sep 17 00:00:00 2001 From: Doug Ayers Date: Mon, 3 Oct 2022 00:37:45 -0500 Subject: [PATCH 07/12] rename util since specific for ip allow list entries --- github/util_v4_organization.go | 88 -------------------- github/util_v4_organization_ip_allow_list.go | 66 +++++++++++++++ 2 files changed, 66 insertions(+), 88 deletions(-) delete mode 100644 github/util_v4_organization.go create mode 100644 github/util_v4_organization_ip_allow_list.go diff --git a/github/util_v4_organization.go b/github/util_v4_organization.go deleted file mode 100644 index fde95ee9f8..0000000000 --- a/github/util_v4_organization.go +++ /dev/null @@ -1,88 +0,0 @@ -package github - -import ( - "context" - - "github.com/shurcooL/githubv4" -) - -type IpAllowListEntry struct { - id githubv4.String - name githubv4.String - allow_list_value githubv4.String - is_active githubv4.Boolean - created_at githubv4.String - updated_at githubv4.String -} - -/** - * Returns all IP allow list entries for an organization. - * This util function is used by both data_source and resource elements. - */ -func getOrganizationIpAllowListEntries(meta interface{}) ([]IpAllowListEntry, error) { - err := checkOrganization(meta) - if err != nil { - return nil, err - } - - ctx := context.Background() - client := meta.(*Owner).v4client - orgName := meta.(*Owner).name - - type IpAllowListEntryGql struct { - ID githubv4.String - Name githubv4.String - AllowListValue githubv4.String - IsActive githubv4.Boolean - CreatedAt githubv4.String - UpdatedAt githubv4.String - } - - type IpAllowListEntriesGql struct { - Nodes []IpAllowListEntryGql - PageInfo PageInfo - TotalCount githubv4.Int - } - - var query struct { - Organization struct { - ID githubv4.String - IpAllowListEntries IpAllowListEntriesGql `graphql:"ipAllowListEntries(first: 100, after: $entriesCursor)"` - } `graphql:"organization(login: $login)"` - } - - variables := map[string]interface{}{ - "login": githubv4.String(orgName), - "entriesCursor": (*githubv4.String)(nil), - } - - var ipAllowListEntriesGql []IpAllowListEntryGql - - for { - err := client.Query(ctx, &query, variables) - if err != nil { - return nil, err - } - - ipAllowListEntriesGql = append(ipAllowListEntriesGql, query.Organization.IpAllowListEntries.Nodes...) - if !query.Organization.IpAllowListEntries.PageInfo.HasNextPage { - break - } - variables["entriesCursor"] = githubv4.NewString(query.Organization.IpAllowListEntries.PageInfo.EndCursor) - } - - // Translate the graphql response to terraform state. - var ipAllowListEntries []IpAllowListEntry - for index := range ipAllowListEntriesGql { - ipAllowListEntries = append(ipAllowListEntries, IpAllowListEntry{ - id: ipAllowListEntriesGql[index].ID, - name: ipAllowListEntriesGql[index].Name, - allow_list_value: ipAllowListEntriesGql[index].AllowListValue, - is_active: ipAllowListEntriesGql[index].IsActive, - created_at: ipAllowListEntriesGql[index].CreatedAt, - updated_at: ipAllowListEntriesGql[index].UpdatedAt, - }) - } - - return ipAllowListEntries, nil -} diff --git a/github/util_v4_organization_ip_allow_list.go b/github/util_v4_organization_ip_allow_list.go new file mode 100644 index 0000000000..ef3ae60525 --- /dev/null +++ b/github/util_v4_organization_ip_allow_list.go @@ -0,0 +1,66 @@ +package github + +import ( + "context" + + "github.com/shurcooL/githubv4" +) + +type IpAllowListEntry struct { + ID githubv4.String `mapstructure:"id"` + Name githubv4.String `mapstructure:"name"` + AllowListValue githubv4.String `mapstructure:"allow_list_value"` + IsActive githubv4.Boolean `mapstructure:"is_active"` + CreatedAt githubv4.String `mapstructure:"created_at"` + UpdatedAt githubv4.String `mapstructure:"updated_at"` +} + +/** + * Returns all IP allow list entries for an organization. + * This util function is used by both data_source and resource elements. + */ +func getOrganizationIpAllowListEntries(meta interface{}) ([]IpAllowListEntry, error) { + err := checkOrganization(meta) + if err != nil { + return nil, err + } + + ctx := context.Background() + client := meta.(*Owner).v4client + orgName := meta.(*Owner).name + + type IpAllowListEntries struct { + Nodes []IpAllowListEntry + PageInfo PageInfo + TotalCount githubv4.Int + } + + var query struct { + Organization struct { + ID githubv4.String + IpAllowListEntries IpAllowListEntries `graphql:"ipAllowListEntries(first: 100, after: $entriesCursor)"` + } `graphql:"organization(login: $login)"` + } + + variables := map[string]interface{}{ + "login": githubv4.String(orgName), + "entriesCursor": (*githubv4.String)(nil), + } + + var ipAllowListEntries []IpAllowListEntry + + for { + err := client.Query(ctx, &query, variables) + if err != nil { + return nil, err + } + + ipAllowListEntries = append(ipAllowListEntries, query.Organization.IpAllowListEntries.Nodes...) + if !query.Organization.IpAllowListEntries.PageInfo.HasNextPage { + break + } + variables["entriesCursor"] = githubv4.NewString(query.Organization.IpAllowListEntries.PageInfo.EndCursor) + } + + return ipAllowListEntries, nil +} From fe9fa22176aed0c34a1107d560cac54612467503 Mon Sep 17 00:00:00 2001 From: Doug Ayers Date: Mon, 3 Oct 2022 00:38:03 -0500 Subject: [PATCH 08/12] fix org id conversion --- github/data_source_github_organization_ip_allow_list.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/github/data_source_github_organization_ip_allow_list.go b/github/data_source_github_organization_ip_allow_list.go index 079e898cb8..67948331c3 100644 --- a/github/data_source_github_organization_ip_allow_list.go +++ b/github/data_source_github_organization_ip_allow_list.go @@ -1,6 +1,8 @@ package github import ( + "strconv" + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" ) @@ -53,7 +55,7 @@ func dataSourceGithubOrganizationIpAllowListRead(d *schema.ResourceData, meta in return err } - d.SetId(string(orgId)) + d.SetId(strconv.FormatInt(orgId, 10)) d.Set("ip_allow_list", ipAllowListEntries) return nil From b2fe09fece1e9ba57449a2caac206f11c0bb9f7f Mon Sep 17 00:00:00 2001 From: Doug Ayers Date: Mon, 3 Oct 2022 00:38:41 -0500 Subject: [PATCH 09/12] handle when ip allow list entry not found --- ...ource_github_organization_ip_allow_list.go | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/github/resource_github_organization_ip_allow_list.go b/github/resource_github_organization_ip_allow_list.go index 394bc0ee3f..f7c76f7ab8 100644 --- a/github/resource_github_organization_ip_allow_list.go +++ b/github/resource_github_organization_ip_allow_list.go @@ -4,9 +4,7 @@ import ( "context" "fmt" "log" - "net/http" - "github.com/google/go-github/v47/github" "github.com/hashicorp/terraform-plugin-sdk/helper/schema" "github.com/shurcooL/githubv4" ) @@ -66,17 +64,21 @@ func resourceGithubOrganizationIpAllowListImport(d *schema.ResourceData, meta in // We support importing by the actual ip allow list entry id or // by the ip range itself because it must be unique. - valueToImport := githubv4.String(d.Id()) + valueToImport := d.Id() ipAllowListEntryId := "" for index := range ipAllowListEntries { ipAllowListEntry := ipAllowListEntries[index] - if ipAllowListEntry.id == valueToImport || ipAllowListEntry.allow_list_value == valueToImport { - ipAllowListEntryId = string(ipAllowListEntry.id) + if string(ipAllowListEntry.ID) == valueToImport || string(ipAllowListEntry.AllowListValue) == valueToImport { + ipAllowListEntryId = string(ipAllowListEntry.ID) break } } + if ipAllowListEntryId == "" { + return nil, fmt.Errorf("Organization %s does not have an IP allow list entry for %s.", orgName, valueToImport) + } + d.SetId(ipAllowListEntryId) err = resourceGithubOrganizationIpAllowListRead(d, meta) if err != nil { @@ -114,12 +116,10 @@ func resourceGithubOrganizationIpAllowListRead(d *schema.ResourceData, meta inte err := client.Query(ctx, &query, variables) if err != nil { - if ghErr, ok := err.(*github.ErrorResponse); ok { - if ghErr.Response.StatusCode == http.StatusNotFound { - log.Printf("[INFO] Removing ip allow list entry %s from state because it no longer exists in GitHub", d.Id()) - d.SetId("") - return nil - } + if githubv4IsNodeNotFoundError(err) { + log.Printf("[INFO] Removing ip allow list entry %s from state because it no longer exists in GitHub", d.Id()) + d.SetId("") + return nil } return err } From 8986ef558eb83f6779581caf6035ce46213dfb58 Mon Sep 17 00:00:00 2001 From: Doug Ayers Date: Mon, 3 Oct 2022 01:08:47 -0500 Subject: [PATCH 10/12] add graphql node id --- github/config.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/github/config.go b/github/config.go index 0c48f736fc..3f9efe11a2 100644 --- a/github/config.go +++ b/github/config.go @@ -26,6 +26,7 @@ type Config struct { type Owner struct { name string id int64 + nodeId string v3client *github.Client v4client *githubv4.Client StopContext context.Context @@ -113,6 +114,7 @@ func (c *Config) ConfigureOwner(owner *Owner) (*Owner, error) { if err == nil { if remoteOrg != nil { owner.id = remoteOrg.GetID() + owner.nodeId = remoteOrg.GetNodeID() owner.IsOrganization = true } } From 786b224484cfe0e217952a9fe65d011e70a56444 Mon Sep 17 00:00:00 2001 From: Doug Ayers Date: Mon, 3 Oct 2022 01:09:23 -0500 Subject: [PATCH 11/12] graphql mutation query fixes --- ...ource_github_organization_ip_allow_list.go | 41 ++++++++++--------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/github/resource_github_organization_ip_allow_list.go b/github/resource_github_organization_ip_allow_list.go index f7c76f7ab8..c9c8f3f70a 100644 --- a/github/resource_github_organization_ip_allow_list.go +++ b/github/resource_github_organization_ip_allow_list.go @@ -62,8 +62,9 @@ func resourceGithubOrganizationIpAllowListImport(d *schema.ResourceData, meta in return nil, err } - // We support importing by the actual ip allow list entry id or - // by the ip range itself because it must be unique. + // For user convenience, we support importing ip allow list entries + // by both their graphql node id or by their ip allow list value. + // For example, "IALE_kwHNBQk8ps2ADPDc" or "192.168.1.1/24" valueToImport := d.Id() ipAllowListEntryId := "" @@ -137,17 +138,17 @@ func resourceGithubOrganizationIpAllowListCreate(d *schema.ResourceData, meta in client := meta.(*Owner).v4client ctx := context.Background() - orgId := meta.(*Owner).id + orgId := meta.(*Owner).nodeId name := d.Get("name").(string) allowListValue := d.Get("allow_list_value").(string) isActive := d.Get("is_active").(bool) - var mutation struct { - CreateIpAllowListEntryInput struct { + var mutate struct { + CreateIpAllowListEntry struct { IpAllowListEntry struct { ID githubv4.String - } - } `graphql:"createIpAllowListEntryInput(input: $input)"` + } `graphql:"ipAllowListEntry"` + } `graphql:"createIpAllowListEntry(input: $input)"` } input := githubv4.CreateIpAllowListEntryInput{ @@ -157,12 +158,12 @@ func resourceGithubOrganizationIpAllowListCreate(d *schema.ResourceData, meta in IsActive: githubv4.Boolean(isActive), } - err := client.Mutate(ctx, &mutation, input, nil) + err := client.Mutate(ctx, &mutate, input, nil) if err != nil { return err } - d.SetId(string(mutation.CreateIpAllowListEntryInput.IpAllowListEntry.ID)) + d.SetId(string(mutate.CreateIpAllowListEntry.IpAllowListEntry.ID)) return resourceGithubOrganizationIpAllowListRead(d, meta) } @@ -175,12 +176,12 @@ func resourceGithubOrganizationIpAllowListUpdate(d *schema.ResourceData, meta in allowListValue := d.Get("allow_list_value").(string) isActive := d.Get("is_active").(bool) - var mutation struct { - UpdateIpAllowListEntryInput struct { + var mutate struct { + UpdateIpAllowListEntry struct { IpAllowListEntry struct { ID githubv4.String - } - } `graphql:"updateIpAllowListEntryInput(input: $input)"` + } `graphql:"ipAllowListEntry"` + } `graphql:"updateIpAllowListEntry(input: $input)"` } input := githubv4.UpdateIpAllowListEntryInput{ @@ -190,12 +191,12 @@ func resourceGithubOrganizationIpAllowListUpdate(d *schema.ResourceData, meta in IsActive: githubv4.Boolean(isActive), } - err := client.Mutate(ctx, &mutation, input, nil) + err := client.Mutate(ctx, &mutate, input, nil) if err != nil { return err } - d.SetId(string(mutation.UpdateIpAllowListEntryInput.IpAllowListEntry.ID)) + d.SetId(string(mutate.UpdateIpAllowListEntry.IpAllowListEntry.ID)) return resourceGithubOrganizationIpAllowListRead(d, meta) } @@ -204,18 +205,18 @@ func resourceGithubOrganizationIpAllowListDelete(d *schema.ResourceData, meta in client := meta.(*Owner).v4client ctx := context.Background() - var mutation struct { - DeleteIpAllowListEntryInput struct { + var mutate struct { + DeleteIpAllowListEntry struct { IpAllowListEntry struct { ID githubv4.String - } - } `graphql:"deleteIpAllowListEntryInput(input: $input)"` + } `graphql:"ipAllowListEntry"` + } `graphql:"deleteIpAllowListEntry(input: $input)"` } input := githubv4.DeleteIpAllowListEntryInput{ IPAllowListEntryID: d.Id(), } - err := client.Mutate(ctx, &mutation, input, nil) + err := client.Mutate(ctx, &mutate, input, nil) return err } From f09f6446c89c5b0a7be39e0039c9f1d1fa9cdc6f Mon Sep 17 00:00:00 2001 From: Doug Ayers Date: Mon, 3 Oct 2022 01:33:01 -0500 Subject: [PATCH 12/12] use for loop iterator value --- github/resource_github_organization_ip_allow_list.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/github/resource_github_organization_ip_allow_list.go b/github/resource_github_organization_ip_allow_list.go index c9c8f3f70a..4edc071ed0 100644 --- a/github/resource_github_organization_ip_allow_list.go +++ b/github/resource_github_organization_ip_allow_list.go @@ -68,8 +68,7 @@ func resourceGithubOrganizationIpAllowListImport(d *schema.ResourceData, meta in valueToImport := d.Id() ipAllowListEntryId := "" - for index := range ipAllowListEntries { - ipAllowListEntry := ipAllowListEntries[index] + for _, ipAllowListEntry := range ipAllowListEntries { if string(ipAllowListEntry.ID) == valueToImport || string(ipAllowListEntry.AllowListValue) == valueToImport { ipAllowListEntryId = string(ipAllowListEntry.ID) break