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

New Resource: azurerm_postgresql_virtual_network_rule #1774

Merged
merged 20 commits into from
Sep 4, 2018

Conversation

ac-astuartkregor
Copy link
Contributor

@ac-astuartkregor ac-astuartkregor commented Aug 15, 2018

This PR allows users to create vnet rules akin to azurerm_sql_virtual_network_rule so that service endpoints can be used to access PostgreSQL instances.

The code was copied and adapted from the azurerm_sql_virtual_network_rule.

I'd be very grateful for any feedback especially if there are any styling or convention considerations.

My thanks to @lfshr and @tombuildsstuff for their advice and input.

(Fixes: #1571 )

@lfshr
Copy link
Contributor

lfshr commented Aug 15, 2018

This fixes #1571 (title didn't seem to link the issue)

@ac-astuartkregor
Copy link
Contributor Author

Good spot, although the PR title should mean the issue is closed when the PR gets merged.

@metacpp metacpp requested a review from WodansSon August 15, 2018 22:24
"server_name": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
Copy link
Collaborator

Choose a reason for hiding this comment

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

Can we add some validation here for non-zero values?

ValidateFunc: validation.NoZeroValues,

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@jeffreyCline Do you have any suggestions for testing that this validation has been applied to this/these attribute(s)? Is it worth testing such a thing? Sorry if this is a daft question, however I'm not sure how it could be tested given the style of the existing unit and acceptance tests.


"subnet_id": {
Type: schema.TypeString,
Required: true,
Copy link
Collaborator

Choose a reason for hiding this comment

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

Can we add some validation on the resource id here?

ValidateFunc: azure.ValidateResourceID,

"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/helper/schema"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/response"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils"
Copy link
Collaborator

Choose a reason for hiding this comment

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

Can we add the azure import to get access to the helper validation methods?

"github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure"

Copy link
Contributor Author

@ac-astuartkregor ac-astuartkregor Aug 16, 2018

Choose a reason for hiding this comment

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

Would this be under the circumstances where the validation function is pulled out of this resource and made more generally available?

Edit: Sorry I misread your previous comment and saw that the validation function was azure specific.

This function checks the format of the PostgreSQL Virtual Network Rule Name to make sure that
it does not contain any potentially invalid values.
*/
func validatePostgreSQLVirtualNetworkRuleName(v interface{}, k string) (ws []string, errors []error) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Where did you find the definition for these constraints? Because according to the general naming rules and restrictions it states:

Name Length Case Allowable characters Suggested Pattern Example
Network Security Group Rule 1 - 80 Case insensitive Alphanumeric, hyphen, underscore, and period <descriptive context> sql-allow
  • Note: They did not have an example for this exact resource so I picked the next best one that seemed reasonable/similar.

In general, avoid having any special characters (- or _) as the first or last character in any name. These characters will cause most validation rules to fail. I am also wondering if we could abstract this out to a general validation method in the azure package and use it on other resources as well?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

These rules are present in the resource_arm_sql_virtual_network_rule which this resource has been copied from. They overall seem to be quite sensible, although I would be happy to adjust them to be a bit more stringent, especially around length or beginning/ending characters.

In terms of abstracting out this validation, this might be worth doing although it'd be worth looking at the existing resources where the validation might apply to assess how valuable that would be.

Copy link
Collaborator

Choose a reason for hiding this comment

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

This is actually fine for now, In the near future I will implement the validation functions in the azure helper package based on Microsoft's Naming conventions best practices. Once I have completed that work I will update this resource to use the new validation rules.

}

_, err := client.CreateOrUpdate(ctx, resourceGroup, serverName, name, parameters)
if err != nil {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Can we combine these into a single line?

if _, err := client.CreateOrUpdate(ctx, resourceGroup, serverName, name, parameters); err != nil {

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'm not sure I fully understand the motivation for combining these lines, could you please advise?

Copy link
Collaborator

Choose a reason for hiding this comment

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

The err variable is scoped only to the if block that handles it, and I believe that the code is a little easier to read this way as well. Since the err variable is not needed beyond the scope of the check it makes sense to combine the lines into a single line if check.

Copy link
Contributor Author

@ac-astuartkregor ac-astuartkregor Aug 17, 2018

Choose a reason for hiding this comment

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

Thanks for clarifying. I suspect I do not feel as strongly on this issue as you do, so I'm happy to change it. I'm not a seasoned Go programmer, so I am not particularly used to its syntax or conventions and as such, the current code (split across 2 lines) is a lot easier for me to read as opposed to combining the lines. However, because of that, I don't know how go is 'supposed' to look either. I don't mind changing it, however I suspect less experienced people will find it a bit more awkward (I'm biased, I know).

In terms of scoping, that's not something I'd have thought of. Does Go give particularly strong emphasis to scope beyond the expectation of dealing with it? Are there expected patterns for scopes and so on? Sorry for all these questions but you've piqued my curiosity. Am I correct in guessing that if the if comes before the variable declaration, that the variable is then scoped to that and only that block/line?

Edit: It would also help if I fully read the code myself 😄 The err is used later on line 83 and is needed outside of the scope of the if block.

Edit: The tests are what clued me into the extra use of err.

Copy link
Collaborator

Choose a reason for hiding this comment

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

I'm not going to push this issue because this is the way the original resource was coded and it's not your fault. However, the use of err on line 84 is pointless because if the code gets to this point the err will always be nil. I would put the if check in a single line and remove the err variable from the log output completely.

log.Printf("[DEBUG] Waiting for PostgreSQL Virtual Network Rule %q (PostgreSQL Server: %q, Resource Group: %q) to become ready.", name, serverName, resourceGroup)

Yes Go is big on scope and making the code as short and compact as possible. Go doesn't like to have variables hanging around any longer than they absolutely have too.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

🤦‍♂️ Good point, I totally hadn't considered that, my bad.

Ok, thanks for the input, I'll keep that in mind.


The following arguments are supported:

* `name` - (Required) The name of the SQL virtual network rule. Changing this forces a new resource to be created. Cannot be empty and must only contain alphanumeric characters and hyphens. Cannot start with a number, and cannot start or end with a hyphen.
Copy link
Collaborator

Choose a reason for hiding this comment

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

Can we move the Changing this forces a new resource to be created. to the end of the sentence?

  • name - (Required) The name of the SQL virtual network rule. Cannot be empty and must only contain alphanumeric characters and hyphens. Cannot start with a number, and cannot start or end with a hyphen. Changing this forces a new resource to be created.

@ac-astuartkregor
Copy link
Contributor Author

Hi @jeffreyCline, many thanks for your feedback. I'll look at addressing this today. I should point out that since this is almost a carbon copy of sql virtual network rule resource, you may wish to also apply the same refactorings to that as well.

Copy link
Collaborator

@WodansSon WodansSon left a comment

Choose a reason for hiding this comment

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

@ac-astuartkregor this mostly LGTM, but I left a comment on a minor issue, thanks.

}

_, err := client.CreateOrUpdate(ctx, resourceGroup, serverName, name, parameters)
if err != nil {
Copy link
Collaborator

Choose a reason for hiding this comment

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

The err variable is scoped only to the if block that handles it, and I believe that the code is a little easier to read this way as well. Since the err variable is not needed beyond the scope of the check it makes sense to combine the lines into a single line if check.

This function checks the format of the PostgreSQL Virtual Network Rule Name to make sure that
it does not contain any potentially invalid values.
*/
func validatePostgreSQLVirtualNetworkRuleName(v interface{}, k string) (ws []string, errors []error) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

This is actually fine for now, In the near future I will implement the validation functions in the azure helper package based on Microsoft's Naming conventions best practices. Once I have completed that work I will update this resource to use the new validation rules.

}

_, err := client.CreateOrUpdate(ctx, resourceGroup, serverName, name, parameters)
if err != nil {
Copy link
Collaborator

Choose a reason for hiding this comment

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

I'm not going to push this issue because this is the way the original resource was coded and it's not your fault. However, the use of err on line 84 is pointless because if the code gets to this point the err will always be nil. I would put the if check in a single line and remove the err variable from the log output completely.

log.Printf("[DEBUG] Waiting for PostgreSQL Virtual Network Rule %q (PostgreSQL Server: %q, Resource Group: %q) to become ready.", name, serverName, resourceGroup)

Yes Go is big on scope and making the code as short and compact as possible. Go doesn't like to have variables hanging around any longer than they absolutely have too.

Copy link
Collaborator

@WodansSon WodansSon left a comment

Choose a reason for hiding this comment

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

@ac-astuartkregor as part of validating the new resource I ran the tests. They all failed, if you could get the tests passing that would be great.

@WodansSon
Copy link
Collaborator

image

@WodansSon
Copy link
Collaborator

@ac-astuartkregor Yes we need to remove the ignore_vnet_service_endpoint attribute completely. I am following up with the internal Microsoft service team to get this fixed, but I agree with Tom that there is no reason to block this from shipping because of this one issue. If you can remove that attribute from the resource and the documentation I think we will be good to go.

@ac-astuartkregor
Copy link
Contributor Author

Cheers @jeffreyCline, working on it.

@ghost ghost added the size/XXL label Aug 29, 2018
Copy link
Collaborator

@WodansSon WodansSon left a comment

Choose a reason for hiding this comment

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

LGTM

@tombuildsstuff tombuildsstuff changed the title Fixes #1571. Add support for PostgreSQL vnet rule. New Resource: azurerm_sql_virtual_network_rule Aug 30, 2018
Copy link
Member

@tombuildsstuff tombuildsstuff left a comment

Choose a reason for hiding this comment

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

hey @ac-astuartkregor

Thanks for pushing the updates to this PR - if we can flip the default from true to false for the IgnoreMissingVnetServiceEndpoint field for the moment (and remove the field from the Read function, since it's not currently present in the schema) - this otherwise LGTM 👍

Thanks!

azurerm/resource_arm_postgresql_virtual_network_rule.go Outdated Show resolved Hide resolved

* `subnet_id` - (Required) The ID of the subnet that the PostgreSQL server will be connected to.

~> **NOTE:** The resource is configured with `ignore_missing_vnet_service_endpoint` set to `true`, meaning the deployment will succeed even if the target subnet does not contain the `Microsoft.Sql` endpoint in the `service_endpoints` array. This attribute will be introduced once the API behaviour is consistent.
Copy link
Member

Choose a reason for hiding this comment

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

given this defaults to false in the other resources, I think it'd be sensible to make this false by default for the moment (since otherwise this'll cause conflicts for users when we introduce this field with a default of false when it's fixed) - what do you think?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I had a brief debate about this with myself and what happens when the endpoint is missing.

I see where you're coming from and perhaps it would be best to do that. I was going to ask what you think is the best approach from a user experience perspective.

  • If IgnoreMissingVnetServiceEndpoint is false and the endpoint is missing, the resource creation will timeout.
  • If IgnoreMissingVnetServiceEndpoint is true and the endpoing is missing, the resource creation will succeed but the functionality will be broken.

I suspect your suggestion is probably the better choice but I'm curious as to what you think.

Copy link
Member

Choose a reason for hiding this comment

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

so the other option would be to temporarily verify that the Subnet contains the Service Endpoint, e.g. something like this in the Create function:

subnetsClient := meta.(*ArmClient).subnetClient

	subnetResourceGroup := subnetParsedId.ResourceGroup
	virtualNetwork := subnetParsedId.Path["virtualNetworks"]
	subnetName := subnetParsedId.Path["subnets"]
	resp, err := subnetsClient.Get(ctx, subnetResourceGroup, virtualNetwork, subnetName, "")
	if err != nil {
		if utils.ResponseWasNotFound(resp.Response) {
			return fmt.Errorf("Subnet with ID %q was not found: %+v", subnetId, err)
		}

		return fmt.Errorf("Error obtaining Subnet %q (Virtual Network %q / Resource Group %q: %+v", subnetName, virtualNetwork, subnetResourceGroup, err)
	}
	containsEndpoint := false
	if props := resp.SubnetPropertiesFormat; props != nil {
		if endpoints := props.ServiceEndpoints; endpoints != nil {
			for _, e := range *endpoints {
				if e.Service == nil {
					continue
				}

				if strings.EqualFold(*e.Service, "Microsoft.Sql") {
					containsEndpoint = true
					break
				}
			}
		}
	}
	if !containsEndpoint {
		return fmt.Errorf("Subnet %q (Virtual Network %q / Resource Group %q) must contain a Service Endpoint for `Microsoft.Sql`", subnetName, virtualNetwork, subnetResourceGroup)
	}

This would ensure the Endpoint would work - and we should be able to revert that once the API if fixed?

Copy link
Contributor Author

@ac-astuartkregor ac-astuartkregor Aug 30, 2018

Choose a reason for hiding this comment

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

Oh.

I think perhaps defaulting to false might be nicer? 🤣

Copy link
Member

Choose a reason for hiding this comment

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

whilst we've removed this, to ensure this fails gracefully I've pushed a commit (I hope you don't mind!) to add the above code to the Create method, to ensure the Subnet's in a usable state prior to creating the subnet rule

@ghost ghost added the size/XXL label Aug 30, 2018
@WodansSon
Copy link
Collaborator

@tombuildsstuff I removed the waiting-response tag since @ac-astuartkregor had committed the requested change to the PR.

@ghost ghost added the size/XXL label Sep 3, 2018
@ghost ghost added the size/XXL label Sep 3, 2018
@tombuildsstuff
Copy link
Member

Tests pass:

screenshot 2018-09-04 at 08 28 13

Copy link
Member

@tombuildsstuff tombuildsstuff left a comment

Choose a reason for hiding this comment

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

hey @ac-astuartkregor

Thanks for the work on this PR - I hope you don't mind I've pushed a commit to fix a couple of minor outstanding issues, but this now LGTM 👍

Thanks!

@tombuildsstuff tombuildsstuff changed the title New Resource: azurerm_sql_virtual_network_rule New Resource: azurerm_postgresql_virtual_network_rule Sep 4, 2018
@tombuildsstuff tombuildsstuff merged commit 8b1c9d1 into hashicorp:master Sep 4, 2018
tombuildsstuff added a commit that referenced this pull request Sep 4, 2018
@ac-astuartkregor ac-astuartkregor deleted the add_postgresql_vnet_rule branch September 4, 2018 07:51
@ac-astuartkregor
Copy link
Contributor Author

Hi @tombuildsstuff,
You're most welcome! Sorry that things were a bit sloppy in places and thank you for implementing the check on the subnet that you mentioned. Probably the best approach to be honest. Hopefully it won't be long before it can be removed.

Thanks for merging too :)

@ghost
Copy link

ghost commented Mar 30, 2020

I'm going to lock this issue because it has been closed for 30 days ⏳. This helps our maintainers find and focus on the active issues.

If you feel this issue should be reopened, we encourage creating a new issue linking back to this one for added context. If you feel I made an error 🤖 🙉 , please reach out to my human friends 👉 hashibot-feedback@hashicorp.com. Thanks!

@ghost ghost locked and limited conversation to collaborators Mar 30, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Feature Request: Virtual Network Rules for Postgresql
5 participants