AT&T Interactive R&D

 
 

Writing custom resources

Resources are the basis for PoolParty. These are what allows you to configure your system... but has_file and has_package just don't cut it for your needs... did you know that you can write your own? It's true!

Virtual Resources

PoolParty allows you to create "virtual" resources to extend the resources to... well, whatever you need! For this tutorial, I'll walk you through creating a git resource (built in to the PoolParty resources as a virtual resource).

Nore: The only difference between a virtual resource and a built-in resource is that virtual resources only encapsulate data, they do not define data.

Let's first create the virtual resource.

   1  module CustomModule
   2    virtual_resource(:git) do
   3    end
   4  end
That's it! We've created a very boring resource called git. Let's actually add something to this.

Here is nearly the git resource (a few things changed for clarity-sake) that is baked into PoolParty

   1  virtual_resource(:git) do
   2    # This is called after instantiated
   3    def loaded(opts={}, parent=self)
   4      has_git_repos
   5    end
   6    
   7    def has_git_repos
   8      exec({:name => "git-#{name}"}) do
   9        command parent.user ? "git clone #{parent.user}@#{parent.source}" : "git clone #{parent.source}"
  10        cwd "#{parent.cwd if parent.cwd}"
  11        creates "#{::File.join( (parent.cwd ? parent.cwd : cwd), ::File.basename(parent.source, ::File.extname(parent.source)) )}/.git"
  12        
  13        exec(:name => "update-#{name}") do
  14          cwd ::File.dirname(parent.creates)
  15          command "git pull"
  16        end      
  17      end
  18    end
  19    
  20    # Since git is not a native type, we have to say which core resource
  21    # it is using to be able to require it
  22    def class_type_name
  23      "exec"
  24    end
  25    
  26    # Because we are requiring an exec, instead of a built-in package of the git, we have to overload
  27    # the to_s method and prepend it with the same name as above
  28    def key
  29      "git-#{name}"
  30    end
  31    
  32  end

Wow, that's a lot, right? Don't worry, it's not too daunting, let's go through it.

Obviously we have a virtual resource as we defined from above. When a resources is loaded from within a poolspec, a few things initialize before to set it up, the final phase of the process is that it calls the callback.

   1  loaded(opts, parent)
This is how we'll jump on the back of the resource and attach what we need to get get our resource written. So, from above, it says we'll attach a few execs (called from has_git_repos after loaded is called).

From within our git resource, we want to make sure we have two commands, one to clone the repository and another to update the repos.

   1  exec({:name => "git-#{name}"}) do
   2    command parent.user ? "git clone #{parent.user}@#{parent.source}" : "git clone #{parent.source}"
   3    cwd "#{parent.cwd if parent.cwd}"
   4    creates "#{::File.join( (parent.cwd ? parent.cwd : cwd), ::File.basename(parent.source, ::File.extname(parent.source)) )}/.git"
   5    
   6    exec(:name => "update-#{name}") do
   7      cwd ::File.dirname(parent.creates)
   8      command "git pull"
   9    end      
  10  end
Even though it looks a little daunting, it's not nearly as bad as it looks. Basically, we are setting up a command that will clone and create the directory if it doesn't already exist. (cwd/.git, because the command creates that on clone). If it does exist, then it doesn't run the command. One nuance to notice here is that when we are calling methods from within the resource that we want access outside the resource for, we have to call it on the parent. Otherwise, we'd have the values for the resource (unless they are not set on the resource, and only on the parent).

Note that resources in PoolParty are nested, so that if you nest a resource from within another resource, it will require that the parent resource exists to run the child one. So in this instance, it requires that the repos exists before it calls the update on it.

Finally, we have to set a few quick notes for resourcing. When you do nest a resource, it needs to know how to call the resource. Thus, we have to set the parent class_type_name so that the child knows how to call the resource on it.

Lastly, for clarity sake (this is not necessary), we can change the key so that the names are specific to the resource call. For this instance, I like to prepend it with git-[name]. That way I know where the resource is called from.

Usage

How do we use this new resource? Well, first make sure it's either a part of a plugin or you require it at the top of your file and then call it with a has_ or does_not_have_ call. For example:

   1  has_git(:name => 'mygitrepos', :source => 'git://...)

If you have any questions, comments or suggestions, don't hesitate to ask by contacting me or checking in with other developers. Community