Chef条件判断逻辑异常求助:文件检测始终触发报错
Chef Cookbook逻辑问题排查:文件存在判断失效导致raise触发
首先得说,你这段代码的核心问题是没搞清楚Chef的执行阶段和资源块的代码运行时机,再加上对Chef资源属性的错误使用,才导致不管文件是否存在都会触发raise。具体问题拆解如下:
问题分析
- raise语句被提前执行:在Chef的资源块(比如你写的
file块)里,直接写的Ruby代码(比如raise)会在编译阶段就运行,根本不会等到资源的执行阶段,也不会受only_if/not_if的约束。也就是说,Chef一开始解析你的Cookbook时,这个raise就直接触发了,完全不管文件是否存在。 - 资源属性逻辑混乱:你给同一个
file资源同时设置了only_if和not_if,这两个条件互斥,会导致这个file资源永远不会被执行,但这不是报错的原因——真正的元凶是那个提前执行的raise。另外,raise根本不是file资源的合法属性,你不能这么写。
正确的实现方案
要实现“文件存在就终止流程,不存在就执行下一个recipe”的逻辑,有两种常用的正确方式,你可以根据场景选择:
方式一:用ruby_block处理执行阶段的判断(推荐)
如果你的锁文件可能是在之前的资源执行过程中创建的(比如其他资源在执行阶段生成),那么用ruby_block最合适,因为它的代码会在执行阶段运行,能准确拿到文件的实时状态:
ruby_block 'check_backup_lock_file' do block do lock_file = '/tmp/db_backup.lock' if ::File.exist?(lock_file) # 加上文件修改时间让报错信息更有用 raise "Backup is already in progress since #{::File.mtime(lock_file)}" end end end # 只有上面的ruby_block不抛出异常,才会执行这个recipe include_recipe "#{cookbook_name}::hostname"
方式二:编译阶段直接判断(适合静态文件检查)
如果锁文件是在Cookbook运行前就已经存在的(不是Cookbook运行过程中生成的),可以直接在资源外做Ruby判断,这样在编译阶段就会终止流程:
lock_file = '/tmp/db_backup.lock' if ::File.exist?(lock_file) raise "Backup is already in progress since #{::File.mtime(lock_file)}" end include_recipe "#{cookbook_name}::hostname"
额外提醒
Chef的执行分为两个关键阶段:
- 编译阶段:Chef会解析所有的资源代码,构建资源列表,这个阶段里所有直接写在资源块外的Ruby代码都会执行。
- 执行阶段:Chef会按照资源列表的顺序,逐个执行资源的动作,只有资源内部的
block(比如ruby_block的block)才会在这个阶段运行。
搞清楚这两个阶段的区别,就能避免很多类似的逻辑错误啦。
内容的提问来源于stack exchange,提问作者ryekayo
相关产品推荐
相关产品推荐

