A service in Opereto is defined as follows:


Service = a software executable that can be executed from a shell command


It doesn’t matter how you write the service code and what programming language or software libraries you use. The only requirement is to build your service code as an executable that can start from a shell command (e.g. myservice.sh, python myservice.py, java -jar myservice.jar, myservice.exe etc.). Examples of services are: start or stop an instance or a service, send email, send SMS, finds a file with a specific text in it, clean up log directory, get a file from a remote storage, seek for particular log lines, execute a test tool, setup test environment, run a given fixture on a give test environment Etc. Services are similar to functions in monolithic applications. They may get some initial input, perform a particular task and return a processed output. Instead of being called directly as functions in code, they are invoked via the network using REST API calls.


Since services are software executables, they must run on some computing device. In terms of execution, a service can either run in Opereto cluster or on remote opereto agents depending on its type or scheme. We will discuss about service schemes later in this article. In our terminology, a service is considered as a flow if it invokes other services too. Invocation of services is done via REST call to opereto cluster specifying the service identifier, the input properties and the target agent to run the new service on. Thus, for a developer writing a flow service, calling another service running on the same agent the flow runs on (single tier flow) or running on another agent (multi tier flow) is basically the same, the only difference is the target agent specified. This mechanism simplifies the development of complex, distributed test or operation flows. 



The following chart demonstrates a multi-tier flow execution:




As the above chart illustrates, flows are controlled and coordinated by Opereto cluster. Flow-1 (a distributed test flow, for instance) uses Opereto REST API to run services and get their statuses and info. As demonstrated, it runs on a dedicated flow agent host while the flow child services run on Agent1 and Agent2 (test agents that are part of a test environment for instance). 


In general, it is a good practice to map test flow services to run on dedicated agent hosts so that a failure or crash on one of the software-under-test agents will not crash the test flow too. For example, let us assume that a flow test service runs on one of the SUT agents as well. Let us also assume that one of the test steps requires a reboot of that agent (a cluster failure recovery test, for instance). Restarting the agent host also terminates the flow execution on that host. It would be the same as someone cutting off the branch sittings on. Having the flow code running on a separate agent improves the execution reliability of that flow and make it independent of the actions it invokes. 


Service code structure

In the Developer Manual you will find tutorials about writing services and many code examples. At this point we will only mention that a service code is a directory containing the following:


  1. The service executable including any additional files/data required for its operation
  2. The service specification YAML (service.yaml)
  3. The service documentation (optional) (service.md)
  4. The service-agent mapping (optional) (service.sam.json)


Opereto defines services by a short YAML script. Following is an example of a service specification:       

   

cmd:
  type: shell
  command:
    default: bash run.sh
    windows: cmd /c run.bat
    linux: bash run.sh

item_properties:
- key: filename
  direction: input
  mandatory: true
  type: text
  value: 
  help: a file name to find
  example: myfile.txt

- key: occurrences
  type: json
  value: 
  direction: output
  example: [
'/tmp/myfile.txt',
'/myfile.txt'
  ]
  help: a list of occurrences of this file (full path)

timeout: 600
type: action

            


