【问题标题】:Passing variables between chef resources在厨师资源之间传递变量
【发布时间】:2013-07-15 20:11:02
【问题描述】:

我想向您展示我的用例,然后讨论可能的解决方案:

问题 A: 我有 2 个菜谱,“a”和“b”。“a”在我的文件系统上安装了一些程序(比如在“/usr/local/bin/stuff.sh”和菜谱“b”需要运行它并执行输出的东西。

所以配方“a”看起来像:

execute "echo 'echo stuff' > /usr/local/bin/stuff.sh" 

(脚本只是将“stuff”回显到标准输出)

配方“b”看起来像:

include_recipe "a"
var=`/usr/local/bin/stuff.sh` 

(注意反引号,var 应该包含stuff

现在我需要用它做点什么,例如用这个用户名创建一个用户。所以在脚本“b”我添加了

user "#{node[:var]}"

碰巧,这不起作用..显然,厨师运行所有不是资源的东西,然后才运行资源,所以一旦我运行脚本厨师抱怨它无法编译,因为它首先尝试运行配方“b”中的“var=...”行并失败,因为配方 a 中的“执行 ...”尚未运行,因此“stuff.sh”脚本尚不存在。 不用说,这非常烦人,因为它打破了我在开始使用它时承诺的“厨师从上到下按顺序运行一切”。 但是,我不是很挑剔,所以我开始寻找这个问题的替代解决方案,所以:

问题 B:我遇到过“ruby_block”的想法。显然,这是一种资源,因此将与其他资源一起评估。我说好的,然后我想创建脚本,在“ruby_block”中获取输出,然后将其传递给“用户”。所以配方“b”现在看起来像:

include_recipe "a"

ruby_block "a_block" do
  block do
    node.default[:var] = `/usr/local/bin/stuff.sh`
  end
end

user "#{node[:var]}"

但是,事实证明变量 (var) 没有从“ruby_block”传递到“user”,并且它仍然是空的。无论我尝试用它做什么杂耍,我都失败了(或者我只是没有找到正确的杂耍方法)

致周围的厨师/红宝石大师:我如何解决问题 A?如何解决问题 B?

【问题讨论】:

    标签: ruby variables chef-infra chef-solo


    【解决方案1】:

    你已经用 Ruby 块解决了问题 A。

    现在你必须用类似的方法解决问题 B:

    ruby_block "create user" do
      block do
        user = Chef::Resource::User.new(node[:var], run_context)
        user.shell '/bin/bash' # Set parameters using this syntax
        user.run_action :create
        user.run_action :manage # Run multiple actions (if needed) by declaring them sequentially
      end
    end
    

    您也可以通过在编译阶段创建文件来解决问题 A:

    execute "echo 'echo stuff' > /usr/local/bin/stuff.sh" do
      action :nothing
    end.run_action(:run)
    

    如果遵循此操作过程,请确保:

    • /usr/local/bin 存在于 Chef 的编译阶段;
    • 要么:
      • stuff.sh 是可执行的;或
      • 通过 shell 执行它(例如:var=`sh /usr/local/bin/stuff.sh`

    【讨论】:

    • 这个解决方案在 2013 年可能是正确的,但不再正确。即使您需要延迟用户名,一个简单的user lazy { node['var'] } 就足够了,无需将整个声明包装在 ruby​​_block 中,也无需再手动构造资源。
    【解决方案2】:

    执行此操作的现代方法是使用自定义资源:

    在食谱/create_script/resources/create_script.rb 中

    provides :create_script
    unified_mode true
    
    property :script_name, :name_property: true
    
    action :run do
      execute "creating #{script_name}" do
        command "echo 'echo stuff' > #{script_name}"
        not_if { File.exist?(script_name) }
      end
    end
    

    然后在配方代码中:

    create_script "/usr/local/bin/stuff.sh"
    

    对于第二种情况,我会完全避免使用节点变量:

    script_location = "/usr/local/bin/stuff.sh"
    
    create_script script_location
    
    # note: the user resources takes a username not a file path so the example is a bit
    # strange, but that is the way the question was asked.
    user script_location 
    

    如果您需要将它移动到一个属性中并从不同的配方中调用它,那么就不需要 ruby​​_blocks 或lazy:

    一些食谱的属性/default.rb 文件(或策略文件等):

    default['script_location'] = "/usr/local/bin/stuff.sh"
    

    在配方代码或其他自定义资源中:

    create_script node['script_location']
    
    user node['script_location']
    

    没有必要偷懒或使用 ruby​​_block 使用这种方法。

    【讨论】:

      【解决方案3】:

      实际上有几种方法可以解决您遇到的问题。

      第一种方法是避免您在传递的块中遇到的范围问题,并执行类似的操作。

      include_recipe "a"
      this = self
      
      ruby_block "a_block" do
        block do
          this.user `/usr/local/bin/stuff.sh`
        end
      end
      

      假设您打算只使用一次,那会很好用。但是,如果您合法地需要在节点上存储一个变量以供其他用途,您可以依靠 ruby​​ 内部的惰性调用来解决这个问题。

      include_recipe "a"
      
      ruby_block "a_block" do
        block do
          node.default[:var] = `/usr/local/bin/stuff.sh`.strip
        end
      end
      
      user do
          username lazy { "#{node[:var]}" }
       end
      

      您会很快注意到 Chef 对此类情况的所有默认假设进行了覆盖。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2020-07-01
        • 2016-08-02
        • 2021-04-18
        • 2018-10-19
        • 2015-03-08
        • 2021-11-09
        相关资源
        最近更新 更多