访问cookbook配方中更新的节点属性

时间:2015-08-17 01:46:51

标签: ruby chef chef-recipe

我是厨师环境的新手。并制作一本简单的食谱。

为了简化,我有以下属性:

属性/ default.rb:

default[:user1] = ""
default[:user2] = ""
default[:filename] = ""

食谱/ default.rb: 在运行时,我从另一本食谱中获取文件名,我必须从该文件中提取用户。

file = "#{node[:filename]}"

ruby_block 'extract userdata' do
  block do
    json = File.read(file)
    obj = JSON.parse(json)

    userdata = obj['users']

    if userdata.empty?
      raise "Errors: userdata not available"
    else
      node.override[:user1] = userdata['user1']
      node.override[:user2] = userdata['user2']
    end
  end
  puts "user1: #{node[:user1]}"
  puts "user2: #{node[:user2]}"
  action :run
end

在运行时从块上面记录(puts)会正确地获取用户名。

现在,我正在尝试使用上面两个更新的属性,如下面的菜谱配方。

user1 = #{node[:user1]}
user2 = #{node[:user2]}

但是这两个值都是空的,好像它们没有设置/覆盖一样。

请建议我如何获取最新数据。

2 个答案:

答案 0 :(得分:2)

我将总结以下答案(其中给出了关于同一问题的另一种观点),并举例说明了您的具体案例。

这里的问题是compile time versus converge time

这里发生的是在编译时评估配方和资源,并在收敛时评估ruby_block资源的内容。

有两种解决方案可以解决这个问题:

  • 第一个选项是在编译时运行ruby_block:

    ruby_block 'extract userdata' do
      block do
        json = File.read(file)
        obj = JSON.parse(json)
    
        userdata = obj['users']
    
        if userdata.empty?
          raise "Errors: userdata not available"
        else
          node.override[:user1] = userdata['user1']
          node.override[:user2] = userdata['user2']
        end
      end
      puts "user1: #{node[:user1]}"
      puts "user2: #{node[:user2]}"
      action :nothing
    end.run_action(:run)
    

    注意action :nothing以避免资源在收敛时间执行,.run_action(:run)告诉评估在评估后立即运行该资源。

  • 第二个选项是在其他资源中使用lazy evaluation

    execute "Do something" do
      command lazy { "/path/command #{node['user1]}" }
    end
    

    lazy {}必须包含整个属性值,而不仅仅是变量,否则会引发错误。

我建议尽可能多地使用选项2。在编译时执行操作可能会导致难以理解何时出错,因为操作是在两个不同的流程中进行的。

我建议使用字符串来访问节点属性,在固定文本时单引号,在需要插值时双引号,因为符号可能会在属性名称与配方DSL名称空间中的现有符号匹配时获得意外行为。

答案 1 :(得分:0)

在覆盖/设置新值到代码块中的属性后尝试添加 public class NotificationService extends Service { ConnectionFactory factory = new ConnectionFactory(); Connection connection; Channel channel; String HostName = "192.168.2.11"; String UserName = "o"; String Password = "m"; public void onCreate() { } @Override public int onStartCommand(Intent intent, int flags, int startId) { String qName="0833"; String routeKey="0833"; new AsyncSubscribe().execute(qName, routeKey); return Service.START_STICKY; } @Override public void onDestroy() { Toast.makeText(this, "service done", Toast.LENGTH_SHORT).show(); } @Override public IBinder onBind(Intent intent) { return null; } private class AsyncSubscribe extends AsyncTask<String, Void, Void> { Consumer consumer; ArrayList<String> messages = new ArrayList<String>(); @Override protected void onPreExecute() { } @Override protected Void doInBackground(String... params) { try { consumer = new DefaultConsumer(channel) { @Override public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException { properties.builder().deliveryMode(2); // here we add messages to arraylist messages.add(new String(body, "UTF-8")); } }; channel.basicConsume(params[0], true, consumer); } catch ( Exception e) { e.printStackTrace(); } return null; } @Override protected void onPostExecute(Void result) { // here we show arraylist size Toast.makeText(NotificationService.this, Integer.toString( messages.size()), Toast.LENGTH_SHORT).show(); try { channel.close(); connection.close(); } catch (IOException e) { e.printStackTrace(); } catch (TimeoutException e) { e.printStackTrace(); } } } public void SubscribeSet() { try { factory.setHost(HostName); factory.setUsername(UserName); factory.setPassword(Password); connection = factory.newConnection(); channel = connection.createChannel(); } catch ( Exception e) { e.printStackTrace(); } } }

node.save