8

I am trying to execute a command on docker on other machine from my machine. When I execute this command:

- name: Add header
      command: docker exec cli bash -l -c "echo '{"payload":{"header":{"channel_header":{"channel_id":"gll", "type":2}},"data":{"config_update":'$(cat jaguar_update.json)'}}}' | jq . > jaguar_update_in_envelope.json"

through ansible playbook, I am getting the error shown below.

fatal:[  
   command-task
]:FAILED! =>{  
   "changed":true,
   "cmd":[  ],
   "delta":"0:00:00.131115",
   "end":"2019-07-11 17:32:44.651504",
   "msg":"non-zero return code",
   "rc":4,
   "start":"2019-07-11 17:32:44.520389",
   "stderr":"mesg: ttyname   
failed: Inappropriate ioctl for device\nparse error: Invalid numeric   
literal at line 1, column 9",
   "stderr_lines":[  
      "mesg: ttyname failed: 
Inappropriate ioctl for device",
      "parse error: Invalid numeric literal 
at line 1, column 9"
   ],
   "stdout":"",
   "stdout_lines":[  

   ]
}

But if I manually execute command in the docker container, it works fine and I don't get any issue.

EDIT: As suggested i tried with shell module

shell: docker exec cli -it bash -l -c "echo '{"payload":{"header":{"channel_header":{"channel_id":"gll", "type":2}},"data":{"config_update":'$(cat jaguar_update.json)'}}}' | jq . > jaguar_update_in_envelope.json"

But i get below error as

fatal: [command-task]: FAILED! => {"changed": true, "cmd": "docker exec cli -it bash -l -c echo '{\"payload\":{\"header\":{\"channel_header\":{\"channel_id\":\"gll\", \"type\":2}},\"data\":{\"config_update\":'$(cat jaguar_update.json)'}}}' | jq . > jaguar_update_in_envelope.json", "delta": "0:00:00.110341", "end": "2019-07-12 10:21:45.204049", "msg": "non-zero return code", "rc": 4, "start": "2019-07-12 10:21:45.093708", "stderr": "cat: jaguar_update.json: No such file or directory\nparse error: Invalid numeric literal at line 1, column 4", "stderr_lines": ["cat: jaguar_update.json: No such file or directory", "parse error: Invalid numeric literal at line 1, column 4"], "stdout": "", "stdout_lines": []}

All the files 'jaguar_update.json' present in the working directory. I have confirmed the working directory.

Above commands works if i put it in a shell script file then execute the shell script from ansible.

TechChain
  • 8,404
  • 29
  • 103
  • 228

3 Answers3

3

As everyone has mentioned this does need you to use shell instead of command. Now you want to simplify this command so it can run first in bash. Which can be done easily using printf

$ printf "%s%s%s" '{"payload":{"header":{"channel_header":{"channel_id":"gll", "type":2}},"data":{"config_update":' $(<jaguar_update.json'}}}' | jq . > jaguar_update_in_envelope.json

$ cat jaguar_update_in_envelope.json
{
  "payload": {
    "header": {
      "channel_header": {
        "channel_id": "gll",
        "type": 2
      }
    },
    "data": {
      "config_update": {
        "name": "tarun"
      }
    }
  }
}

So now our commands runs without issues. Next is to move it with bash -l -c format. So instead using -c which requires us to pass the whole command as one parameter, we use the multiline commands

$ bash -l <<EOF
printf "%s%s%s" '{"payload":{"header":{"channel_header":{"channel_id":"gll", "type":2}},"data":{"config_update":' $(<jaguar_update.json) '}}}' | jq . > jaguar_update_in_envelope.json
EOF

But this fails with an error

{"payload":{"header":{"channel_header":{"channel_id":"gll", "type":2}},"data":{"config_update":{bash: line 2: name:: command not found
bash: line 3: syntax error near unexpected token `}'
bash: line 3: `} '}}}' | jq . > jaguar_update_in_envelope.json'

This is because the EOF format will treat each new line as a different command. So we need to replace all new line characters

bash -l <<EOF
printf "%s%s%s" '{"payload":{"header":{"channel_header":{"channel_id":"gll", "type":2}},"data":{"config_update":' $(sed -E 's|"|\\"|g' jaguar_update.json | tr -d '\n') '}}}' | jq . > jaguar_update_in_envelope.json
EOF

And now in ansible

- name: a play that runs entirely on the ansible host
  hosts: 127.0.0.1
  connection: local
  tasks:
     - name: Solve the problem
       shell: |
           bash -l <<EOF
                printf "%s%s%s" '{"payload":{"header":{"channel_header":{"channel_id":"gll", "type":2}},"data":{"config_update":' $(sed -E 's|"|\\"|g' jaguar_update.json | tr -d '\n') '}}}' | jq . > jaguar_update_in_envelope.json
           EOF

And the result

$ ansible-playbook test.yml
PLAY [a play that runs entirely on the ansible host] *********************************************************************************************************************************

TASK [Gathering Facts] ***************************************************************************************************************************************************************
ok: [127.0.0.1]

TASK [Solve the problem] *************************************************************************************************************************************************************
changed: [127.0.0.1]

PLAY RECAP ***************************************************************************************************************************************************************************
127.0.0.1                  : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

$ cat jaguar_update_in_envelope.json
{
  "payload": {
    "header": {
      "channel_header": {
        "channel_id": "gll",
        "type": 2
      }
    },
    "data": {
      "config_update": {
        "name": "tarun"
      }
    }
  }
}
Tarun Lalwani
  • 142,312
  • 9
  • 204
  • 265
1

c.f. the docs -

  • The command(s) will not be processed through the shell, so variables like $HOME and operations like "<", ">", "|", ";" and "&" will not work. Use the shell module if you need these features.

shell pretty literally submits a script to the sh command parser.

Another note - you end the single-quote before the $(cat jaguar_update.json) and restart it after, but don't use any double quoting around it. Your output may handle that, but I wanted to call attention in case it matters.

Paul Hodges
  • 13,382
  • 1
  • 17
  • 36
1

To avoid any complexity, try as in this question to wrap your command in a script, and call that script (with command or shell)

- name: Add header
      raw: /path/to/script/docker-add-header.sh

And in /path/to/script/docker-add-header.sh:

docker exec cli -it bash -l -c "echo '{"payload":{"header":{"channel_header":{"channel_id":"gll", "type":2}},"data":{"config_update":'$(cat jaguar_update.json)'}}}' | jq . > jaguar_update_in_envelope.json

Try to make the script work first alone (no Ansible).
See (if it is not working, even outside any Ansible call), to escape nested double-quotes:

docker exec cli -it bash -l -c "echo '{\"payload\":{\"header\":...
VonC
  • 1,262,500
  • 529
  • 4,410
  • 5,250
  • haha. That's what i have done for now as a fix to it. But i was seeking solution for ansible also :-) – TechChain Jul 15 '19 at 05:13
  • @DhirajKumar I understand. But after many trials, that is what I ended up doing as well: using a script. – VonC Jul 15 '19 at 05:18
  • Thanks @Vonc. I think then we only left with shell script solution – TechChain Jul 15 '19 at 05:38
  • @DhirajKumar I agree: let us wait the end of your bounty and see if anyone has a better solution to offer. – VonC Jul 15 '19 at 06:12