Note: This bug is displayed in read-only format because the product is no longer active in Red Hat Bugzilla.

Bug 1515667

Summary: etcd schema migration playbook failing on etcdctl commands
Product: OpenShift Container Platform Reporter: Bryan Yount <byount>
Component: InstallerAssignee: Scott Dodson <sdodson>
Status: CLOSED WONTFIX QA Contact: Johnny Liu <jialiu>
Severity: high Docs Contact:
Priority: unspecified    
Version: 3.6.1CC: a.badger, aos-bugs, byount, erich, jburck, jokerman, mmccomas, sdodson
Target Milestone: ---Keywords: Unconfirmed
Target Release: 3.7.z   
Hardware: Unspecified   
OS: Unspecified   
Whiteboard:
Fixed In Version: Doc Type: If docs needed, set a value
Doc Text:
Story Points: ---
Clone Of: Environment:
Last Closed: 2018-08-01 13:48:00 UTC Type: Bug
Regression: --- Mount Type: ---
Documentation: --- CRM:
Verified Versions: Category: ---
oVirt Team: --- RHEL 7.3 requirements from Atomic Host:
Cloudforms Team: --- Target Upstream Version:
Embargoed:

Description Bryan Yount 2017-11-21 08:24:36 UTC
Description of problem:
Customer recently upgraded to OpenShift 3.6 and wanted to migrate etcd from V2 to V3 schema. When they run the migrate.yml playbook, it consistently fails on the first etcd_cluster_health task. After some digging, it appears that almost all (maybe all?) etcdctl commands are failing when they are run in an Ansible playbook.

Version-Release number of the following components:
* openshift-ansible-3.6.173.0.48-1.git.0.1609d30
* ansible-2.3.2
* etcd 3.2.5-1 in a 3 node configuration
* Possible proxy on the network between the nodes (Unconfirmed)

How reproducible:
Very

Steps to Reproduce:
1. Customer ran the migrate.yml playbook to migrate etcd from V2 to V3 schema
2. Playbook was failing on the first "etcd cluster health" task:

TASK [etcd_migrate : Check the etcd cluster health]
Etcd member x.x.x.14 is not healthy
Etcd member x.x.x.16 is not healthy
Etcd member x.x.x.15 is not healthy

3. Manually running the same etcdctl command found in the "etcd cluster health" task succeeded:

$ sudo etcdctl --cert-file <path> --key-file <path> --ca-file <path> --endpoint https://x.x.x.16:2379 cluster-health
member 3bd48cd30ff411a4 is healthy: got healthy result from https://x.x.x.16:2379
member b9f21dd4d323a704 is healthy: got healthy result from https://x.x.x.15:2379
member bbeaef2df8fbf299 is healthy: got healthy result from https://x.x.x.14:2379
cluster is healthy

4. Running playbook again with -vvv revealed this error:

mux_client_read_packet: read header failed: Broken pipe\r\ndebug2: Received exit status from master 0\r\nShared connection to master01.example.com closed.\r\n')

5. After some digging, we ran etcdctl a slightly different way using Ansible ad-hoc and saw an error that led us to suspect a proxy:

$ sudo ansible -a "etcdctl --ca-file=/etc/origin/master/master.etcd-ca.crt --cert-file=/etc/origin/master/master.etcd-client.crt --key-file=/etc/origin/master/master.etcd-client.key  --peers '${PEERS}' cluster-health" masters
master01.example.com | FAILED | rc=5 >>
failed to check the health of member 3bd48cd30ff411a4 on https://x.x.x.16:2379: Get https://x.x.x.16:2379/health: proxyconnect tcp: dial tcp y.y.y.8:80: i/o timeout

6. Because of the proxyconnect error, it was suggested to use the no_proxy variable. We tried adding it to the hosts file:

[OSEv3:vars]
ansible_ssh_user=root
openshift_deployment_type=openshift-enterprise
no_proxy=<master1>,<master2>,<master3>

7. The behavior didn't change and the same error was seen:

mux_client_read_packet: read header failed: Broken pipe\r\ndebug2: Received exit status from master 0\r\nShared connection to master01.example.com closed.\r\n')

