Skip to content
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 managing organization ip allow list entries (Fixes #1067) #1315

Closed
wants to merge 12 commits into from
2 changes: 2 additions & 0 deletions github/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
}
}
Expand Down
75 changes: 6 additions & 69 deletions github/data_source_github_organization_ip_allow_list.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
package github

import (
"context"
"strconv"

"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
"github.com/shurcooL/githubv4"
)

func dataSourceGithubOrganizationIpAllowList() *schema.Resource {
Expand Down Expand Up @@ -49,77 +48,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(strconv.FormatInt(orgId, 10))
d.Set("ip_allow_list", ipAllowListEntries)

return nil
}
1 change: 1 addition & 0 deletions github/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ func Provider() terraform.ResourceProvider {
"github_issue_label": resourceGithubIssueLabel(),
"github_membership": resourceGithubMembership(),
"github_organization_block": resourceOrganizationBlock(),
"github_organization_ip_allow_list_entry": resourceGithubOrganizationIpAllowList(),
"github_organization_project": resourceGithubOrganizationProject(),
"github_organization_settings": resouceGithubOrganizationSettings(),
"github_organization_webhook": resourceGithubOrganizationWebhook(),
Expand Down
221 changes: 221 additions & 0 deletions github/resource_github_organization_ip_allow_list.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,221 @@
package github

import (
"context"
"fmt"
"log"

"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{
"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
}

// 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 := ""

for _, ipAllowListEntry := range ipAllowListEntries {
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 {
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 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
}

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).nodeId
name := d.Get("name").(string)
allowListValue := d.Get("allow_list_value").(string)
isActive := d.Get("is_active").(bool)

var mutate struct {
CreateIpAllowListEntry struct {
IpAllowListEntry struct {
ID githubv4.String
} `graphql:"ipAllowListEntry"`
} `graphql:"createIpAllowListEntry(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, &mutate, input, nil)
if err != nil {
return err
}

d.SetId(string(mutate.CreateIpAllowListEntry.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 mutate struct {
UpdateIpAllowListEntry struct {
IpAllowListEntry struct {
ID githubv4.String
} `graphql:"ipAllowListEntry"`
} `graphql:"updateIpAllowListEntry(input: $input)"`
}

input := githubv4.UpdateIpAllowListEntryInput{
IPAllowListEntryID: d.Id(),
Name: (*githubv4.String)(&name),
AllowListValue: githubv4.String(allowListValue),
IsActive: githubv4.Boolean(isActive),
}

err := client.Mutate(ctx, &mutate, input, nil)
if err != nil {
return err
}

d.SetId(string(mutate.UpdateIpAllowListEntry.IpAllowListEntry.ID))

return resourceGithubOrganizationIpAllowListRead(d, meta)
}

func resourceGithubOrganizationIpAllowListDelete(d *schema.ResourceData, meta interface{}) error {
client := meta.(*Owner).v4client
ctx := context.Background()

var mutate struct {
DeleteIpAllowListEntry struct {
IpAllowListEntry struct {
ID githubv4.String
} `graphql:"ipAllowListEntry"`
} `graphql:"deleteIpAllowListEntry(input: $input)"`
}

input := githubv4.DeleteIpAllowListEntryInput{
IPAllowListEntryID: d.Id(),
}

err := client.Mutate(ctx, &mutate, input, nil)
return err
}
11 changes: 11 additions & 0 deletions github/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,17 @@ func buildChecksumID(v []string) string {
return fmt.Sprintf("%x", bs)
}

func expandNestedSet(m map[string]interface{}, target string) []string {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Moved from github/util_v4.go because this isn't v4 api specific (as far as I can tell) and looked similar to the expandStringList func that was here, too.

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 {
Expand Down
Loading