Attributes

Every Reactor has the following attributes, built from either the Reactor’s execution environment or by means of some clever mocking code in a local test environment:

name type contents source
aliases aliases.store.AliasStore A helper for managing App and Actor aliases Configured on instantiation with current client
client agavepy.agave.Agave An active Agave API client Values provided by Abaco platform using agavepy.actors or local Agave API credentials.
context dict Variables passed by Abaco platform to the current execution Values passed by Abaco and materialized via agavepy.actors or generated by mocking support functions.
execid string The current Abaco execution ID `context.execution_id
local boolean True or False to indicate whether the function is running under Abaco or in a testing environment. The LOCALONLY environment variable, which can be set in local testing configurations.
logger `logging.StreamHandler` Pointer to the screen logger of loggers  
loggers dict Python loggers screen, which logs to both STDERR and (optionally) a structured log aggregator, and slack which can log directly to a Slack channel. Configured on instantiation, with credentials for Slack and other services provided by environment variables.
nickname string A semi-random, human memorable string. (e.g. sleek-lemur) A call to the petname library
pemagent agaveutils.recursive.PemAgent A helper for applying recursive Agave API files permissions. Calls to it will eventually be an asynchronous. Configured on instantiation with current client
session string A correlation key for connecting related events. Query parameters SESSION or x-session, then nickname
settings attrdict.AttrDict Contents of config.yml, where keys are accessible in dot.notation. Populated at instantiation from config.yml via tacconfig.load_settings()
state dict The current Abaco actor state variable context.state
uid string The current Abaco actor ID `context.actor_id
username string TACC.cloud username on whose behalf the actor’s current execution is being undertaken. Provided by `context.username under Abaco or by inspecting client when running locally.

aliases

This is an instance of AliasStore with which one can create, get, remove, and share alias entries for any actor. See AliasStore documentation for details.

Example Usage

Get an alias
 >>> r = Reactor()
 >>> id = r.aliases.get('slackbot')
 >>> print(id)
 GGPg6KqVrqPl6
Use the ‘me’ built-in
 >>> r = Reactor()
 >>> print(r.uid)
 e50Yz5jaBA4Eo
 >>> id = r.aliases.get('me')
 >>> print(id)
 e50Yz5jaBA4Eo

client

This is an active AgavePy API client used to make authenticated API calls on behalf of the user that invoked execution of the Reactor. All AgavePy functions are supported.

Example Usage

List available public apps
 >>> r = Reactor()
 >>> apps = r.client.apps.list(publicOnly=True)
 >>> type(apps)
 <type 'list'>
 >>> for a in apps:
 >>>    print(a.name)
 psap-0.1.0u1
 xplan-0.1.0u1
 lcms-pyquant-0.1.1u1
 ...

context

This is a dictionary populated from environment variables passed to the container by Abaco.

Example Usage

An example context
 >>> r = Reactor()
 >>> print(r.context)
 AttrDict({'HOSTNAME': '4647e1acfe46', 'LOCALONLY': '1', '_REACTOR_TEMP': '/mnt/ephemeral-01', 'raw_message_parse_log': 'Error parsing message: malformed node or string: None', 'message_dict': {}, 'SCRATCH': '/mnt/ephemeral-01', 'REACTORS_VERSION': '0.7.5', '_': '/usr/bin/python3', 'SHLVL': '1', 'PWD': '/', 'LESSOPEN': '| /usr/bin/lesspipe %s', 'TERM': 'xterm', 'HOME': '/root', '_PROJ_STOCKYARD': '/work/projects/', 'PATH': '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin', 'OLDPWD': '/mnt/ephemeral-01', 'MSG': 'Hello There', _USER_WORK': '', '_PROJ_CORRAL': '/corral', 'actor_dbid': '5GJbZRQYk0VmY', 'username': 'tacocat', 'actor_id': '5GJbZRQYk0VmY', 'state': {}, 'execution_id': 'DeAzrr6ABO1pr', 'raw_message': 'Hello There', 'content_type': 'application/json'})
 >>> print(r.context.raw_message)
 Hello There

Context combines environment variables inherited from the container image, set in the container’s worker by Abaco, and passed to the specific execution by Abaco into a single dictionary. In addition to the string value keys, there are two important dict objects: state and message_dict

  • state is a dict that can be read from and modified to pass information between executions of an actor without relying on an external database.
  • message_dict is populated by parsing a JSON message passed to the actor into a Python dictionary. This is done automatically and safely via Pythons ast and json.loads functions. If a message can’t be parsed, message_dict is set to an empty AttrDict. If you believe you’re sending valid JSON but it’s not resolving as a dictionary, context.raw_message_parse_log can be inspected for clues to what is causing the failure.

execid

This is the unique identifier for the current execution.

Example Usage

Get the execution identifier
 >>> r = Reactor()
 >>> print(r.execid)
 ePmJlZOg0W03

local

This is a boolean value set based on the state of the LOCALONLY environment variable. It is intended to be used to selectively disable or enable code branches when running under local emulation.

An environment-specific print() statement
 r = Reactor()
 if r.local is not True:
     print('This code is not running under a test environment')
 else:
     print('This code is running locally')

The state of local is set automatically by some CI support scripts, and can be set in a pytest environment using the monkeypatch fixture.

Monkeypatching local to return True
 def test_demo_local(monkeypatch):
     monkeypatch.setenv('LOCALONLY', 1)
     r = Reactor()
     assert r.local is True

logger

This is an ready-to-use Python logger.

Example Usage

Get the execution identifier
 >>> r = Reactor()
 >>> r.logger.info('This is a log message')
 5AB11Q8XxwPK5 INFO This is a log message

A nicely formatted message is printed to STDERR that includes the current actor ID. All logging levels (debug, info, warning, critical) are available. Logging level is set in the logs stanza of config.yml.

At the same time a plaintext message is sent to the standard log, it can (optionally) be sent over the network in a structured format. This is described in the advanced topics section.

Log redaction

Sensitive data passed into a Reactor using the secrets mechanism are redacted automatically when logged. For instance, assume API credentials for AWS have been set in a Reactor. Under a standard logging scheme, it would be very easy to print those sensitive data in build logs, screenshots, commits, and such, where it could be easily discoverable.

Redaction in action
 >>> r = Reactor()
 >>> api_secret = r.settings.api.secret
 >>> r.logger.info('Here are credentials: {}'.format(api_secret))
 Pk4B11Q8Xxw INFO Here are credentials: ****
 >>> api_url = r.settings.api.url
 >>> r.logger.info('Here is API server: {}'.format(api_urk))
 Pk4B11Q8Xxw INFO Here is API server: https://tacos.tacc.cloud/api/v1

loggers

This dict holds references to all loggers configured by a Reactor. At present, two loggers are established. The default logger, screen, prints to STDERR and (optionally) a log aggregator. The other logger, slack, allows logging directly to Slack assuming a webhook is provided when the actor is configured.

Slack logger configuration
---
slack:
  channel: "notifications"
  icon_emoji: ":smile:"
  username: "tacobot"
  webhook: ~
Using loggers
 >>> r = Reactor()
 >>> r.loggers['screen'].info('This actor is a logger and a slacker')
 5AB11Q8XxwPK5 INFO This actor is a logger and a slacker
 >>> r.loggers['slack'].info('This actor is a logger and a slacker')

nickname

Inspired by the naming of Docker containers and other cloud resources, each Reactor invocation is assigned a “human meaningful, decentralized, secure” nickname generated by the petname library. By default, two-word nicknames are used, but this can be overridden with an entry in config.yml

---
nickname:
  length: 3

Example Usage

View the current nickname
 >>> r = Reactor()
 >>> print(r.nickname)
 sleepy-wallaby

pemagent

This is an instance of PemAgent, a helper for recursive Agave files permissions management. Most permissions operations should be handled directly using AgavePy files.*Permissions commands. The pemagent helper exists provide an optimized, and potentially asynchronous, method for doing batch operations.

Example Usage

Let user tacopal read a specific directory
 r = Reactor()
 r.pemsagent.grant(systemId='data.tacc.cloud',
                   absPath='/demo',
                   username='tacopal',
                   pem='READ')

session

This string is a correlation identifier among platform events with a common ancestry. Its value is set as follows:

  1. If environment variable x-session is not empty, session takes on its value
  2. Else, if environment variable SESSION is not empty, session takes on its value
  3. Else, session is set to the value of nickname

Critically, actors can message other actors. When this is done using Reactor.send_message, the session value is forwarded along to the receipients as x-session and will thus become their session identifier.

Example Usage

An Agave API job may send a string, such as job name or ID as SESSION, when messaging an Abaco actor. In this example, demojob is sent as a value for SESSION.

{ "notifications": [
    {
      "url": "https://api.tacc.cloud/actors/v2/eZE7XDPLzZOwo/messages?x-nonce=SD2E_KbyGjq4XOM4&channel=agavejobs&SESSION=demojob",
      "event": "FINISHED",
      "persistent": false
    }
}

The value of session in the downstream Reactor instance will be demojob. If that Reactor messages another reactor, the downstream entity’s session will also be demojob.

settings

state

uid

username