Repository: openshift/openshift-ansible

Issue:
Module failure when redeploy-certificates
#### Description
When i try to redeploy certificates on my openshift cluster i get this error:

```
TASK [openshift_certificate_expiry : Check cert expirys on host] ************************************************************************************************************************
fatal: [smartiot-kube1.c.ptin.corppt.com]: FAILED! => {"changed": false, "module_stderr": "Shared connection to smartiot-kube1.c.ptin.corppt.com closed.\r\n", "module_stdout": "Traceback (most recent call last):\r\n  File \"/tmp/ansible_PgKVjj/ansible_module_openshift_cert_expiry.py\", line 833, in <module>\r\n    main()\r\n  File \"/tmp/ansible_PgKVjj/ansible_module_openshift_cert_expiry.py\", line 548, in main\r\n    cert_serial) = load_and_handle_cert(cert, now, ans_module=module)\r\n  File \"/tmp/ansible_PgKVjj/ansible_module_openshift_cert_expiry.py\", line 282, in load_and_handle_cert\r\n    cert_loaded = FakeOpenSSLCertificate(openssl_decoded)\r\n  File \"/tmp/ansible_PgKVjj/ansible_module_openshift_cert_expiry.py\", line 101, in __init__\r\n    self._parse_cert()\r\n  File \"/tmp/ansible_PgKVjj/ansible_module_openshift_cert_expiry.py\", line 148, in _parse_cert\r\n    not_after_parsed = datetime.datetime.strptime(not_after_raw, '%b %d %H:%M:%S %Y %Z')\r\n  File \"/usr/lib64/python2.7/_strptime.py\", line 325, in _strptime\r\n    (data_string, format))\r\nValueError: time data 'May  3 14:19:31 2020 GMT' does not match format '%b %d %H:%M:%S %Y %Z'\r\n", "msg": "MODULE FAILURE", "rc": 1}
fatal: [smartiot-kube3.c.ptin.corppt.com]: FAILED! => {"changed": false, "module_stderr": "Shared connection to smartiot-kube3.c.ptin.corppt.com closed.\r\n", "module_stdout": "Traceback (most recent call last):\r\n  File \"/tmp/ansible_A6RoYy/ansible_module_openshift_cert_expiry.py\", line 833, in <module>\r\n    main()\r\n  File \"/tmp/ansible_A6RoYy/ansible_module_openshift_cert_expiry.py\", line 548, in main\r\n    cert_serial) = load_and_handle_cert(cert, now, ans_module=module)\r\n  File \"/tmp/ansible_A6RoYy/ansible_module_openshift_cert_expiry.py\", line 282, in load_and_handle_cert\r\n    cert_loaded = FakeOpenSSLCertificate(openssl_decoded)\r\n  File \"/tmp/ansible_A6RoYy/ansible_module_openshift_cert_expiry.py\", line 101, in __init__\r\n    self._parse_cert()\r\n  File \"/tmp/ansible_A6RoYy/ansible_module_openshift_cert_expiry.py\", line 148, in _parse_cert\r\n    not_after_parsed = datetime.datetime.strptime(not_after_raw, '%b %d %H:%M:%S %Y %Z')\r\n  File \"/usr/lib64/python2.7/_strptime.py\", line 325, in _strptime\r\n    (data_string, format))\r\nValueError: time data 'May  3 14:19:46 2020 GMT' does not match format '%b %d %H:%M:%S %Y %Z'\r\n", "msg": "MODULE FAILURE", "rc": 1}
fatal: [smartiot-kube2.c.ptin.corppt.com]: FAILED! => {"changed": false, "module_stderr": "Shared connection to smartiot-kube2.c.ptin.corppt.com closed.\r\n", "module_stdout": "Traceback (most recent call last):\r\n  File \"/tmp/ansible_ukRSLG/ansible_module_openshift_cert_expiry.py\", line 833, in <module>\r\n    main()\r\n  File \"/tmp/ansible_ukRSLG/ansible_module_openshift_cert_expiry.py\", line 548, in main\r\n    cert_serial) = load_and_handle_cert(cert, now, ans_module=module)\r\n  File \"/tmp/ansible_ukRSLG/ansible_module_openshift_cert_expiry.py\", line 282, in load_and_handle_cert\r\n    cert_loaded = FakeOpenSSLCertificate(openssl_decoded)\r\n  File \"/tmp/ansible_ukRSLG/ansible_module_openshift_cert_expiry.py\", line 101, in __init__\r\n    self._parse_cert()\r\n  File \"/tmp/ansible_ukRSLG/ansible_module_openshift_cert_expiry.py\", line 148, in _parse_cert\r\n    not_after_parsed = datetime.datetime.strptime(not_after_raw, '%b %d %H:%M:%S %Y %Z')\r\n  File \"/usr/lib64/python2.7/_strptime.py\", line 325, in _strptime\r\n    (data_string, format))\r\nValueError: time data 'May  3 14:19:44 2020 GMT' does not match format '%b %d %H:%M:%S %Y %Z'\r\n", "msg": "MODULE FAILURE", "rc": 1}
	to retry, use: --limit @/home/ptin_admin/openshift-ansible/playbooks/redeploy-certificates.retry
```