8. Tried adding "no_proxy" into the playbook instead. Since migrate.yml is nested and complicated, a simple, test playbook was created that would only run the "etcdctl cluster-health" command.

environment:
        no_proxy: "<master1>,<master2>,<master3>"

9. Success!! Putting no_proxy in the playbook resolved the issue.

member 3bd48cd30ff411a4 is healthy: got healthy result from https://x.x.x.16:2379
member bbeaef2df8fbf299 is healthy: got healthy result from https://x.x.x.14:2379
member c594d73b86b696d2 is healthy: got healthy result from https://x.x.x.15:2379
cluster is healthy

10. Why do etcdctl commands run inside of a playbook fail but the same command run manually succeeds?

Comment 4 Scott Dodson 2017-11-21 13:57:44 UTC
Bryan,

I don't see any /etc/profile.d or elsewhere that a proxy environment variable is set for specific users but can you confirm whether or not HTTP_PROXY, HTTPS_PROXY, NO_PROXY envrionment variables are set for either the user who executed the playbooks or the ansible_ssh_user on the etcd hosts?

`env | grep -i _PROXY` as both the user executing the playbooks and ansible_ssh_user should show me what I'm looking for.

Comment 9 Bryan Yount 2017-11-22 01:49:53 UTC
(In reply to Scott Dodson from comment #4)
> Bryan,
> 
> I don't see any /etc/profile.d or elsewhere that a proxy environment
> variable is set for specific users but can you confirm whether or not
> HTTP_PROXY, HTTPS_PROXY, NO_PROXY envrionment variables are set for either
> the user who executed the playbooks or the ansible_ssh_user on the etcd
> hosts?
> 
> `env | grep -i _PROXY` as both the user executing the playbooks and
> ansible_ssh_user should show me what I'm looking for.

* /etc/environment exists on every server but is 0 bytes (nothing in it)
* HTTP_PROXY and HTTPS_PROXY environment variables are set in the shell
* `env | grep -i _PROXY` as the user executing the playbooks

$ env | grep http
http_proxy=http://proxy.example.com:80
HTTP_PROXY=http://proxy.example.com:80
ftp_proxy=http://proxy.example.com:80
FTP_PROXY=http://proxy.example.com:80

* We yet don't have the output of `env | grep -i _PROXY` for the ansible_ssh_user. Will request that from the customer.

* Tried working around the issue by adding no_proxy to all of the Ansible playbooks where etcdctl is called.
 - Attached (private etcd-scheme-upgrade.log file) logs with no_proxy enabled and now fails when recreating cluster. 1st etcd restarted removes other two members leaving a cluster of 1 and fails to communicate with other two unavailable or misconfigured members.
 - Attached (private second_etcd_run.txt file) logs from a second run on a second OCP cluster that had not been upgraded and got the same error with the modified no_proxy playbooks.

Comment 10 Scott Dodson 2017-11-22 14:03:02 UTC
How are those environment variables being set for the user? Rather than modifying the playbooks I think we should focus on figuring out how they're set and if that would potentially affect the ansible execution environment because the ansible documentation for the command module explicitly states that the command module should not be affected by the user's environment. 

One other thought, is etcdctl on the host perhaps a wrapper script? `which etcdctl` as the ansible_ssh_user would tell us.

Comment 11 Bryan Yount 2017-11-23 00:11:18 UTC
(In reply to Scott Dodson from comment #10)
> How are those environment variables being set for the user? Rather than
> modifying the playbooks I think we should focus on figuring out how they're
> set and if that would potentially affect the ansible execution environment
> because the ansible documentation for the command module explicitly states
> that the command module should not be affected by the user's environment. 
> 
> One other thought, is etcdctl on the host perhaps a wrapper script? `which
> etcdctl` as the ansible_ssh_user would tell us.

I agree, the customer shouldn't have to modify the playbooks to get this to work. We only tried that as a workaround. The question is: why doesn't etcdctl like being run through an ssh tunnel in an Ansible playbook in an environment that has a proxy?

Here's the requested output:

[root@master01 ~]# env | grep -i _PROXY
http_proxy=http://proxy.example.com:80
FTP_PROXY=http://proxy.example.com:80
ftp_proxy=http://proxy.example.com:80
no_proxy=.example.com
HTTP_PROXY=http://proxy.example.com:80

[root@master01 ~]# which etcdctl
/usr/bin/etcdctl

Their ansible user is actually running as root and they set up ssh keys for root between masters -> all nodes. In the hosts file, ansible_ssh_user is set to root:

[OSEv3:vars]
ansible_ssh_user=root
openshift_deployment_type=openshift-enterprise
no_proxy="."

They added no_proxy="." recently in this dev cluster while troubleshooting the etcd schema migration issue. The no_proxy setting doesn't exist in other clusters.

Comment 14 Scott Dodson 2017-11-29 22:04:31 UTC
I've confirmed that when the local user has HTTP_PROXY HTTPS_PROXY variables set and ansible_connection=local is used for any of the hosts affected by the playbook the playbook will fail.

Removing ansible_connection=local results in a successful migration.

Comment 15 Scott Dodson 2017-11-30 18:35:00 UTC
Alternately, determine where those environment variables for the ansible_ssh_user are being set and comment them out for the time that the migration is being executed. They shouldn't be necessary in any form or fashion as all hosts should be co-located and shouldn't require a proxy to communicate with each other.

Comment 16 Bryan Yount 2017-12-03 02:55:53 UTC
(In reply to Scott Dodson from comment #15)
> Alternately, determine where those environment variables for the
> ansible_ssh_user are being set and comment them out for the time that the
> migration is being executed. They shouldn't be necessary in any form or
> fashion as all hosts should be co-located and shouldn't require a proxy to
> communicate with each other.

Thank you for confirming this, Scott. The customer does have the ansible user set as 'root' and root does have those environment variables in place. I have asked the customer to confirm the ansible_connection=local setting.

I will also have them test the schema migration again after either removing ansible_connection=local or removing the environment variables from root.

Comment 17 Bryan Yount 2017-12-05 23:32:09 UTC
(In reply to Scott Dodson from comment #14)
> I've confirmed that when the local user has HTTP_PROXY HTTPS_PROXY variables
> set and ansible_connection=local is used for any of the hosts affected by
> the playbook the playbook will fail.
> 
> Removing ansible_connection=local results in a successful migration.

They are not using the ansible_connection ansible variable in their inventory file apparently. I wonder if ansible_connection is local by default?

[user@node1 ~]$ grep ansible_connection /etc/ansible/hosts
[user@node1 ~]$

We're going to try removing those variables now instead...

Comment 18 Toshio Ernie Kuratomi 2017-12-06 16:25:26 UTC
The command module documentation is misleading/wrong here.  The command module does use the user's environment variables just like any other ansible module.  I've done some sleuthing in the git repository history to confirm and it looks like the documentation was added way back when in-module documentation was first added as an ansible feature and it has never matched with the module's behaviour.

Just like any other Ansible module, the command module inherits the environment variables that are set when we connect to the remote machine.  In the case of a local connection and no become method, the module inherits the same environment as the user invoking ansible.  In the case of a remote connection like ssh, the user's shell on the remote machine plays a part because the shell will source certain startup files when we open our ssh session.  In the case of become, the configuration of the become method and the startup files of the user we're becoming may also play a part.

Which shell startup files play a part depends on the shell being used (and likely, how the distribution has configured the shell).  I've written up the results of some tests on Ubuntu and CentOS in a series of three comments on this upstream bug:
https://github.com/ansible/ansible/issues/22885#issuecomment-293741361  in case you need some of those details.

I'll submit an upstream PR to correct the command module's documentation.

Comment 21 Chris Kim 2018-01-04 19:35:22 UTC
I can confirm that removing the proxy environment variables allowed for a successful v2 to v3 schema migration.

I'm unsure of whether the original issue was stemming from the proxy or whether it is an etcdctl problem.

Comment 25 Scott Dodson 2018-08-01 13:48:00 UTC
The only attached cases are CLOSED and this stems from host customization outside of that which openshift-ansible is responsible for. I'd expect an admin with knowledge of their proxy configuration to have figured this out.

CLOSED WONTFIX