-
Notifications
You must be signed in to change notification settings - Fork 133
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 dry run #245
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
--- | ||
minor_changes: | ||
- add support for dry run with kubernetes client version >=18.20 (https://github.com/ansible-collections/kubernetes.core/pull/245). |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -229,6 +229,7 @@ def __init__(self, module, pyyaml_required=True, *args, **kwargs): | |
module.fail_json(msg=missing_required_lib('kubernetes'), exception=K8S_IMP_ERR, | ||
error=to_native(k8s_import_exception)) | ||
self.kubernetes_version = kubernetes.__version__ | ||
self.supports_dry_run = LooseVersion(self.kubernetes_version) >= LooseVersion("18.20.0") | ||
|
||
if pyyaml_required and not HAS_YAML: | ||
module.fail_json(msg=missing_required_lib("PyYAML"), exception=YAML_IMP_ERR) | ||
|
@@ -668,14 +669,18 @@ def _empty_resource_list(): | |
else: | ||
# Delete the object | ||
result['changed'] = True | ||
if not self.check_mode: | ||
if self.check_mode and not self.supports_dry_run: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Instead of doing these checks on two values, can we instead wrap There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I may not be fully understanding what you are suggesting, but I don't think this would be possible. The problem is check_mode can be true even if the library doesn't support dry run. In that case, we use the older, slightly buggy client side dry run. So there are really three states a module could be in: 1. no check mode 2. check mode with dry run 3. check mode without dry run. Different actions will be taken for all three states. I agree this is ugly. It's a perfect example of one of the problems I've highlighted in this collection as why it needs refactoring. This is a change that ideally happens in one place. Until we can drop support for the older kubernetes client I think it's always going to be a bit messy, but once we can solely rely on dry run the best way to handle this is to probably be able to put the client into something like read only mode. This would happen once during module setup and we wouldn't need to have any check mode logic in the rest of the code. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks for taking the time to explain the problem. |
||
return result | ||
else: | ||
if delete_options: | ||
body = { | ||
'apiVersion': 'v1', | ||
'kind': 'DeleteOptions', | ||
} | ||
body.update(delete_options) | ||
params['body'] = body | ||
if self.check_mode: | ||
params['dry_run'] = "All" | ||
try: | ||
k8s_obj = resource.delete(**params) | ||
result['result'] = k8s_obj.to_dict() | ||
|
@@ -687,7 +692,7 @@ def _empty_resource_list(): | |
return result | ||
else: | ||
self.fail_json(msg=build_error_msg(definition['kind'], origin_name, msg), error=exc.status, status=exc.status, reason=exc.reason) | ||
if wait: | ||
if wait and not self.check_mode: | ||
success, resource, duration = self.wait(resource, definition, wait_sleep, wait_timeout, 'absent', label_selectors=label_selectors) | ||
result['duration'] = duration | ||
if not success: | ||
|
@@ -708,15 +713,18 @@ def _empty_resource_list(): | |
kind=definition['kind'], name=origin_name, namespace=namespace) | ||
return result | ||
if apply: | ||
if self.check_mode: | ||
if self.check_mode and not self.supports_dry_run: | ||
ignored, patch = apply_object(resource, _encode_stringdata(definition)) | ||
if existing: | ||
k8s_obj = dict_merge(existing.to_dict(), patch) | ||
else: | ||
k8s_obj = patch | ||
else: | ||
try: | ||
k8s_obj = resource.apply(definition, namespace=namespace).to_dict() | ||
params = {} | ||
if self.check_mode: | ||
params['dry_run'] = 'All' | ||
k8s_obj = resource.apply(definition, namespace=namespace, **params).to_dict() | ||
except DynamicApiError as exc: | ||
msg = "Failed to apply object: {0}".format(exc.body) | ||
if self.warnings: | ||
|
@@ -757,11 +765,14 @@ def _empty_resource_list(): | |
parameter has been set to '{state}'".format( | ||
kind=definition['kind'], name=origin_name, state=state) | ||
return result | ||
elif self.check_mode: | ||
elif self.check_mode and not self.supports_dry_run: | ||
k8s_obj = _encode_stringdata(definition) | ||
else: | ||
params = {} | ||
if self.check_mode: | ||
params['dry_run'] = "All" | ||
try: | ||
k8s_obj = resource.create(definition, namespace=namespace).to_dict() | ||
k8s_obj = resource.create(definition, namespace=namespace, **params).to_dict() | ||
except ConflictError: | ||
# Some resources, like ProjectRequests, can't be created multiple times, | ||
# because the resources that they create don't match their kind | ||
|
@@ -807,11 +818,14 @@ def _empty_resource_list(): | |
diffs = [] | ||
|
||
if state == 'present' and existing and force: | ||
if self.check_mode: | ||
if self.check_mode and not self.supports_dry_run: | ||
k8s_obj = _encode_stringdata(definition) | ||
else: | ||
params = {} | ||
if self.check_mode: | ||
params['dry_run'] = "All" | ||
try: | ||
k8s_obj = resource.replace(definition, name=name, namespace=namespace, append_hash=append_hash).to_dict() | ||
k8s_obj = resource.replace(definition, name=name, namespace=namespace, append_hash=append_hash, **params).to_dict() | ||
except DynamicApiError as exc: | ||
msg = "Failed to replace object: {0}".format(exc.body) | ||
if self.warnings: | ||
|
@@ -842,7 +856,7 @@ def _empty_resource_list(): | |
return result | ||
|
||
# Differences exist between the existing obj and requested params | ||
if self.check_mode: | ||
if self.check_mode and not self.supports_dry_run: | ||
k8s_obj = dict_merge(existing.to_dict(), _encode_stringdata(definition)) | ||
else: | ||
for merge_type in self.params['merge_type'] or ['strategic-merge', 'merge']: | ||
|
@@ -884,6 +898,8 @@ def patch_resource(self, resource, definition, existing, name, namespace, merge_ | |
version="3.0.0", collection_name="kubernetes.core") | ||
try: | ||
params = dict(name=name, namespace=namespace) | ||
if self.check_mode: | ||
params['dry_run'] = 'All' | ||
if merge_type: | ||
params['content_type'] = 'application/{0}-patch+json'.format(merge_type) | ||
k8s_obj = resource.patch(definition, **params).to_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.
It would be nice to use a _fact module for that.