Fix Secret check_mode (#343)

When adding a Secret and using stringData, check_mode will always show
changes. An existing resource fetched from Kubernetes will have the
stringData already base64 encoded and merged into the data attribute.
This change performs the base64 encoding and merging with the provided
definition to more accurately represent the current state of the
cluster.

This change only affects check_mode. When making any changes to the
cluster the stringData is passed along as provided in the definition.

Closes #282.
This commit is contained in:
Mike Graves
2021-01-14 11:05:11 -05:00
committed by GitHub
parent 003d802065
commit b26afc3518
4 changed files with 131 additions and 5 deletions

View File

@@ -0,0 +1,2 @@
bugfixes:
- k8s - fix check_mode always showing changes when using stringData on Secrets (https://github.com/ansible-collections/community.kubernetes/issues/282).

View File

@@ -761,6 +761,81 @@
that:
- deploy_after_serviceaccount_removal is failed
- name: Add a secret
k8s:
definition:
apiVersion: v1
kind: Secret
metadata:
name: apply-secret
namespace: "{{ apply_namespace }}"
type: Opaque
stringData:
foo: bar
register: k8s_secret
- name: Check secret was created
assert:
that:
- k8s_secret is changed
- k8s_secret.result.data.foo
- name: Add same secret
k8s:
definition:
apiVersion: v1
kind: Secret
metadata:
name: apply-secret
namespace: "{{ apply_namespace }}"
type: Opaque
stringData:
foo: bar
register: k8s_secret
- name: Check nothing changed
assert:
that:
- k8s_secret is not changed
- name: Add same secret with check mode on
k8s:
definition:
apiVersion: v1
kind: Secret
metadata:
name: apply-secret
namespace: "{{ apply_namespace }}"
type: Opaque
stringData:
foo: bar
check_mode: yes
register: k8s_secret
- name: Check nothing changed
assert:
that:
- k8s_secret is not changed
- name: Add same secret with check mode on using data
k8s:
definition:
apiVersion: v1
kind: Secret
metadata:
name: apply-secret
namespace: "{{ apply_namespace }}"
type: Opaque
data:
foo: YmFy
check_mode: yes
register: k8s_secret
- name: Check nothing changed
assert:
that:
- k8s_secret is not changed
always:
- name: Remove namespace
k8s:

View File

@@ -18,6 +18,7 @@
from __future__ import absolute_import, division, print_function
__metaclass__ = type
import base64
import time
import os
import traceback
@@ -28,7 +29,7 @@ from distutils.version import LooseVersion
from ansible.module_utils.basic import AnsibleModule, missing_required_lib
from ansible.module_utils.six import iteritems, string_types
from ansible.module_utils._text import to_native
from ansible.module_utils._text import to_native, to_bytes, to_text
from ansible.module_utils.common.dict_transformations import dict_merge
from ansible.module_utils.parsing.convert_bool import boolean
@@ -690,7 +691,7 @@ class K8sAnsibleMixin(object):
else:
if self.apply:
if self.check_mode:
ignored, patch = apply_object(resource, definition)
ignored, patch = apply_object(resource, _encode_stringdata(definition))
if existing:
k8s_obj = dict_merge(existing.to_dict(), patch)
else:
@@ -721,7 +722,7 @@ class K8sAnsibleMixin(object):
if not existing:
if self.check_mode:
k8s_obj = definition
k8s_obj = _encode_stringdata(definition)
else:
try:
k8s_obj = resource.create(definition, namespace=namespace).to_dict()
@@ -757,7 +758,7 @@ class K8sAnsibleMixin(object):
if existing and force:
if self.check_mode:
k8s_obj = definition
k8s_obj = _encode_stringdata(definition)
else:
try:
k8s_obj = resource.replace(definition, name=name, namespace=namespace, append_hash=self.append_hash).to_dict()
@@ -781,7 +782,7 @@ class K8sAnsibleMixin(object):
# Differences exist between the existing obj and requested params
if self.check_mode:
k8s_obj = dict_merge(existing.to_dict(), definition)
k8s_obj = dict_merge(existing.to_dict(), _encode_stringdata(definition))
else:
if LooseVersion(self.openshift_version) < LooseVersion("0.6.2"):
k8s_obj, error = self.patch_resource(resource, definition, existing, name,
@@ -858,3 +859,12 @@ class KubernetesAnsibleModule(AnsibleModule, K8sAnsibleMixin):
self.warn("class KubernetesAnsibleModule is deprecated"
" and will be removed in 2.0.0. Please use K8sAnsibleMixin instead.")
def _encode_stringdata(definition):
if definition['kind'] == 'Secret' and 'stringData' in definition:
for k, v in definition['stringData'].items():
encoded = base64.b64encode(to_bytes(v))
definition.setdefault('data', {})[k] = to_text(encoded)
del definition['stringData']
return definition

View File

@@ -0,0 +1,39 @@
# -*- coding: utf-8 -*-
# Copyright: (c) 2021, Ansible Project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
from ansible_collections.community.kubernetes.plugins.module_utils.common import (
_encode_stringdata,
)
def test_encode_stringdata_modifies_definition():
definition = {
"apiVersion": "v1",
"kind": "Secret",
"type": "Opaque",
"stringData": {
"mydata": "ansiβle"
}
}
res = _encode_stringdata(definition)
assert "stringData" not in res
assert res["data"]["mydata"] == "YW5zac6ybGU="
def test_encode_stringdata_does_not_modify_data():
definition = {
"apiVersion": "v1",
"kind": "Secret",
"type": "Opaque",
"data": {
"mydata": "Zm9vYmFy"
}
}
res = _encode_stringdata(definition)
assert res["data"]["mydata"] == "Zm9vYmFy"