I think it is a problem on the date, because:

`File \"/usr/lib64/python2.7/_strptime.py\", line 325, in _strptime\r\n    (data_string, format))\r\n**ValueError: time data 'May  3 14:19:31 2020 GMT' does not match format '%b %d %H:%M:%S %Y %Z'\r\n", "msg": "MODULE FAILURE"**, "rc": 1}`

##### Version

Ansible: 2.5.2
Openshift Origin: openshift-ansible-3.9.27-1-41-ga5c91e7

##### Steps To Reproduce
$ ansible-playbook -i ../inventory/hosts.39 ../playbooks/redeploy-certificates.yml






Discussion:
We got the same error.

Ansible 2.5.2
Commit: 5c1207cd2c63736e93168e77c30a6b0fbd05afc9

Host: Ubuntu 18.04 
```
ansible 2.5.2
 config file = None
 configured module search path = ['~/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
 ansible python module location = ~/.local/lib/python3.6/site-packages/ansible
 executable location = ~/.local/bin/ansible
 python version = 3.6.3 (default, Oct  3 2017, 21:45:48) [GCC 7.2.0]
```
Does it work with ansible 2.4?
It works with Ansible 2.5 on Python 2:

```
ansible 2.5.0
  config file = /Users/jkr/Documents/ansible/ansible/ansible.cfg
  configured module search path = [u'/Users/jkr/Documents/ansible/ansible/library', u'/Users/jkr/Documents/ansible/ansible/openshift-ansible/roles/lib_openshift/library', u'/Users/jkr/Documents/ansible/ansible/openshift-ansible/roles/lib_os_firewall/library']
  ansible python module location = /usr/local/Cellar/ansible/2.5.0/libexec/lib/python2.7/site-packages/ansible
  executable location = /usr/local/bin/ansible
  python version = 2.7.14 (default, Mar 22 2018, 14:43:05) [GCC 4.2.1 Compatible Apple LLVM 9.0.0 (clang-900.0.39.2)]
```

We upgrade our cluster yesterday, so I can't it retest at this time now because of restricted maintenance window.
We just encountered the same Problem with:
 - ansible 2.5.2
 - Python 2.7.5

```
ansible 2.5.2
  config file = /root/openshift-ansible/ansible.cfg
  configured module search path = [u'/root/.ansible/plugins/modules', u'/usr/share/ansible/plugins/modules']
  ansible python module location = /usr/lib/python2.7/site-packages/ansible
  executable location = /usr/bin/ansible
  python version = 2.7.5 (default, Aug  4 2017, 00:39:18) [GCC 4.8.5 20150623 (Red Hat 4.8.5-16)]
```

Also happens with ansible 2.4 btw. Is this possibly a locale problem? The nodes are configured with `de_DE.UTF-8` locale.

Full stacktrace:

