Chef运行中先生成文件名再下载S3备份的方案咨询
这个问题我之前也碰到过,核心是Chef的**编译阶段(Compile Phase)和收敛阶段(Converge Phase)**的执行顺序差异搞的鬼,咱们一步步拆解解决:
问题根源
你当前的代码里,ruby_block的逻辑是在收敛阶段才会执行,但aws_s3_file资源的remote_path属性会在编译阶段就被解析求值——这时候latest_backup变量不仅还没被赋值,而且它是ruby_block内部的局部变量,外部资源根本访问不到,所以才会抛出undefined method 'latest_backup'的错误。
另外还要注意你原代码里的时间条件逻辑有问题:(utc_now > utc_midday ) && (utc_now < utc_midnight)这个判断永远不会成立(utc_midday是22点,utc_midnight是10点,时间不可能同时大于22点又小于10点),得先修正这个逻辑漏洞。
最佳解决方案
方案1:编译阶段直接计算文件名(推荐,最简单高效)
如果文件名的生成不需要依赖收敛阶段的资源状态(比如不需要先检查某个文件是否存在),直接在编译阶段计算文件名是最优选择,这样aws_s3_file可以直接使用变量:
require 'time' utc_now = Time.now.utc current_date = utc_now.strftime("%Y%m%d") utc_midnight = Time.new(utc_now.year, utc_now.month, utc_now.day, 10, 00, 1).utc utc_midday = Time.new(utc_now.year, utc_now.month, utc_now.day, 22, 00, 1).utc if utc_now > utc_midnight && utc_now < utc_midday # 当天10:00:01到22:00:00之间,使用当天的-00001.tgz latest_backup = "data_#{current_date}-00001.tgz" elsif utc_now >= utc_midday # 当天22:00:01到次日10:00:00之间,使用当天的-120001.tgz latest_backup = "data_#{current_date}-120001.tgz" else # 当天00:00:00到10:00:00之间,使用前一天的-120001.tgz previous_date = (utc_now - 86400).strftime("%Y%m%d") latest_backup = "data_#{previous_date}-120001.tgz" end aws_s3_file "/root/backup.tgz" do remote_path "backup-dir/#{latest_backup}" bucket "my-backups-bucket" region "ap-southeast-2" end
方案2:收敛阶段生成文件名(适合依赖收敛状态的场景)
如果必须在收敛阶段生成文件名(比如需要先执行某些资源后才能确定文件名),可以用node.run_state临时存储变量,配合lazy属性延迟remote_path的求值:
ruby_block "generate file name" do block do require 'time' utc_now = Time.now.utc current_date = utc_now.strftime("%Y%m%d") utc_midnight = Time.new(utc_now.year, utc_now.month, utc_now.day, 10, 00, 1).utc utc_midday = Time.new(utc_now.year, utc_now.month, utc_now.day, 22, 00, 1).utc if utc_now > utc_midnight && utc_now < utc_midday node.run_state[:latest_backup] = "data_#{current_date}-00001.tgz" elsif utc_now >= utc_midday node.run_state[:latest_backup] = "data_#{current_date}-120001.tgz" else previous_date = (utc_now - 86400).strftime("%Y%m%d") node.run_state[:latest_backup] = "data_#{previous_date}-120001.tgz" end end action :create end aws_s3_file "/root/backup.tgz" do # 使用lazy块延迟到收敛阶段求值remote_path remote_path lazy { "backup-dir/#{node.run_state[:latest_backup]}" } bucket "my-backups-bucket" region "ap-southeast-2" # 确保S3下载在文件名生成之后执行 subscribes :create, 'ruby_block[generate file name]', :immediately end
这里node.run_state是Chef用来临时存储运行时数据的容器,不会持久化到节点属性中,非常适合这种临时变量的场景;lazy块会推迟属性的求值到收敛阶段,这时候node.run_state[:latest_backup]已经被赋值了。
内容的提问来源于stack exchange,提问作者never_odd_or_even

