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 module] Added Azure SQL Servers support - cloud/azure/azure_rm_sql_servers #33077
[new module] Added Azure SQL Servers support - cloud/azure/azure_rm_sql_servers #33077
Conversation
The test
The test
|
@brusMX @devigned @haroldwongms @julienstroheker @jwhitbeck @lmazuel @obsoleted @ozboms @sozercan @tstringer @xscript @yuwzho As a maintainer of a module in the same namespace this new module has been submitted to, your vote counts for shipits. Please review this module and add |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Have added some comments, some things we would need to do before merging is
- Fix up the idempotency, right now it reports a change everytime it runs
- Expand the tests to validate the module
- Make the return values more Ansible like
Overall the module works for me and creates a SQL Server which is great. One question I do have more on the Azure side, is it planned to add support for more SQL types, e.g. MySQL and so or is that not supported in Azure or going to be in separate modules?
--- | ||
module: azure_rm_sql_server | ||
version_added: "2.5" | ||
short_description: Manage an Server. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
short_description should not have a .
, are you able to expand on this like, Manage a Azure SQL Server
?
version_added: "2.5" | ||
short_description: Manage an Server. | ||
description: | ||
- Create, update and delete an instance of Server. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
SQL
location: | ||
description: | ||
- Resource location. | ||
required: True |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be nice if this wasn't required and could be derived from the resource group specified.
tags: | ||
description: | ||
- Resource tags. | ||
required: False |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
required: False
is the default and is usually omitted.
suboptions: | ||
type: | ||
description: | ||
- "The identity type. Set this to 'SystemAssigned' in order to automatically create and assign an Azure Active Directory principal for th |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would argue that SystemAssigned
should be the default value if this is not set or can a SQL Server not have an identity? Is there a reason why this is a nested option under the type
key? If not can identity just by the type value or do you see there being more options available to identity?
When quoting a value, you can enclose it with C()
, e.g. 'SystemAssigned'
should be C(SystemAssigned)
. You can also use I()
when referring to option option names.
def exec_module(self, **kwargs): | ||
"""Main module execution method""" | ||
|
||
for key in list(self.module_arg_spec.keys()) + ['tags']: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Have a look at azure_rm_virtualmachine to see how to do this, you usually set the parameter as None in the init and these are populated in AzureRMModuleBase
for you.
else: | ||
self.log("Server instance already exists") | ||
if self.state == 'absent': | ||
self.delete_servers() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This shouldn't be run in check mode.
self.results['changed'] = True | ||
self.log("Server instance deleted") | ||
elif self.state == 'present': | ||
self.log("Need to check if Server instance has to be deleted or may be updated") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You need some code to check if the server actually needs updating, right now the module is reporting a change when running it twice which is not idempotent.
self.results['state'] = self.create_update_servers() | ||
self.results['changed'] = True | ||
else: | ||
self.results['state'] = response |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should look at changing the return values, see my note in the documentation portion.
@@ -0,0 +1,13 @@ | |||
- name: Create Servers |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I know you were saying you were having issues trying to get this to run but we should also look at expanding these tests a lot more, usually we look at performing 3 tasks per action, e.g. 3 tasks for creating a server, 3 tasks for modifying a server and 3 tasks for removing a server. The 3 tasks per action should follow;
- Run the task in check mode and check that the server wasn't actually created
- Run the task normally and check that the server was created as expected
- Run the task again (idempotent) and check that a change wasn't reported
ready_for_review |
@jborean93 please check the module again, i think it's ready for merging. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I couldn't really test this as I came across the error
fatal: [127.0.0.1]: FAILED! => {"changed": false, "msg": "Error creating the SQL Server instance: Azure Error: InvalidRequestContent\nMessage: The request content was invalid and could not be deserialized: 'Required property 'type' not found in JSON. Path 'identity', line 1, position 44.'."}
A few things to note;
- The tests need to be activated by adding the alias I mentioned in the comment (that should have picked up the error I recieved)
- The tests need to be expanded to cover a check mode, normal and idempotent tests of the actions
create
,update
, anddelete
scenarios - I'm not sure if the tags are actually supported, I was unable to test it but couldn't find anything that really seem to check and set them
- The location is still required, it fails if you don't set it
description: | ||
- The name of the server. | ||
required: True | ||
tags: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm pretty sure you don't need the tags as you are extending the azure_tags
documentation below.
def exec_module(self, **kwargs): | ||
"""Main module execution method""" | ||
|
||
for key in list(self.module_arg_spec.keys()) + ['tags']: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's the point of this whole block, AzureRMModuleBase
should be setting self.x
based on the module_arg_spec
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
well, i have tried and it doesn't work as expected, so i have to leave it for now...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, will try and debug and find out what is happening, it should just work.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok I see my mistake, most modules just have
for key in list(self.module_arg_spec.keys()) + ['tags']:
setattr(self, key, kwargs[key])
Is there a reason why you have all these if statements as the above should be enough for you.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes, these attributes are sticked into objects padded to Azure SDK, so treated in different way
self.mgmt_client = self.get_mgmt_svc_client(SqlManagementClient, | ||
base_url=self._cloud_environment.endpoints.resource_manager) | ||
|
||
try: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No need for a try/except
here as azure_rm_common handles a missing source group
except CloudError: | ||
self.fail('resource group {0} not found'.format(self.resource_group)) | ||
|
||
if not ("location" in self.parameters): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This doesn't seem to be working, I got The error was: msrest.exceptions.ValidationError: Parameter 'Server.location' can not be None
in an error which means this musn't work. Try doing;
if self.location is None:
self.location = resource_group.location
self.parameters = dict() | ||
self.parameters['identity'] = dict() | ||
|
||
self.results = dict(changed=False, state=dict()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
state
doesn't need to be there.
self.results['changed'] = True | ||
else: | ||
self.results['changed'] = old_response.__ne__(response) | ||
self.results.update(response) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of updating the results object here with the response and then removing the unneeded ones below, just set the results you want manually.
self.log("Creation / Update done") | ||
elif self.to_do == Actions.Delete: | ||
self.log("SQL Server instance deleted") | ||
self.delete_sqlserver() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Need to not run when in check mode
self.results['changed'] = True | ||
else: | ||
self.log("SQL Server instance unchanged") | ||
self.results['state'] = old_response |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This shouldn't be returned, it should be the same values that you set in add or update.
@@ -0,0 +1,2 @@ | |||
cloud/azure | |||
destructive |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You will need to add posix/ci/cloud/group2/azure
to this list so the tests actually run in CI
@jborean93 I have made all necessary updates, seems like test can't run as Microsoft.Sql namespace has to be added. Error creating the SQL Server instance: Azure Error: MissingSubscriptionRegistration\nMessage: The subscription is not registered to use namespace 'Microsoft.Sql'. See https://aka.ms/rps-not-found for how to register subscriptions.\nException Details:\n\tError Code: MissingSubscriptionRegistration\n\tMessage: The subscription is not registered to use namespace 'Microsoft.Sql'. See https://aka.ms/rps-not-found for how to register subscriptions.\n\tTarget: Microsoft.Sql |
Looks like the tests are failing because we aren't authorised in CI, will see if @mattclay can help. Edit: just saw your post :) |
@zikalino once we get the tests running we need to look at expanding the tests, there is at least 1 issue I can find that the tests should have picked up if we had some more tests. Let me know if you are confused as to what I am asking for, I'm happy to discuss it further over IRC as it would be good to get this merged today so you can update your other PR's with the changes you made here. |
@jborean do you mean taking location from resource group? this could be difficult. some resources can't be created in particular location (I believe i couldn't create SQL server in the same location as the one where default rg is created). |
@zikalino, for the location part I see the benefit in being able to set the location independently to override the resource group but most modules seem to have it as an optional value and if not set explicitly it is derived from the resource group location. What I am talking about is expanding the tests from just the create, update, delete tasks to test each scenario under check mode, normal and an idempotent task. In the end it would look similar to
This would be repeated again for updating a SQL server and then finally again for removing a SQL server. |
@zikalino, the Microsoft.SQL resource has been registered and the tests are now running. |
@jborean93 seems now tests are passing. should i still add more tests to this pr? or is it ok to merge, and i could expand tests when adding facts module? |
I think we should still continue to add the extra tests just without that second step of checking the results with a fact module for now (should still come back and expand once a facts module is in). We can still do some assertions on the results of just the task itself. Do you know how to run tests locally? |
@jborean93 I have more tests for check_mode and also delete unexisting sql server test (which i moved to front, as delete after delete seems a bit unstable -- second delete may get invalid information that resource still exists) |
Looks better, we still need a test for each action again to ensure that no changes are recorded (idempotent). |
@jborean93 how about now? added 2 more tests |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking good, left some comments on some tests, only thing remaining is to delete the instance again right at the end for idempotency checks
- output.changed | ||
- output.state == 'Ready' | ||
|
||
- name: Create again instance of SQL Server -- check mode |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Delete this one, you only need to run the check mode test for a scenario once
random_postfix: "{{ 1000 | random }}" | ||
run_once: yes | ||
|
||
- name: Delete unexisting instance of SQL Server -- check mode |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Take away this test
that: | ||
- output.changed == false | ||
|
||
- name: Delete unexisting instance of SQL Server |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't need this test as you should be starting with a random resource group so no SQL server would exist anyway
shipit |
Awesome @zikalino, are you able to raise a PR to add this module to the changelog for 2.5 https://github.com/ansible/ansible/blob/devel/CHANGELOG.md. We try to keep things in alphabetical order in each section. |
SUMMARY
Adding support for Azure SQL Servers
ISSUE TYPE
COMPONENT NAME
azure_rm_sql_servers
ANSIBLE VERSION
ADDITIONAL INFORMATION