```
<node03> ESTABLISH SSH CONNECTION FOR USER: root
<node03> SSH: EXEC ssh -o ControlMaster=auto -o ControlPersist=600s -o StrictHostKeyChecking=no -o Port=12488 -o KbdInteractiveAuthentication=no -o PreferredAuthentications=gssapi-with-mic,gssapi-keyex,hostbased,publickey -o PasswordAuthentication=no -o User=root -o ConnectTimeout=10 -o ControlPath=/root/.ansible/cp/%h-%r node03 '/bin/sh -c '"'"'/usr/bin/python && sleep 0'"'"''
<node03> (1, '', 'Traceback (most recent call last):\n  File "/tmp/ansible_RcITyT/ansible_module_openshift_cert_expiry.py", line 865, in <module>\n    main()\n  File "/tmp/ansible_RcITyT/ansible_module_openshift_cert_expiry.py", line 545, in main\n    cert_serial) = load_and_handle_cert(cert, now, ans_module=module)\n  File "/tmp/ansible_RcITyT/ansible_module_openshift_cert_expiry.py", line 282, in load_and_handle_cert\n    cert_loaded = FakeOpenSSLCertificate(openssl_decoded)\n  File "/tmp/ansible_RcITyT/ansible_module_openshift_cert_expiry.py", line 101, in __init__\n    self._parse_cert()\n  File "/tmp/ansible_RcITyT/ansible_module_openshift_cert_expiry.py", line 148, in _parse_cert\n    not_after_parsed = datetime.datetime.strptime(not_after_raw, \'%b %d %H:%M:%S %Y %Z\')\n  File "/usr/lib64/python2.7/_strptime.py", line 325, in _strptime\n    (data_string, format))\nValueError: time data \'May 14 09:47:50 2021 GMT\' does not match format \'%b %d %H:%M:%S %Y %Z\'\n')
The full traceback is:
Traceback (most recent call last):
  File "/tmp/ansible_RcITyT/ansible_module_openshift_cert_expiry.py", line 865, in <module>
    main()
  File "/tmp/ansible_RcITyT/ansible_module_openshift_cert_expiry.py", line 545, in main
    cert_serial) = load_and_handle_cert(cert, now, ans_module=module)
  File "/tmp/ansible_RcITyT/ansible_module_openshift_cert_expiry.py", line 282, in load_and_handle_cert
    cert_loaded = FakeOpenSSLCertificate(openssl_decoded)
  File "/tmp/ansible_RcITyT/ansible_module_openshift_cert_expiry.py", line 101, in __init__
    self._parse_cert()
  File "/tmp/ansible_RcITyT/ansible_module_openshift_cert_expiry.py", line 148, in _parse_cert
    not_after_parsed = datetime.datetime.strptime(not_after_raw, '%b %d %H:%M:%S %Y %Z')
  File "/usr/lib64/python2.7/_strptime.py", line 325, in _strptime
    (data_string, format))
ValueError: time data 'May 14 09:47:50 2021 GMT' does not match format '%b %d %H:%M:%S %Y %Z'

```
Yeah, we probably need to set a static locale when looking at the time if we're attempting to parse the time.
I managed to work around this issue by setting the locale to 'en_US.utf8' on the system where I run the playbooks from.
@jkroepke : you can run the certificate-expiry playbook to test this, as it only collects certificate information from the openshift nodes and doesn't change anything.
I'm wounder why it has worked on a Mac with de_DE.UTF-8. 

But maybe brew does magic.

I'll try to test it on a colleague workstation, since I can't get this error on my mac.
Yup, I can reproduce that behavior on a Centos Docker Container. (LANG=en_US.UTF-8 works, de_DE not)
Any progress on that?

Resolving patch:
diff --git a/roles/lib_utils/library/openshift_cert_expiry.py b/roles/lib_utils/library/openshift_cert_expiry.py
--- a/roles/lib_utils/library/openshift_cert_expiry.py
+++ b/roles/lib_utils/library/openshift_cert_expiry.py
@@ -10,6 +10,7 @@
 import os
 import subprocess
 import yaml
+import dateutil.parser
 
 # pylint import-error disabled because pylint cannot find the package
 # when installed in a virtualenv
@@ -145,7 +146,7 @@ def _parse_cert(self):
                 # => 20190207181935Z
                 not_after_raw = l.partition(' : ')[-1]
                 # Last item: ('Not After', ' : ', 'Feb  7 18:19:35 2019 GMT')
-                not_after_parsed = datetime.datetime.strptime(not_after_raw, '%b %d %H:%M:%S %Y %Z')
+                not_after_parsed = dateutil.parser.parse(not_after_raw)
                 self.not_after = not_after_parsed.strftime('%Y%m%d%H%M%SZ')
 
             elif l.startswith('X509v3 Subject Alternative Name:'):


Test patch: