使用Ansible将本地文件Upsert至远程MongoDB遇问题求助
问题解决:Ansible通过stdin传JSON到mongoimport报错的处理
问题重现
我尝试用Ansible将本地JSON文件的记录Upsert到远程MongoDB集合,不想走"传文件→执行→删文件"的流程,于是写了两个任务:本地读取文件内容,远程通过stdin传给mongoimport执行Upsert。剧本代码如下:
- name: 读取users集合数据 local_action: module: ansible.builtin.command cmd: cat files/mongo/users.js register: users_collection - name: Upsert到MongoDB ansible.builtin.command: cmd: >- mongoimport --db mydb --collection users --jsonArray --mode upsert --upsertFields username stdin: "{{ users_collection.stdout }}" failed_when: result.stdout.find("0 document(s) failed to import.") != -1
但执行时mongoimport抛出奇怪错误:
Failed: error processing document #1: invalid character 'r' in literal Timestamp (expecting 'i') 0 document(s) imported successfully. 0 document(s) failed to import.
我的JSON里根本没有Timestamp类型,手动传文件到远程执行mongoimport完全正常,把剧本里的mongoimport换成cat验证输入内容也没问题,而且报错时显示"0个失败文档"也不符合逻辑。
原因分析与解决办法
1. 替换文件读取方式:用slurp代替cat
command: cat读取文件时可能引入隐藏的格式问题(比如换行符转换、编码异常),Ansible自带的slurp模块是专门用于读取文件内容的,能更可靠地处理文件编码和格式:
- name: 读取users集合数据 local_action: module: ansible.builtin.slurp src: files/mongo/users.js register: users_collection - name: Upsert到MongoDB ansible.builtin.command: cmd: >- mongoimport --db mydb --collection users --jsonArray --mode upsert --upsertFields username stdin: "{{ users_collection.content | b64decode }}" failed_when: "'Failed:' in result.stdout"
slurp返回的内容是Base64编码的,所以需要用b64decode过滤器解码后再传给stdin。
2. 换用shell模块执行mongoimport
command模块在处理stdin时可能存在字符转义或管道处理的问题,换成shell模块可以避免这类问题:
- name: Upsert到MongoDB ansible.builtin.shell: | mongoimport \ --db mydb \ --collection users \ --jsonArray \ --mode upsert \ --upsertFields username stdin: "{{ users_collection.stdout }}" failed_when: "'Failed:' in result.stdout"
3. 处理换行符格式问题
如果你的JSON文件是Windows格式(CRLF换行),远程Linux主机上的mongoimport可能解析出错。可以在读取时直接转换为LF换行:
- name: 读取并转换换行符 local_action: module: ansible.builtin.command cmd: cat files/mongo/users.js | tr -d '\r' register: users_collection
4. 修正failed_when的判断逻辑
你原来的failed_when逻辑写反了:当stdout包含"0 document(s) failed to import."时标记失败,但这句话实际表示没有失败文档,正确的判断应该是检查输出中是否存在"Failed:"字样,这样才能准确捕获错误。
内容的提问来源于stack exchange,提问作者rgov
相关产品推荐
相关产品推荐

