I am trying to build a custom resource which would in turn use another of my custom resource as part of its action. The pseudo-code would look something like this
customResource A
property component_id String
action: doSomething do
component_id = 1 if component_id.nil?
node.default[component_details][component_id] = ''
customResource_b "Get me component details" do
comp_id component_id
action :get_component_details
end
Chef::log.info("See the output computed by my customResourceB")
Chef::log.info(node[component_details][component_id])
end
Thing to note:
1. The role of customResource_b is to make a PS call to a REST web service and store the JSON result in node[component_details][component_id] overriding its value. I am creating this attribute node on this resource since I know it will be used later one, hence avoiding compile time issues.
Issues I am facing:
1. When testing a simple recipe that calls this resource in chef-client, the code in the resource gets executed to the last log line and after that the call to customResource_b is made. Which is something I am not expecting to happen.
Any advice would be appreciated. I am also quite new to Chef so any design improvements are also welcome
there is no need to nest chef resources, rather use chef idompotance, guards and notification.
and as usualy, you can always use a condition to decide which cookbook\recipe to run.
Related
I am trying to develop a simple Lita chat bot with more flexible command routing.
There are a couple of issues I am having difficulties with.
1. Conditional routing
How can I use config values before or inside route definitions?
For example, instead of this definition that needs a "run" prefix:
route(/^\s*run\s+(\S*)\s*(.*)$/, :cmd, command: true)
I would like to use something like this, with a flexible, config-based prefix:
route(/^\s*#{config.prefix}\s+(\S*)\s*(.*)$/, :cmd, command: true)
Which fails. So I also tried something like this:
if config.use_prefix
route(/^\s*run\s+(\S*)\s*(.*)$/, :cmd, command: true)
else
route(/^\s*(\S*)\s*(.*)$/, :cmd, command: true)
end
Which also fails with a not very helpful error.
In both cases, I defined the proper config key with config :prefix and config :use_prefix.
2. Showing the bot name in the help
I know there is a robot.name property available for me inside the executed command, but I was unable to use it inside of the help string. I was trying to achieve something like this:
route(/^\s*run\s+(\S*)\s*(.*)$/, :cmd, command: true, help: {
"run SCRIPT" => "run the specified SCRIPT. use `#{robot.name} run list` for a list of available scripts."
})
but it just printed something unexpected.
Any help is appreciated.
The issue is that you're confusing the config class method and the config instance method. config at the class level (code in the class body but not inside an instance method definition) defines a new configuration attribute for the plugin. config at the instance level (inside an instance method or in an inline callback provided to route using a block) accesses the values of the plugin's own configuration at runtime.
In the current version of Lita, there isn't a pretty way to use runtime configuration in class-level definitions like chat routes. The workaround I've used myself is to register an event listener for the :loaded event, which triggers when the Lita::Robot has been initialized. At this point, configuration has been finalized, and you can use it to define more routes.
For example:
class MyHandler < Lita::Handler
on :loaded, :define_dynamic_routes
def define_dynamic_routes(payload)
if config.some_setting
self.class.route(/foo/, :callback)
else
self.class.route(/bar/, :callback)
end
end
end
You can look at the code for lita-karma for a more detailed example, as it uses this pattern.
The next major version of Lita is going to include an overhaul to the plugin system which will make this pattern much easier. For now, this is what I'd recommend, though.
Maybe this is really simple, and I'm just not understanding something. I want to invoke a Chef resource from within an HWRP that I wrote. In my scenario, I'd like to invoke the reboot resource. How should I go about doing so?
I have tried something like the following:
def reboot_system
wu_reboot = Chef::Resource::Reboot.new('wu_reboot', :reboot_now)
wu_reboot.run_action(:reboot_now)
end
A few things. I am not sure if I should be creating an instance of Chef::Resource::Reboot or Chef::Provider::Reboot. I also don't really understand the second argument listed above..this is supposed to be the "run_context", but I don't know what that is. Finally, I do not know how to set attributes or invoke an action.
I tried using this as a format to go by, but I haven't been able to get it to work so far. Any help understanding would be much appreciated.
EDIT:
I looked at the source code and I could just execute this:
node.run_context.request_reboot(
:delay_mins => #new_resource.delay_mins,
:reason => #new_resource.reason,
:timestamp => Time.now,
:requested_by => #new_resource.name
)
However, I don't think this is the best solution. I would like to know how to accomplish invoking the resource instead of bypassing it this way.
You can find an example of using Chef-Resources inside a HWRP in an older revision of the official Jenkins cookbook (was converted to LWRP in the meantime):
https://github.com/opscode-cookbooks/jenkins/blob/v2.0.2/libraries/plugin.rb#L138-L141
Keep in mind, that the Reboot resource is rather new (Chef 12+)
You can do it the same way you would in a recipe. If you need it to run immediately, then you would do:
reboot 'now' do
action :nothing
end.run_action(:reboot_now)
Within Ruby classes, you don't have access to the Chef DSL, so you have to access the underlying implementation of the resource as a class. The name of the class will be the camelcase-conversion of the resource name. You invoke the action with the run_action method.
Your original version actually was pretty close. You only use the resource, not the provider (because the provider may not even always be the same, depending on your platform).
The run_context is an object that chef uses to pass information to the resource - for instance, you can access node attributes through run_context.node['attributename']. It is already a member variable in your provider (and I think also in the resource object); you can simply pass it in to the constructor for your new resource.
You set attributes through member variables by the same name, and you trigger the actual action with the run_action method.
r = Chef::Resource::Reboot.new("wu_reboot", run_context)
r.reason("Because we need a reboot")
r.run_action(:reboot_now)
I have a Puppet custom function that returns information about a user defined in OpenStack's Keystone identity service. Usage is something along the lines of:
$tenant_id = lookup_tenant_by_name($username, $password, "mytenant")
The problem is that the credentials used in this query ($username) are supposed to be created by another resource during the Puppet run (a Keystone_user resource from puppet-keystone). As far as I can tell, the call to the lookup_tenant_by_name function is being evaluated before any resource ordering happens, because no amount of dependencies in the calling code is able to force the credentials to be created prior to this function being executed.
In general, it is possible to write custom functions -- or place them appropriately in a manifest -- such that they will not be executed by Puppet until after some specified resource has been instantiated?
Short answer: You cannot make your manifest's behavior depend on resources declared inside of it.
Long answer: Parser functions are called during the compilation phase (on the master if you use one, or the agent if you use puppet apply). In neither case can it ever run before any resource is synced, because that will happen after the compiler has done all its work (including invocation of your functions).
To query information from the agent machine, you generally want to use custom facts. Still, those will be populated before even the compiler run.
Likely the best approach in this situation is to make the manifest tolerate the absence of the information, so that anything that depends on the value that your lookup_tenant_by_name function returns will only be evaluated if that value is available. This will usually be during the second Puppet run.
if $tenant_id == "" {
notify { "cannot yet find tenant $username": }
}
else {
# your code using the tenant ID
}
I'm writing a LWRP for chef 10.
And when that resource is run in other recipes it should be marked as "updated_by_last_action" if something has changed. But if nothing has changed. updated_by_last_action should be false.
So as example I have chef documentation http://docs.opscode.com/lwrp_custom_provider.html#updated-by-last-action. That example the resource template is wrapped inside an variable to test if it's been changed, and then set the updated_by_last_action status.
So my code should look something like this
f = file new_resource.filename do
xxx
end
new_resource.updated_by_last_action(f.updated_by_last_action?)
t = template new_resource.templatename do
xxx
end
new_resource.updated_by_last_action(t.updated_by_last_action?)
m mount new_resource.mountpoint do
xxx
end
new_resource.updated_by_last_action(m.updated_by_last_action?)
But if a provider gets bigger and uses a lot of resources like template, file, directory, mount, etc..
Should all those resource be wrapped inside variables like the example to find out if a resource have been updated, so to then further send a status that this provider have been updated.
I'm wondering if there is a simpler and cleaner way to run new_resource.updated_by_last_action(true) other then to wrap all resources inside variables. Cause if I just put a new_resource.updated_by_last_action(true) inside action before end the LWRP is marked as being updated every chef run, which is not optimal.
You can add use_inline_resources at the top of your LWRP, which delegates the updated_by_last_action to the inline resources.
I have two LWRPs. The first deals with creating a disk volume, formatting it, and mounting it on a virtual machine, we'll call this resource cloud_volume. The second resource (not really important what it does) needs a UUID for the newly formatted volume which is a required attribute, we'll call this resource foobar.
The resources cloud_volume and foobar are used in a recipe something like the following.
volumes.each do |mount_point, volume|
cloud_volume "#{mount_point}" do
size volume['size']
label volume['label']
action [:create, :initialize]
end
foobar "#{mount_point}" do
disk_uuid node[:volumes][mount_point][:uuid] # This is set by cloud_volume
action [:do_stuff]
end
end
So, when I do a chef run I get a Required argument disk_identifier is missing! exception.
After doing some digging I discovered that recipes are processed in two phases, a compile phase and an execute phase. It looks like the issue is at compile time as that is the point in time that node[:volumes][mount_point][:uuid] is not set.
Unfortunately I can't use the trick that OpsCode has here as notifications are being used in the cloud_volume LWRP (so it would fall into the anti-pattern shown in the documentation)
So, after all this, my question is, is there any way to get around the requirement that the value of disk_uuid be known at compile time?
A cleaner way would be to use Lazy Attribute Evaluation. This will evaluate node[:volumes][mount_point][:uuid] during execution time instead of compile
foobar "#{mount_point}" do
disk_uuid lazy { node[:volumes][mount_point][:uuid] }
action [:do_stuff]
end
Disclaimer: this is the way to go with older Chef (<11.6.0), before they added lazy attribute evaluation.
Wrap your foobar resource into ruby_block and define foobar dynamically. This way after the compile stage you will have a ruby code in resource collection and it will be evaluated in run stage.
ruby_block "mount #{mount_point} using foobar" do
block do
res = Chef::Resource::Foobar.new( mount_point, run_context )
res.disk_uuid node[:volumes][mount_point][:uuid]
res.run_action :do_stuff
end
end
This way node[:volumes][mount_point][:uuid] will not be known at compile time, but it also will not be accessed at compile time. It will only be accessed in running stage, when it should already be set.