Let us review the specification and see what opereto learns about this service:

  1. How to run this service in general and on particular operating systems
  2. The service I/O properties: gets a file name string as an input and outputs a JSON list of all occurrences of this file on the host running it
  3. It is of scheme type action (we'll discuss service types at the end of this article)
  4. It will timeout after 10 minutes if not completed earlier than that 


This YAML scheme is a basic one but this information is enough for Opereto to execute it on any remote agent, present the relevant user input form in UI, know when to stop if it stops responding for any reason, etc. 
Let us review now each and every specification directive in details: 



item_properties


item_properties are the service interface. It is an optional list of input or output parameters of the form:       


item_properties:
- key: param1# mandatory
  value: my_value_1# mandatory
  type: text# mandatory
  editor: text# optional
  mandatory: true# optional
  direction: input# optional
  help: an example of a text input# optional
  example: my text input# optional
  store: # optional

                       



Element
Mandatory
Description
key
yes
the name of the input/output property
value
yes
the default value of the property. The default is null.
type
yes
valid types are: text, integer, json, boolean, list (comma delimited strings) and environment (a particular built-in property type to pass Opereto environments to services).
The default is text.
editor
no
specifies the web UI editor for this property. If not specified Opereto will select a default editor based on the type (e.g. a textfield for a text value, a checkbox for a boolean, etc.).
Editor types are:  
  • number
  • json
  • textarea
  • checkbox
  • selectbox
  • multiselectbox
  • itemselector
  • Text editors - any of the following:
text,password,textarea,yaml,xml,html,selectbox,checkbox,number,abap,abc,apache_conf,
applescript,asciidoc,assembly_x86,autohotkey,batchfile,bro,c9search,c_cpp,cirru,clojure,cobol,
coffee,coldfusion,csharp,csound_document,csound_orchestra,csound_score,csp,css,curly,dart,diff,
django,d,dockerfile,dot,drools,edifact,eiffel,ejs,elixir,elm,erlang,forth,fortran,ftl,
gcode,gherkin,gitignore,glsl,golang,gobstones,graphqlschema,groovy,haml,handlebars,haskell_cabal,
haskell,haxe,hjson,html_elixir,html_ruby,ini,io,jack,jade,java,javascript,jsoniq,json,
jsp,jssm,jsx,julia,kotlin,latex,less,liquid,liquid,lisp,livescript,logiql,lsl,lua,luapage,
lucene,makefile,markdown,mask,matlab,maze,mel,mixal,mushcode,mysql,nix,nsis,objectivec,
ocaml,pascal,perl,pgsql,php,pig,plain_text,powershell,praat,prolog,properties,protobuf,
python,razor,rdoc,red,redshift,rhtml,r,rst,ruby,rust,sass,scad,scala,scheme,scss,
sh,sjs,smarty,snippets,soy_template,space,sparql,sql,sqlserver,stylus,svg,swift,tcl,tex,
textile,toml,tsx,turtle,twig,typescript,vala,vbscript,velocity,verilog,vhdl,wollok,xquery
mandatory
no
specifying if this property must have a value specified. The default is false.
direction
yes
specifies if this property is an "input" or "output". Opereto passes only properties with 'input' direction specification to the executable service code upon initiation. The output of data will only be allowed for output properties. The default is input.
store
no
a map of keys and values to select from for a given property. Following are the main usages of store:

1. environment type (see Opereto Environments):    
item_properties:
- key: sut_environment
  type: environment
  direction: input
  value:
  store:
    environment_types: simple_test_topology, complex_test_topology
      2. multi selector editor       
item_properties:
-  key: clients
   editor: multiselector
   type: list
   mandatory: false
   store:
   client1: client1
        client2: client2
        client3: client3
         3. item selector editor     
item_properties:
-  key: clients
   editor: itemselector
   type: list
   mandatory: false
   store:
   client1: client1
        client2: client2
        client3: client3
        
example
no
If specified, Opereto adds it to the service info page.
help
no
A short text description of the property. Opereto adds this text to the documentation generated by Opereto for the given service.




cmd

Specifies the command line to run the service executable. Opereto supports two command types:


1. basic shell command:

cmd:
  type: shell
  command:
    default: bash run.sh        ## default command to run 
    windows: cmd /c run.bat     ## command to run on Windows agent hosts (optional)
    linux: bash run.sh          ## command to run on Linux agents hosts (optional)
 valid_exit_codes: [0,10,3]     ## a list of valid exit codes for the command. In case the command returns none of the defined valid_exit_codes, the command will fail (optional) 



2. Python virtual environment execution

cmd:
  type: python-venv
  command:
    default: python -u run.py      ## The python command (to run within a python virtual env)
  path:
    default: ~/myvirtualenv .      ## The path to the virtual env directory



In case that the service executable command requires input parameters, Opereto enables to map properties to command line parameters as demonstrated in the following example:    


cmd:
  type: shell
  command:
    default: ${default_command}
  valid_exit_codes: ${valid_exit_codes}
timeout: 600
type: action
item_properties:
-   key: default_command
    value: my command
    type: text
    direction: input
    mandatory: true
    help: Shell command with arguments
-   key: valid_exit_codes
    value:
    type: text
    direction: input
    mandatory: false
    help: Comma separated valid exit codes. e.g:1,5,8
-   key: exit_code
    value: null
    type: text
    direction: output
    mandatory: false
    help: Exit code of the executed command

   

  

Anyway, item properties are also passed to the service executable in several ways and may be processed by the service code. For more details about service input mechanism see the Handling Service Input / Output article.



bell-2x.png In most languages, when running code from command line, all STDOUT, and STDERR content will be redirected to shell immediately without buffering. However, in some cases, a particular flag or config parameter should be added to the command. In Python, for example, adding the flag “-u” to the command instruct Python interpreter to redirect all console output to the shell without buffering it: python -u myapp.py.




timeout

The default service execution timeout (in seconds). The user can override this timeout upon service invocation. Minimal allowed timeout is 60 seconds.



type

Service types (or schemes) are classifiers that direct Opereto how to operate a given service and what constraints to put on the service specification YAML. 

Please refer to the Service Scheme section to learn more about the supported schemes and use cases.



Run a service

Executing a service (or creating a new process instance of a given service) can be done from the Dashboard in the user interface or via the REST API. The user initiating (or registering) the process inserts values to required input parameters, a title )optional) to the new process and a list of agents (if the service requires it) to run the new process on.


