From fb6b12f2179eb3a0b4ab2dda2f5b8e48daf09bf2 Mon Sep 17 00:00:00 2001 From: Vlad H Date: Tue, 15 Oct 2024 15:37:36 +0300 Subject: [PATCH] Add Location Context CRUD (#50) * Add Location Context CRUD * Run go mod tidy * Add Location Context docs * Add advanced example --------- Co-authored-by: vladhanzha --- cloudconnexa/data_source_location_context.go | 66 +++++ cloudconnexa/provider.go | 38 +-- cloudconnexa/resource_location_context.go | 252 +++++++++++++++++++ docs/data-sources/location_context.md | 59 +++++ docs/resources/location_context.md | 68 +++++ examples/location_contexts.tf | 48 ++++ go.mod | 4 +- go.sum | 8 +- 8 files changed, 519 insertions(+), 24 deletions(-) create mode 100644 cloudconnexa/data_source_location_context.go create mode 100644 cloudconnexa/resource_location_context.go create mode 100644 docs/data-sources/location_context.md create mode 100644 docs/resources/location_context.md create mode 100644 examples/location_contexts.tf diff --git a/cloudconnexa/data_source_location_context.go b/cloudconnexa/data_source_location_context.go new file mode 100644 index 0000000..9d43346 --- /dev/null +++ b/cloudconnexa/data_source_location_context.go @@ -0,0 +1,66 @@ +package cloudconnexa + +import ( + "context" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/openvpn/cloudconnexa-go-client/v2/cloudconnexa" +) + +func dataSourceLocationContext() *schema.Resource { + return &schema.Resource{ + ReadContext: dataSourceLocationContextRead, + Schema: map[string]*schema.Schema{ + "id": { + Type: schema.TypeString, + Required: true, + }, + "name": { + Type: schema.TypeString, + Computed: true, + }, + "description": { + Type: schema.TypeString, + Computed: true, + }, + "user_groups_ids": { + Type: schema.TypeList, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + Computed: true, + }, + "ip_policy": { + Type: schema.TypeList, + Elem: ipPolicyConfig(), + Computed: true, + }, + "country_policy": { + Type: schema.TypeList, + Elem: countryPolicyConfig(), + Computed: true, + }, + "default_policy": { + Type: schema.TypeList, + Elem: defaultPolicyConfig(), + Computed: true, + }, + }, + } +} + +func dataSourceLocationContextRead(ctx context.Context, data *schema.ResourceData, i interface{}) diag.Diagnostics { + c := i.(*cloudconnexa.Client) + var diags diag.Diagnostics + var id = data.Get("id").(string) + context, err := c.LocationContexts.Get(id) + + if err != nil { + return diag.FromErr(err) + } + if context == nil { + return append(diags, diag.Errorf("Location Context with ID %s was not found", id)...) + } + setLocationContextData(data, context) + return nil +} diff --git a/cloudconnexa/provider.go b/cloudconnexa/provider.go index 2f80469..1d722f6 100644 --- a/cloudconnexa/provider.go +++ b/cloudconnexa/provider.go @@ -45,27 +45,29 @@ func Provider() *schema.Provider { }, }, ResourcesMap: map[string]*schema.Resource{ - "cloudconnexa_network": resourceNetwork(), - "cloudconnexa_connector": resourceConnector(), - "cloudconnexa_route": resourceRoute(), - "cloudconnexa_dns_record": resourceDnsRecord(), - "cloudconnexa_user": resourceUser(), - "cloudconnexa_host": resourceHost(), - "cloudconnexa_user_group": resourceUserGroup(), - "cloudconnexa_ip_service": resourceIPService(), - "cloudconnexa_application": resourceApplication(), + "cloudconnexa_network": resourceNetwork(), + "cloudconnexa_connector": resourceConnector(), + "cloudconnexa_route": resourceRoute(), + "cloudconnexa_dns_record": resourceDnsRecord(), + "cloudconnexa_user": resourceUser(), + "cloudconnexa_host": resourceHost(), + "cloudconnexa_user_group": resourceUserGroup(), + "cloudconnexa_ip_service": resourceIPService(), + "cloudconnexa_application": resourceApplication(), + "cloudconnexa_location_context": resourceLocationContext(), }, DataSourcesMap: map[string]*schema.Resource{ - "cloudconnexa_network": dataSourceNetwork(), - "cloudconnexa_connector": dataSourceConnector(), - "cloudconnexa_user": dataSourceUser(), - "cloudconnexa_user_group": dataSourceUserGroup(), - "cloudconnexa_vpn_region": dataSourceVpnRegion(), - "cloudconnexa_network_routes": dataSourceNetworkRoutes(), - "cloudconnexa_host": dataSourceHost(), - "cloudconnexa_ip_service": dataSourceIPService(), - "cloudconnexa_application": dataSourceApplication(), + "cloudconnexa_network": dataSourceNetwork(), + "cloudconnexa_connector": dataSourceConnector(), + "cloudconnexa_user": dataSourceUser(), + "cloudconnexa_user_group": dataSourceUserGroup(), + "cloudconnexa_vpn_region": dataSourceVpnRegion(), + "cloudconnexa_network_routes": dataSourceNetworkRoutes(), + "cloudconnexa_host": dataSourceHost(), + "cloudconnexa_ip_service": dataSourceIPService(), + "cloudconnexa_application": dataSourceApplication(), + "cloudconnexa_location_context": dataSourceLocationContext(), }, ConfigureContextFunc: providerConfigure, } diff --git a/cloudconnexa/resource_location_context.go b/cloudconnexa/resource_location_context.go new file mode 100644 index 0000000..df63c37 --- /dev/null +++ b/cloudconnexa/resource_location_context.go @@ -0,0 +1,252 @@ +package cloudconnexa + +import ( + "context" + "github.com/openvpn/cloudconnexa-go-client/v2/cloudconnexa" + + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" +) + +func resourceLocationContext() *schema.Resource { + return &schema.Resource{ + Description: "Use `cloudconnexa_location_context` to create a Location Context policy.", + CreateContext: resourceLocationContextCreate, + ReadContext: resourceLocationContextRead, + DeleteContext: resourceLocationContextDelete, + UpdateContext: resourceLocationContextUpdate, + Importer: &schema.ResourceImporter{ + StateContext: schema.ImportStatePassthroughContext, + }, + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + Description: "The Location Context name.", + }, + "description": { + Type: schema.TypeString, + Optional: true, + Default: "Managed by Terraform", + ValidateFunc: validation.StringLenBetween(1, 120), + Description: "The description for the UI. Defaults to `Managed by Terraform`.", + }, + "user_groups_ids": { + Type: schema.TypeList, + Required: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + Description: "List of User Group IDs assigned to this policy.", + }, + "ip_policy": { + Type: schema.TypeList, + MaxItems: 1, + Optional: true, + AtLeastOneOf: []string{"ip_policy", "country_policy"}, + Elem: ipPolicyConfig(), + }, + "country_policy": { + Type: schema.TypeList, + MaxItems: 1, + Optional: true, + Elem: countryPolicyConfig(), + }, + "default_policy": { + Type: schema.TypeList, + MaxItems: 1, + Required: true, + Elem: defaultPolicyConfig(), + }, + }, + } +} + +func ipPolicyConfig() *schema.Resource { + return &schema.Resource{ + Schema: map[string]*schema.Schema{ + "allowed": { + Type: schema.TypeBool, + Required: true, + }, + "ips": { + Type: schema.TypeList, + Required: true, + Elem: ipConfig(), + }, + }, + } +} + +func ipConfig() *schema.Resource { + return &schema.Resource{ + Schema: map[string]*schema.Schema{ + "ip": { + Type: schema.TypeString, + Required: true, + }, + "description": { + Type: schema.TypeString, + Optional: true, + }, + }, + } +} + +func countryPolicyConfig() *schema.Resource { + return &schema.Resource{ + Schema: map[string]*schema.Schema{ + "allowed": { + Type: schema.TypeBool, + Required: true, + }, + "countries": { + Type: schema.TypeList, + Required: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + }, + } +} + +func defaultPolicyConfig() *schema.Resource { + return &schema.Resource{ + Schema: map[string]*schema.Schema{ + "allowed": { + Type: schema.TypeBool, + Required: true, + }, + }, + } +} + +func resourceLocationContextCreate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + c := m.(*cloudconnexa.Client) + var diags diag.Diagnostics + dr := resourceDataToLocationContext(d) + response, err := c.LocationContexts.Create(dr) + if err != nil { + return append(diags, diag.FromErr(err)...) + } + d.SetId(response.Id) + return diags +} + +func resourceLocationContextRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + c := m.(*cloudconnexa.Client) + var diags diag.Diagnostics + recordId := d.Id() + lc, err := c.LocationContexts.Get(recordId) + if err != nil { + return append(diags, diag.FromErr(err)...) + } + if lc == nil { + d.SetId("") + } else { + setLocationContextData(d, lc) + } + return diags +} + +func resourceLocationContextUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + c := m.(*cloudconnexa.Client) + var diags diag.Diagnostics + lc := resourceDataToLocationContext(d) + _, err := c.LocationContexts.Update(d.Id(), lc) + if err != nil { + return append(diags, diag.FromErr(err)...) + } + return diags +} + +func resourceLocationContextDelete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + c := m.(*cloudconnexa.Client) + var diags diag.Diagnostics + routeId := d.Id() + err := c.LocationContexts.Delete(routeId) + if err != nil { + return append(diags, diag.FromErr(err)...) + } + return diags +} + +func setLocationContextData(d *schema.ResourceData, lc *cloudconnexa.LocationContext) { + d.SetId(lc.Id) + d.Set("name", lc.Name) + d.Set("description", lc.Description) + d.Set("user_groups_ids", lc.UserGroupsIds) + + if lc.IpPolicy != nil { + ipPolicy := make(map[string]interface{}) + ipPolicy["allowed"] = lc.IpPolicy.Allowed + var ips []interface{} + for _, ip := range lc.IpPolicy.Ips { + ips = append(ips, map[string]interface{}{ + "ip": ip.Ip, + "description": ip.Description, + }) + } + ipPolicy["ips"] = ips + d.Set("ip_policy", []interface{}{ipPolicy}) + } + + if lc.CountryPolicy != nil { + countryPolicy := make(map[string]interface{}) + countryPolicy["allowed"] = lc.CountryPolicy.Allowed + countryPolicy["countries"] = lc.CountryPolicy.Countries + d.Set("country_policy", []interface{}{countryPolicy}) + } + + defaultPolicy := make(map[string]interface{}) + defaultPolicy["allowed"] = lc.DefaultPolicy.Allowed + d.Set("default_policy", []interface{}{defaultPolicy}) +} + +func resourceDataToLocationContext(data *schema.ResourceData) *cloudconnexa.LocationContext { + defaultPolicyData := data.Get("default_policy").([]interface{})[0].(map[string]interface{}) + defaultPolicy := &cloudconnexa.DefaultPolicy{ + Allowed: defaultPolicyData["allowed"].(bool), + } + + response := &cloudconnexa.LocationContext{ + Id: data.Id(), + Name: data.Get("name").(string), + Description: data.Get("description").(string), + DefaultPolicy: defaultPolicy, + } + + for _, id := range data.Get("user_groups_ids").([]interface{}) { + response.UserGroupsIds = append(response.UserGroupsIds, id.(string)) + } + + ipPolicyList := data.Get("ip_policy").([]interface{}) + if len(ipPolicyList) > 0 { + ipPolicy := &cloudconnexa.IpPolicy{} + ipPolicyData := ipPolicyList[0].(map[string]interface{}) + ipPolicy.Allowed = ipPolicyData["allowed"].(bool) + for _, ip := range ipPolicyData["ips"].([]interface{}) { + ipPolicy.Ips = append(ipPolicy.Ips, cloudconnexa.Ip{ + Ip: ip.(map[string]interface{})["ip"].(string), + Description: ip.(map[string]interface{})["description"].(string), + }) + } + response.IpPolicy = ipPolicy + } + + countryPolicyList := data.Get("country_policy").([]interface{}) + if len(countryPolicyList) > 0 && countryPolicyList[0] != nil { + countryPolicyData := data.Get("country_policy").([]interface{})[0].(map[string]interface{}) + countryPolicy := &cloudconnexa.CountryPolicy{ + Allowed: countryPolicyData["allowed"].(bool), + } + for _, country := range countryPolicyData["countries"].([]interface{}) { + countryPolicy.Countries = append(countryPolicy.Countries, country.(string)) + } + response.CountryPolicy = countryPolicy + } + + return response +} diff --git a/docs/data-sources/location_context.md b/docs/data-sources/location_context.md new file mode 100644 index 0000000..cf385c9 --- /dev/null +++ b/docs/data-sources/location_context.md @@ -0,0 +1,59 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "cloudconnexa_location_context Data Source - terraform-provider-cloudconnexa" +subcategory: "" +description: |- + +--- + +# cloudconnexa_location_context (Data Source) + + + + + + +## Schema + +### Read-Only + +- `country_policy` (List of Object) (see [below for nested schema](#nestedatt--country_policy)) +- `default_policy` (List of Object) (see [below for nested schema](#nestedatt--default_policy)) +- `description` (String) +- `id` (String) The ID of this resource. +- `ip_policy` (List of Object) (see [below for nested schema](#nestedatt--ip_policy)) +- `name` (String) +- `user_groups_ids` (List of String) + + +### Nested Schema for `country_policy` + +Read-Only: + +- `allowed` (Boolean) +- `countries` (List of String) + + + +### Nested Schema for `default_policy` + +Read-Only: + +- `allowed` (Boolean) + + + +### Nested Schema for `ip_policy` + +Read-Only: + +- `allowed` (Boolean) +- `ips` (List of Object) (see [below for nested schema](#nestedobjatt--ip_policy--ips)) + + +### Nested Schema for `ip_policy.ips` + +Read-Only: + +- `description` (String) +- `ip` (String) diff --git a/docs/resources/location_context.md b/docs/resources/location_context.md new file mode 100644 index 0000000..06f13a7 --- /dev/null +++ b/docs/resources/location_context.md @@ -0,0 +1,68 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "cloudconnexa_location_context Resource - terraform-provider-cloudconnexa" +subcategory: "" +description: |- + Use cloudconnexa_location_context to create a Location Context policy. +--- + +# cloudconnexa_location_context (Resource) + +Use `cloudconnexa_location_context` to create a Location Context policy. + + + + +## Schema + +### Required + +- `default_policy` (Block List, Min: 1, Max: 1) (see [below for nested schema](#nestedblock--default_policy)) +- `name` (String) The Location Context name. +- `user_groups_ids` (List of String) List of User Group IDs assigned to this policy. + +### Optional + +- `country_policy` (Block List, Max: 1) (see [below for nested schema](#nestedblock--country_policy)) +- `description` (String) The description for the UI. Defaults to `Managed by Terraform`. +- `ip_policy` (Block List, Max: 1) (see [below for nested schema](#nestedblock--ip_policy)) + +### Read-Only + +- `id` (String) The ID of this resource. + + +### Nested Schema for `default_policy` + +Required: + +- `allowed` (Boolean) + + + +### Nested Schema for `country_policy` + +Required: + +- `allowed` (Boolean) +- `countries` (List of String) + + + +### Nested Schema for `ip_policy` + +Required: + +- `allowed` (Boolean) +- `ips` (Block List, Min: 1) (see [below for nested schema](#nestedblock--ip_policy--ips)) + + +### Nested Schema for `ip_policy.ips` + +Required: + +- `ip` (String) + +Optional: + +- `description` (String) diff --git a/examples/location_contexts.tf b/examples/location_contexts.tf new file mode 100644 index 0000000..51ae5dc --- /dev/null +++ b/examples/location_contexts.tf @@ -0,0 +1,48 @@ +resource "cloudconnexa_location_context" "this" { + name = "Location Context Policy" + description = "Description for Location Context Policy" + user_groups_ids = [] + ip_policy { + allowed = true + ips { + ip = "10.10.0.0/16" + description = "Test subnet" + } + ips { + ip = "10.20.0.0/16" + description = "Test subnet 2" + } + } + country_policy { + allowed = true + countries = ["US", "GB"] + } + default_policy { + allowed = false + } +} + +#Advanced +resource "cloudconnexa_location_context" "this" { + name = "Location Context Policy" + description = "Description for Location Context Policy" + user_groups_ids = values(cloudconnexa_user_group.this)[*].id + ip_policy { + allowed = true + ips { + ip = "10.10.0.0/16" + description = "Test subnet" + } + ips { + ip = "10.20.0.0/16" + description = "Test subnet 2" + } + } + country_policy { + allowed = true + countries = ["US", "GB"] + } + default_policy { + allowed = false + } +} \ No newline at end of file diff --git a/go.mod b/go.mod index 0a63c13..b8f6d8c 100644 --- a/go.mod +++ b/go.mod @@ -8,7 +8,7 @@ require ( github.com/gruntwork-io/terratest v0.47.2 github.com/hashicorp/go-cty v1.4.1-0.20200414143053-d3edf31b6320 github.com/hashicorp/terraform-plugin-sdk/v2 v2.34.0 - github.com/openvpn/cloudconnexa-go-client/v2 v2.0.15 + github.com/openvpn/cloudconnexa-go-client/v2 v2.0.16 github.com/stretchr/testify v1.9.0 ) @@ -89,7 +89,7 @@ require ( golang.org/x/sync v0.7.0 // indirect golang.org/x/sys v0.20.0 // indirect golang.org/x/text v0.15.0 // indirect - golang.org/x/time v0.6.0 // indirect + golang.org/x/time v0.7.0 // indirect golang.org/x/tools v0.20.0 // indirect google.golang.org/api v0.162.0 // indirect google.golang.org/appengine v1.6.8 // indirect diff --git a/go.sum b/go.sum index 16d0cfb..2c3079d 100644 --- a/go.sum +++ b/go.sum @@ -485,8 +485,8 @@ github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zx github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/oklog/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA= github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DVU= -github.com/openvpn/cloudconnexa-go-client/v2 v2.0.15 h1:AcU6zhs8WvOPQ7s3kJ+6ikk6RsJzKhgjTrMFnkIezdo= -github.com/openvpn/cloudconnexa-go-client/v2 v2.0.15/go.mod h1:WJZU+oOwHUYoO7Q2tFnGavKGbhrAvo8OEgvvr9EEtCw= +github.com/openvpn/cloudconnexa-go-client/v2 v2.0.16 h1:KX3OEU1RegZZ4E9TAgMFsP/KtFiugaRfLnnLMb9H0uI= +github.com/openvpn/cloudconnexa-go-client/v2 v2.0.16/go.mod h1:aky93nehxFyRZ/rE6sjhQ7aLW9IU7XHOpAMCFbTgYIw= github.com/pjbgf/sha1cd v0.3.0 h1:4D5XXmUUBUl/xQ6IjCkEAbqXskkq/4O7LmGn0AqMDs4= github.com/pjbgf/sha1cd v0.3.0/go.mod h1:nZ1rrWOcGJ5uZgEEVL1VUM9iRQiZvWdbZjkKyFzPPsI= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -797,8 +797,8 @@ golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U= -golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +golang.org/x/time v0.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ= +golang.org/x/time v0.7.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=