VariablesComponent XMLThere are many instances where variables prove useful in providing a flexible component deployment model. By providing as many configuration parameters as possible you can allow for the maximal reuse of the same component xml. A simple example is if we use variables to define the port and docroot of Apache then we can reuse the same Apache component xml when we wish to deploy Apache using different ports or docroots. <?xml version="1.0" encoding="UTF-8"?> <project name="apache" default="clean" basedir="."> ... <property name="home" value="/opt/dev/apache/httpd/"/> <property name="port" value="80"/> <property name="webroot" value="${home}/htdocs/"/> ... <target name="deploy"> ... <!-- set docroot --> <replace file="${home}conf/httpd.conf" token="DocumentRoot "/opt/dev/apache/httpd/htdocs"" value="DocumentRoot "${webroot}""/> <!-- set port --> <replace file="${home}conf/httpd.conf" token="Listen 80" value="Listen ${port}"/> ... </target> ... </project> In this example, we set the port and docroot to the natural defaults of Apache - 80 and the htdocs directory within the Apache home directory. This example can be further extended to include variables to specify which particular version of Apache is downloaded, where it is installed, and much more. The end result being that we don't require a separate component xml file for the multiple different configurations of Apache that need to be supported. System XMLThe system xml file, amongst other things, is the file that specifies which components are involved. In declaring the components used you can also specify which variables will be set for each target invocation. So continuing with the Apache example, the default port and webroot variables could be set with the overriding values 88 and "/home/wwwroot" as shown: <comp base="apache" id="apache"> <property name="port">88</property> <property name="webroot">/home/wwwroot</property> </comp> Now the values of these variables do not need to be as static. They may obtain their values from other variables in the system, agent, group or component scope. For example, we can declare the value of the mysql port as a system level variable with the components requiring it accessing it as shown: <system> <property name="mysqlport" value="3306"/> ... <comp base="mysql" id="mysql"> <property name="port" value="${system.mysqlport}"/> ... </comp> <comp base="php" id="php"> <property name="mysqlport" value="${system.mysqlport}"/> ... </comp> Alternatively the port property could be declared in the mysql component and accessed by the php component via the component scope. <system> ... <comp base="mysql" id="mysql"> <property name="port" value="3306"/> ... </comp> <comp base="php" id="php"> <property name="mysqlport" value="${component.mysql.port}"/> ... </comp> These examples demonstrate that there is a degree of flexibility on offer and the choice may be down to readability, or simply personal preference. Variable ScopingThe system scope provides access to the variables defined in the parent <system> and thus is accessible to the children of the <system> element. That is a inside a <agent>, <group>, or <component> can access the value of a <system> variable. System scoped variables are accessible via the "system." prefix. This is particularly useful for global variables, for example declaring the repository url from which components should download from. <system> <property repository="http://blackbadger.spikesource.com/repository/"/> ... <comp base="apache" id="apache"> <property name="repository" value="${system.repository}"/> ... </comp> They may also be accessed in the component xml file, again using the "system." prefix. That is, as an alternative to the above example we could set the repository variable using the same 'system.repository' reference within the Apache component xml. The agent scope provides the set of variables specific to a given agent block. As well as the declared <property> elements in the <agent> block, the agent scope is also automatically populated with details of the machine that it's been chosen to run on - the ipaddress, os, osarch, and osversion. These are available to the groups and components that an agent contains via the "agent.this." prefix. It's also possible to access the agent variables of another non-parent agent by using the prefix "agent.<id>." where "<id>" should be replaced with the id of the agent in question. agent.<id>.ipaddress - agent machine ip address agent.<id>.os - agent machine OS agent.<id>.osarch - agent machine OS architecture agent.<id>.osversion - agent machine OS version One possible scenario where this is useful is when you have PHP and MySQL deployed on different agents. PHP needs to know the IP address of the MySQL instance so it can access the agent scope of the agent that's running the MySQL instance to do so. <agent id="webagent"> <group id="web"> <comp base="php" id="php"> <property name="mysql_ipaddress">${agent.dbagent.ipaddress}</property> ... </comp> ... </group> </agent> <agent id="dbagent"> <group id="db"> <comp base="mysql" id="mysql"> <property name="ipaddress">${agent.this.ipaddress}</property> <property name="port">3306</property> </comp> </group> </agent> If you attach variables to a given group, they can be accessed using the "group.this." prefix by the components within that group. Similar to the agent scope, you may also access variables in other groups by using the "group.<id>." prefix where this time the id is that of the group. The component scope provides access to the variables defined within a component. Taking the previous example, we could alternatively use the component scoped component.mysql.address to set the php "mysql_ipaddress" variable. <comp base="php" id="php"> <property name="mysql_ipaddress">${component.mysql.ipaddress}</property> ... </cfcomp> In general it's good practice to fully qualify variable names - that is you should refer to variables using their name prefixed with the scope that it belongs to. However in the case where the scope is not specified, BlackBadger supports automatic scope searching. Firstly BlackBadger checks the scope that the variable belongs to. So in the following example the port variable used in the 'url' variable value is resolved to the 'port' variable in the same system scope. <system> <property name="port" value="80"/> <property name="url" value="http://www.mysite.com:${port}"/> ... Should the variable not be found in the same scope, BlackBadger will search the 'outer' scopes in order. If the origin of the variable in question is the component scope, the group, agent and system will also be searched. If the origin scope is the agent scope, the agent and system scope will be searched in that respective order. Finally if the origin scope is the agent scope, only the system scope will be searched. This slightly contrived example illustrates how the scope searching is performed. <system> <property name="port" value="80"/> <property name="url" value="http://www.mysite.com:${port}"/> <agent id="webserver"> <property name="url" value="http://${ipaddress}:${port}"/> <group id="web"> <comp base="apache" id="apache"> <property name="apache.port" value="${port}"/> <property name="apache.url" value="${url}"/> ... If we take the 'apache.port' variable in the declared <comp>, it refers to the unscoped 'port' variable for it's value. BlackBadger looks for a 'port' variable in the component scope, then group scope, followed by the agent scope and finishing successfully on the system scope where it finds the value "80" to use. The 'apache.url' variable on the other hand will obtain it's value from the 'url' variable specified in the agent scope as it takes precedence in the search over the 'url' variable in the system scope. The value of that 'url' variable contains a reference to an unscoped 'ipaddress' variable. Since BlackBadger will search the agent scope first in that instance it will find and resolve the value to the 'agent.this.ipaddress' variable (one of the automatically created variables). |