Agent-based registered processes are in pending mode until the relevant Agent collects them. It usually takes up to 2 seconds, but the user may reconfigure it per agent. To reduce redundant network traffic, the Agent use a caching mechanism for services. Unless a change had been made in a given service, the Agent would use its local copy of that service code. The Agent runs the service executable and sends all logs, property updates, and execution statuses back to the Opereto cluster.



Running via UI



Running via Opereto CLI

opereto services run shell_command --agent=MY_AGENT_NAME --title="running my process" -params='{"command": "ls -la"}'  

## to run the command in async mode, add --async at the end of the command



Running via REST

See Opereto REST API - Create a new process



Running via pyopereto (Opereto Official Python client)

from pyopereto.client import OperetoClient

client = OperetoClient()

def run_process():
    pid = client.create_process(service='shell_command', agent='MY_AGENT_NAME',title='running my process'  \
  ,command='ls -la'  \
  ,valid_exit_codes=0  \
    )
    if client.success(pid):
        return client.SUCCESS
    else:
        return client.FAILURE

if __name__ == '__main__':
    run_process()




Schedule service executions

Opereto provides a built-in scheduler for services. The scheduler supports a cron style time specification. There may be one of more schedulers per service. For each scheduler, the service mandatory input properties, if any, must be configured with a default value. Otherwise, that scheduled task will fail.



Service documentation

A developer of a service may add markdown formatted service documentation located in the services code directory in a file named service.md. For example:

This service executes a given shell command with the provided arguments

#### Service success criteria
Success if command to execute exists, is accessible and its execution was successful.
If valid_exit_codes property is provided, then the process will be successful only if the return code is one of the valid_exit_codes.

#### Assumptions/Limitations

#### Dependencies
No dependencies.


Opereto automatically generate a documentation page per service based on the service.md, service.yaml and service.sam.json files (see bellow).



Modify services

Opereto is an operations framework, not a source control. For this reason, modifying services is the same as modifying any other software code. The service developer must commit all changes to the service repository and than deploy it to Opereto via Opereto CLI, REST API or UI. 


Deleting a service in Opereto only deletes the local copy of this service and all schedulers related to this service. It will remove nothing from the source repository.



Service audit log

Opereto provides a deployment history log per service as shown bellow.




  

To learn more how to manage an maintain services programmatically please refer to: