Skip to content

Commit

Permalink
GH-150 Add IP Ranges data source (#151)
Browse files Browse the repository at this point in the history
Along with docs.
  • Loading branch information
zahiar authored Apr 23, 2023
1 parent da2ec73 commit a9eaf46
Show file tree
Hide file tree
Showing 6 changed files with 234 additions and 0 deletions.
122 changes: 122 additions & 0 deletions bitbucket/data_source_bitbucket_ip_ranges.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
package bitbucket

import (
"context"
"encoding/json"
"fmt"
"golang.org/x/exp/slices"
"net/http"

"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
)

type IpRanges struct {
CreationDate string `json:"creationDate"`
SyncToken int64 `json:"syncToken"`
Items []IpRangeItem `json:"items"`
}

type IpRangeItem struct {
Network string `json:"network"`
MaskLen int64 `json:"mask_len"`
Cidr string `json:"cidr"`
Mask string `json:"mask"`
Region []string `json:"region"`
Product []string `json:"product"`
Direction []string `json:"direction"`
}

func dataSourceBitbucketIpRanges() *schema.Resource {
return &schema.Resource{
ReadContext: dataSourceBitbucketIpRangesRead,
Schema: map[string]*schema.Schema{
"ranges": {
Type: schema.TypeSet,
Computed: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"network": {
Description: "The IP Address.",
Type: schema.TypeString,
Computed: true,
},
"cidr": {
Description: "The CIDR.",
Type: schema.TypeString,
Computed: true,
},
"mask": {
Description: "The Mask.",
Type: schema.TypeString,
Computed: true,
},
"mask_len": {
Description: "The Mask Length.",
Type: schema.TypeInt,
Computed: true,
},
"regions": {
Description: "A list of regions this IP Address resides in. Follows AWS Region Code Notation.",
Type: schema.TypeSet,
Elem: &schema.Schema{Type: schema.TypeString},
Computed: true,
},
"directions": {
Description: "A list defining if the IP address is ingress, egress or both.",
Type: schema.TypeSet,
Elem: &schema.Schema{Type: schema.TypeString},
Computed: true,
},
},
},
},
},
}
}

func dataSourceBitbucketIpRangesRead(ctx context.Context, resourceData *schema.ResourceData, meta interface{}) diag.Diagnostics {
req, err := http.Get("https://ip-ranges.atlassian.com/")
if err != nil {
return diag.FromErr(fmt.Errorf("unable to get ip ranges with error: %s", err))
}
defer req.Body.Close()

if req.StatusCode != 200 {
return diag.FromErr(fmt.Errorf("unable to get ip ranges: response code was not 200"))
}

var ipRanges IpRanges
err = json.NewDecoder(req.Body).Decode(&ipRanges)
if err != nil {
return diag.FromErr(fmt.Errorf("unable to decode ip ranges with error: %s", err))
}

resourceData.SetId(fmt.Sprintf("%d", ipRanges.SyncToken))
_ = resourceData.Set("ranges", mapToResource(ipRanges.Items))

return nil
}

func mapToResource(ipRanges []IpRangeItem) []interface{} {
var resourceList []interface{}

for _, item := range ipRanges {
if slices.Contains(item.Product, "bitbucket") {
resourceList = append(resourceList, map[string]interface{}{
"network": item.Network,
"mask_len": item.MaskLen,
"cidr": item.Cidr,
"mask": item.Mask,
"regions": item.Region,
"directions": item.Direction,
})
}
}

if len(resourceList) == 0 {
return nil
}

return resourceList
}
87 changes: 87 additions & 0 deletions bitbucket/data_source_bitbucket_ip_ranges_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
package bitbucket

import (
"testing"

"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
"github.com/stretchr/testify/assert"
)

func TestAccBitbucketIpRangesDataSource_basic(t *testing.T) {
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
ProviderFactories: testAccProviders,
Steps: []resource.TestStep{
{
Config: `data "bitbucket_ip_ranges" "testacc" {}`,
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttrSet("data.bitbucket_ip_ranges.testacc", "ranges.#"),

resource.TestCheckResourceAttrSet("data.bitbucket_ip_ranges.testacc", "ranges.0.cidr"),
resource.TestCheckResourceAttrSet("data.bitbucket_ip_ranges.testacc", "ranges.0.directions.#"),
resource.TestCheckResourceAttrSet("data.bitbucket_ip_ranges.testacc", "ranges.0.mask"),
resource.TestCheckResourceAttrSet("data.bitbucket_ip_ranges.testacc", "ranges.0.mask_len"),
resource.TestCheckResourceAttrSet("data.bitbucket_ip_ranges.testacc", "ranges.0.network"),
resource.TestCheckResourceAttrSet("data.bitbucket_ip_ranges.testacc", "ranges.0.regions.#"),
),
},
},
})
}

