Variables

A major benefit of using a workflow solution is the consistency and availability properties provided by the system. A simple litmus test for identifying the need for a workflow based solution is answering the question, Where does the state live? If it's ok for the state to live on a single system, then running a regular script or program there is probably a simpler solution. Another way to ask the question is this: Is there a system whose failure makes the operation irrelevant? If such a system exists, then it should be the one driving the operation and keeping the state. For example, if the operation consists of cleaning the temporary files of an instance, then that instance failing makes the operation irrelevant. Therefore, it is OK for it to be responsible for scheduling the operation.

For distributed operations, there is often no simple answer to where state can be kept or where a conventional program can be run. The operation must continue regardless of individual system failures. For example, if the operation consists of analyzing the metrics data on all the instances in a deployment and taking action if certain values reach a given threshold, then no single instance is a good place to run the operation. And a separate management server isn't good either as it can fail as well. In this case, using a workflow based solution makes sense as the workflow engine is in charge of keeping the state and making sure that the operation is either carried out or gives proper feedback in case of failure. This is obviously not the only reason to use a workflow solution, but state bookkeeping is one of the main perks that come out of it. (Managing concurrent execution of activities is another big one described in the Cloud Workflow Processes section.)

The state of a process is kept in variables and references. References were already covered in Cloud Workflow Definitions. They contain collections of resources. A Cloud workflow variables may contain a number, a string, a boolean, a time value, the special null value, an array or a hash. Variables are initialized directly via literal values in code, from resource fields or from the results of a computation. A variable name must start with $ and may contain letters, numbers, and underscores. As with references the naming convention consists of using lower_case. For example:

Initializing a variable from a literal value

$variable = 1 

Initializing a variable from a resource field:

@servers = rs_cm.get(href: "/api/deployments/123").servers()

$names = @servers.name[]

The above code initializes the $names variable to an array containing the names of all servers in deployment with href /deployments/123.

Accessing a variable that has not been initialized is not an error, it just returns the value null.

Click the appropriate link below to learn more about variables.

Writing Explicit Values
Datetime Data Type
Operations on Variables
References and Variables Scope
Global References and Variables
Use of Variables in Place of Namespace, Type or Resource Action or Link

Writing Explicit Values

The syntax used to write explicit values uses the Javascript notation:

$object = { "rails/app_name": "Mephisto", "db_mysql/password": "cred:DB_PASSWORD", "app_server/ports": [80, 443] } 

The above will initialize the $object variable with the given object (hash).

The value stored at a given key of an object can be read using the bracket operator [ ], such as:

$object["rails/app_name"] # Will resolve to "Mephisto" 

The value stored at a given index of an array field value can also be read using the bracket operator [ ]. For example:

$object["app_server/ports"][0] # Will resolve to 80 

Note:Array indices are 0-based.

Datetime Data Type

Cloud Workflow Language xtends the Javascript syntax to add support for the Datetime data type. This type is stored internally as the number of seconds since the epoch and thus supports up to the second granularity. The syntax for writing Datetime values is:

pre dyear/month/day [hours:minutes:seconds] [AM|PM]

Note:Date time values do not include a timezone. If needed, the timezone information must be stored separately.

Examples are:

d"2012/07/01" 

d"2012/07/12 04:21:14 PM" 

If no time is specified then midnight is used (00:00:00). If no AM/PM value is specified then AM is assumed unless the hour value is greater than 12.

Operations on Variables

The language supports a range of operators for dealing with variables including arithmetic operators to manipulate numbers, operators to concatenate strings, arrays, and objects as well as logical operators.

Some examples are listed below:

Arithmetic
Concatenation / Merging
Boolean Operators
Miscellaneous

References and Variables Scope

The Cloud Workflow Language supports two types of variables and references: local variables and references are only accessible from a limited scope (defined below) while Global References and Variables are accessible throughout the execution of the process that defines them.

Click the appropriate link to learn more about:

Local Variables, Local References and Blocks
Local Variables and References Definitions

Local Variables, Local References and Blocks

A block in Cloud Workflow Language is code contained inside a define, a sub, a if or a loop expression (while, foreach or map).

Both local references and variables are scoped to their containing block and all children blocks. This means that a variable initialized in a parent block can be read and modified by child blocks. Consider the following:

$variable = 42 

sub do 

  assert $variable == 42 # $variable is "inherited" from parent block 

  $variable = 1 

end 

assert $variable == 1 # $variable is updated in child block 

The scope of $variable in the example above covers the child block, modifying that variable there affects the value in the parent block (or more exactly both the child and parent blocks have access to the same variable).

Note that the scope is limited to the block where a variable or a reference is first defined and child blocks. In particular the value cannot be read from a parent block:

sub do 

  @servers = rs_cm.servers.get(filter: ["name==my_other_server"])

    assert @servers.name == "my_other_server" # true, @servers was "overridden" 

  sub do 

    assert @servers.name == "my_other_server" # true, @servers contains value defined in parent block 

  end 

end 

assert @servers.name == "my_other_server" # RAISES AN ERROR, @servers is not defined in the parent block 

Local Variables and References Definitions

The only local variables and references that are defined when a definition starts are the ones passed as arguments. Consider the following:

define main() do 

  @servers = rs_cm.servers.get(filter: ["name==my_server"]) 

  @other_servers =rs_cm.servers.get(filter: ["name==my_other_server"]) 

  call launch_servers(@servers

end 

 

define launch_servers(@servers) do 

  assert @servers.name == "my_server" # @servers contains value passed in arguments 

  assert @other_servers.name == "my_other_server" # RAISES AN ERROR, @other_servers is not passed in arguments 

end 

In the example above the launch_servers definition does not have access to the @other_servers local reference because it is not listed as an argument.

Global References and Variables

Sometimes it is useful to retrieve a value defined in an inner block or in one of the sub definitions called via call. Global references and global variables can be used for that purpose as their scope is the entire process rather than the current block. Global references are prefixed with @@, while global variables are prefixed with $$:

sub do 

  $$one = 1 

end 

assert $$one == 1 # global variable remains set for the entire execution of the process 

Global references and variables exist for the lifetime of the process, independently of where they are set:

define init_servers() do 

  @@servers = rs_cm.get(href: "/api/servers/234")

end 

call init_servers()

assert @@servers.name == "my_server" # @@servers set for the entire execution of the process 

Best Practice:Use return values to retrieve values from a different definition via the return and retrieve attributes as shown in the Cloud Workflow section.

Special care needs to be taken when using global references or variables in a process that involves concurrency. For more details, see Processes.

Use of Variables in Place of Namespace, Type or Resource Action or Link

It is possible to use a Variable in place of a Namespace. An example would be a definition that creates a resource of unspecified type, which is passed as an argument to that definition:

define my_create_method($namespace, $type) return @any do 

  @any = $namespace.$type.create(some_common_arguments: { color: "Saffron" })

end 

A Variable can also be used in place of an action or link for either a Namespace, Type or Resource:

...

$action = "do_stuff" 

$arg = "stuff" 

my_ns.$action($arg) # equivalent to my_ns.do_stuff($arg) 

my_ns.my_type.$action($arg) # equivalent to my_ns.my_type.do_stuff($arg) 

@my_resource.$action($arg) # equivalent to @my_resource.do_stuff($arg) 

...

You can also combine multiple uses of Variables in place of Namespace, etc. into a single command:

...

$namespace = "rs_cm" 

$type = "servers" 

$action = "get" 

@result = $namespace.$type.$action(href: $my_href)

...

Note that a Variable cannot currently be used in place of a field name.