Elsware runs from a configuration, which is a python dictionary. The entry-point into elsware is the method elsware.deploy.deploy(). It takes two parameters, plus any additional keyword arguments, which are available to custom actions.
As an example, the django management command that comes with elsware implements the needed requirements to plug into django’s “manage.py” command, but then kicks everything off to elsware. Here’s a simplified example of that code:
import elsware.deploy
config={...} #comes from settings.DEPLOYMENTS.
#error checking
deploy.deploy(config,deployments,password=password_from_argument)
Using one entry point, you can create your own utilities that use elsware.
Review this example of a configuration dictionary, the following sections describe each piece.
CONFIGURATION={
'servers':{ #server information
'admin@slicehost':{
'ip':'1.1.1.10',
'user':'admin',
}
},
'slicehost_update_app':{ #deployment instruction
'actions':( #action list
{'transaction':('login','update','logout')}
),
'login':{ #action parameters for the login action
'action_class':'ssh_login',
'server':'admin@slicehost'
},
'logout':{ #action parameters for the logout action
'action_class':'ssh_logout',
'server':'admin@slicehost',
},
'update':({ #action parameters for the update action
'action_class':'git_update',
'server':'admin@slicehost',
'dir':'/var/www/vhosts/deployments/dtesting',
})
},
'slicehost_static_media':{ #another deployment instructions
'actions':('push_media'),
'push_media':{
'action_class':'scp',
'server':'admin@slicehost',
'localdir':'/Users/aaronsmith/dev/projects/git/rallyo/static_media',
'serverdir':'/var/www/vhosts/rallyo/media/',
}
}
}
The server information is another dictionary, who’s keys are used as lookups for information about a server. You can use those keys as server names in action parameters. Any action can find the server information, based off of that name.
What’s defined in the above example is generally the bare minimum you need to define for a server. Because the entire configuration is passed around, any action can search for information from anywhere in the dictionary.
The server information is also extendable. You can put your own keys and values inside of anyone of the servers. As an example, the pre-built apache actions requires this in the configuration:
'servers':{
'admin@slicehost':{
'ip':'..',
'user':'...',
'apache':{
'apachectl':'/usr/bin/apachectl',
'sudo:True|False',
}
}
}
The apache actions then search for the ‘apache’ key inside of a server, in order to find where the apachectl binary is. As well as a couple other optional parameters.
A “deployment instruction” is a name given to a deployment process. A single deployment instruction contains the actions to run, and any parameters for the actions. Elsware uses those “deployment instruction” names to run actions.
The entry-point into elsware requires a list of deployment instructions.
import elsware.deploy
deploy.deploy(config,["slicehost_update_app",])
Each deployment instruction requires an action list. Action lists must be a list, or tuple. And can optionally contain a dictionary with a ‘transaction’ key - who’s value is a list of actions.
Actions wrapped in a transaction dictionary initialize transaction logic. Transactions make sure that all actions succeed. If one fails, they all have a chance to roll back.
You can also mix and match transactions, nested transactions, or non-transactional actions.
Consider these examples:
...
#no transactions.
'actions':(
'login','update','logout',
)
...
#transactions plus a non transactional action
'actions':(
'login',{'transaction':('update','restart',)},'logout',
)
...
'actions':(
{'transaction':('login','update','scp','restart_fcgi','logout',),}
)
...
Whenever you define an action list, you can optionally define arguments for each action. When the action class that does the work is instantiated, it’s passed that dictionary of parameters.
The bare minimum:
...
'actions':(
'login',
),
'login':{
'action_class':'ssh_login', #required
'server':'admin@slicehost',
}
...
Elsware looks at the action parameters for the “action_class” key, uses it’s value to find the registered action class. For example, the pre-built action “SSHLoginAction” is registered with elsware as ‘ssh_login’.
When you define those parameters, the entire dictionary is passed to the implementing action, as the “action_info” parameter. This allows the configuration to be dynamic and extendable, and allows your actions to be unbound from any knowledge of the configuration around it.
As a concrete example, review how elsware instantiates an action class, from the login information.:
#oversimplified
info = {
'action_class':'ssh_login',
'server':'admin@slicehost',
}
action_name=info.get("action_class",False)
clss=elsware.actions.ActionClasses.lookup.get(action_name,False)
instance=clss(deployment,info)
The action parameters aren’t required. If you register an action class with elsware, and you use that in the action list, it will be found.
As an example of how it becomes optional, consider the built-in action called “exception”, which is used while developing actions to test transactions support:
'actions':(
'login','update','exception',
)
'login':..,
'update':..,
The “exception” action is registered with elsware like this:
actions.register_action_class('exception',actions.RaiseExceptionAction)
Because the RaiseExceptionAction doesn’t require any special action parameters, you don’t need to specify them.
Leaving passwords in the configuration dictionaries are optional. Generally any action that needs some form of password can be supplied in a couple ways.
Consider the SSH login action, which searches for passwords in configuration:
config={
'servers':{
'admin@slicehost':{
'ip':'..',
'user':'...',
'password':'mypassword' #optional
}
},
'update_slicehost':{
'actions':(
'login','update','logout',
),
...
'login':{
'action_class':'ssh_login',
'password':'mypassword' #optional
}
...
}
}
All of the pre-built actions search for passwords. But they also can search for passwords in the keyword arguments passed to the elsware entrypoint:
import elsware
elsware.deploy.deploy(config,["update_slicehost"],login_password="abRrkdO")
The keyword parameter “login_password” is saved, and can be accessed later by elsware. In this next configuration, the “password_in_opt” is used to signify that the value should be taken from the original keyword argument.
config={
'servers':{
'admin@slicehost':{
'ip':'..',
'user':'...',
'password_in_opt':'login_password'
}
},
'update_slicehost':{
'actions':(
'login','update','logout',
),
...
'login':{
'action_class':'ssh_login',
'password_in_opt':'login_password'
}
...
}
}