func TestMapToResource(t *testing.T) {
var emptyRange []IpRangeItem
assert.Empty(t, mapToResource(emptyRange))

var nonBitBucketIpRange []IpRangeItem
nonBitBucketIpRange = append(nonBitBucketIpRange, IpRangeItem{Product: []string{"notBitbucket"}})
assert.Empty(t, mapToResource(nonBitBucketIpRange))

var bitbucketIpRanges []IpRangeItem
bitbucketIpRanges = append(
bitbucketIpRanges,
IpRangeItem{
Network: "123.456.789.123",
MaskLen: 8,
Cidr: "123.456.789.123/8",
Mask: "255.255.255.255",
Region: []string{"eu-west-1"},
Product: []string{"bitbucket"},
Direction: []string{"ingress"},
},
IpRangeItem{
Network: "456.789.123.456",
MaskLen: 16,
Cidr: "456.789.123.456/16",
Mask: "255.255.255.255",
Region: []string{"eu-west-2"},
Product: []string{"bitbucket"},
Direction: []string{"egress"},
},
)

var expected []interface{}
expected = append(
expected,
map[string]interface{}{
"network": "123.456.789.123",
"mask_len": int64(8),
"cidr": "123.456.789.123/8",
"mask": "255.255.255.255",
"regions": []string{"eu-west-1"},
"directions": []string{"ingress"},
},
)
expected = append(
expected,
map[string]interface{}{
"network": "456.789.123.456",
"mask_len": int64(16),
"cidr": "456.789.123.456/16",
"mask": "255.255.255.255",
"regions": []string{"eu-west-2"},
"directions": []string{"egress"},
},
)
assert.Equal(t, expected, mapToResource(bitbucketIpRanges))
}
1 change: 1 addition & 0 deletions bitbucket/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ func Provider() *schema.Provider {
"bitbucket_deployment_variable": dataSourceBitbucketDeploymentVariable(),
"bitbucket_group": dataSourceBitbucketGroup(),
"bitbucket_group_permission": dataSourceBitbucketGroupPermission(),
"bitbucket_ip_ranges": dataSourceBitbucketIpRanges(),
"bitbucket_pipeline_variable": dataSourceBitbucketPipelineVariable(),
"bitbucket_project": dataSourceBitbucketProject(),
"bitbucket_repository": dataSourceBitbucketRepository(),
Expand Down
21 changes: 21 additions & 0 deletions docs/data-sources/bitbucket_ip_ranges.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Data Source: bitbucket_ip_ranges
Use this data source to get a list of IP Address information for Bitbucket Cloud, this can be used as part of allow-lists.
See here for more information: https://support.atlassian.com/bitbucket-cloud/docs/what-are-the-bitbucket-cloud-ip-addresses-i-should-use-to-configure-my-corporate-firewall/

## Example Usage
```hcl
data "bitbucket_ip_ranges" "example" {}
```

## Argument Reference
No arguments are passed in.

## Attribute Reference
The following attributes are exported:
* `ranges` - A list of IP Address information, of which each entry in the list contains:
* `network` - The IP Address.
* `cidr` - The CIDR.
* `mask` - The Mask.
* `mask_len` - The Mask Length.
* `regions` - A list of regions this IP Address resides in. Follows AWS Region Code Notation.
* `directions` - A list defining if the IP address is ingress, egress or both.
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ require (
github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
github.com/zclconf/go-cty v1.13.1 // indirect
golang.org/x/crypto v0.7.0 // indirect
golang.org/x/exp v0.0.0-20230420155640-133eef4313cb // indirect
golang.org/x/mod v0.8.0 // indirect
golang.org/x/net v0.9.0 // indirect
golang.org/x/oauth2 v0.7.0 // indirect
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,8 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y
golang.org/x/crypto v0.5.0/go.mod h1:NK/OQwhpMQP3MwtdjgLlYHnH9ebylxKWv3e0fK+mkQU=
golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A=
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
golang.org/x/exp v0.0.0-20230420155640-133eef4313cb h1:rhjz/8Mbfa8xROFiH+MQphmAmgqRM0bOMnytznhWEXk=
golang.org/x/exp v0.0.0-20230420155640-133eef4313cb/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.8.0 h1:LUYupSeNrTNCGzR/hVBk2NHZO4hXcVaW1k4Qx7rjPx8=
Expand Down

0 comments on commit a9eaf46

Please sign in to comment.