pre-release

master
Chris Punches 2021-04-04 21:21:05 -04:00
parent 4b3a9170bf
commit 89f734de84
34 changed files with 553 additions and 348 deletions

View File

@ -1,7 +1,7 @@
cmake_minimum_required(VERSION 3.5) cmake_minimum_required(VERSION 3.5)
project(examplar) project(rex)
set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -std=c++1z -O0 -DDEBUG=1") set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -std=c++1z -O0 -DDEBUG=1")
set(SOURCE_FILES examplar.cpp src/loaders/abstract/loaders.cpp src/loaders/abstract/loaders.h src/json/jsoncpp.cpp src/loaders/low_level/JSON_Loader.cpp src/loaders/low_level/JSON_Loader.h src/loaders/misc/helpers.cpp src/loaders/misc/helpers.h src/loaders/abstract/Suite.cpp src/loaders/abstract/Suite.h src/loaders/abstract/Plan.cpp src/loaders/abstract/Plan.h src/loaders/abstract/Conf.cpp src/loaders/abstract/Conf.h src/loaders/abstract/Unit.cpp src/loaders/abstract/Unit.h src/loaders/abstract/Task.cpp src/loaders/abstract/Task.h src/Sproc/Sproc.cpp src/Sproc/Sproc.h src/Logger/Logger.cpp src/Logger/Logger.h) set(SOURCE_FILES Rex.cpp src/loaders/abstract/loaders.cpp src/loaders/abstract/loaders.h src/json/jsoncpp.cpp src/loaders/low_level/JSON_Loader.cpp src/loaders/low_level/JSON_Loader.h src/loaders/misc/helpers.cpp src/loaders/misc/helpers.h src/loaders/abstract/Suite.cpp src/loaders/abstract/Suite.h src/loaders/abstract/Plan.cpp src/loaders/abstract/Plan.h src/loaders/abstract/Conf.cpp src/loaders/abstract/Conf.h src/loaders/abstract/Unit.cpp src/loaders/abstract/Unit.h src/loaders/abstract/Task.cpp src/loaders/abstract/Task.h src/Sproc/Sproc.cpp src/Sproc/Sproc.h src/Logger/Logger.cpp src/Logger/Logger.h)
add_executable(examplar ${SOURCE_FILES}) add_executable(rex ${SOURCE_FILES})

View File

@ -1,19 +1,19 @@
# Examplar # Rex
## What is Examplar? ## What is Rex?
Examplar is an execution flow component designed for the generation of SURRO Linux but kept broad in design for other use cases. Rex is an execution flow component designed for the generation of SURRO Linux but kept broad in design for other use cases.
At a high level, it is a very simple thing: It executes scripts and other executables in a predetermined order, logs their output, and has basic error handling using exit codes of the executables it is running. At a high level, it is a very simple thing: It executes scripts and other executables in a predetermined order, logs their output, and has basic error handling using exit codes of the executables it is running.
It relies on a library of Units which are files that define, in json format, what executables it can execute. It uses a Plan to define which of those units it will actually execute. This allows you to have many things defined by multiple teams, and, with sufficient abstraction, use the same library of automations for multiple purposes. It relies on a library of Units which are files that define, in json format, what executables it can execute. It uses a Plan to define which of those units it will actually execute. This allows you to have many things defined by multiple teams, and, with sufficient abstraction, use the same library of automations for multiple purposes.
# Instructions # Instructions
These are instructions for using Examplar. These are instructions for using Rex.
## Build ## Build
Compiling Examplar is easy. There are zero external dependencies. Build does require *cmake*. Compiling Rex is easy. There are zero external dependencies. Build does require *cmake*.
~~~~ ~~~~
$ cmake . $ cmake .
@ -31,39 +31,39 @@ Then place the binary where you'd like. I'd recommend packaging it for your fav
5. Turn on the rectify pattern in the unit definition. 5. Turn on the rectify pattern in the unit definition.
## Definitions ## Definitions
So you've got Examplar compiled and you're ready to start automating the world. So you've got Rex compiled and you're ready to start automating the world.
If you're thinking "how do I configure this thing", this article is for you. If you're thinking "how do I configure this thing", this article is for you.
### Units ### Units
A Unit is an automation definition, written in JSON in a UNIT FILE. Deeper into Examplars internals, Units and Tasks have slightly different functions, but for the purposes of users, the terms can be used interchangeably. A Task is a task to be performed in a Plan, and a Unit is its definition. A Unit is a JSON object that has: A Unit is an automation definition, written in JSON in a UNIT FILE. Deeper into Rexs internals, Units and Tasks have slightly different functions, but for the purposes of users, the terms can be used interchangeably. A Task is a task to be performed in a Plan, and a Unit is its definition. A Unit is a JSON object that has:
* A `name`, which is an identifier for the Unit used by people. * A `name`, which is an identifier for the Unit used by people.
* A `target`, which is the path to the automation script performing the work. This provides a clean linear path for huge chains of scripts to be executed in order and tracked on return for additional logic in chaining. * A `target`, which is the path to the automation script performing the work. This provides a clean linear path for huge chains of scripts to be executed in order and tracked on return for additional logic in chaining.
* A `rectifier`, which is the path to the automation script to be executed if the target call fails. * A `rectifier`, which is the path to the automation script to be executed if the target call fails.
* A `rectify` attribute, which tells Examplar whether or not to execute the rectifier in the case of failure when executing the target. * A `rectify` attribute, which tells Rex whether or not to execute the rectifier in the case of failure when executing the target.
* An `active` attribute,which tells Examplar whether or not the Unit can be used in a Plan. This gives Unit developers a way to tell Plan developers not to use the Unit. * An `active` attribute,which tells Rex whether or not the Unit can be used in a Plan. This gives Unit developers a way to tell Plan developers not to use the Unit.
* A `required` attribute which tells Examplar whether or not the Plan can continue if the Unit fails. If the rectify attribute is set to true, this attribute is checked after a rectifier failure. If not, this is checked after target failure. In either case, if the rectifier or target do not return successfully, Examplar will halt the execution of the Plan if this is turned on for the unit being executed. Otherwise it simply moves to the next Unit being executed. * A `required` attribute which tells Rex whether or not the Plan can continue if the Unit fails. If the rectify attribute is set to true, this attribute is checked after a rectifier failure. If not, this is checked after target failure. In either case, if the rectifier or target do not return successfully, Rex will halt the execution of the Plan if this is turned on for the unit being executed. Otherwise it simply moves to the next Unit being executed.
### Tasks ### Tasks
A Task is an action item in a Plan, just like in real life. In the context of Examplar, a Task is a Unit that has been loaded and incorporated into a Plan in an actionable state. Inactive Units can not be loaded into a Plan and thus can never be a Task. The primary difference between a Task and a Unit is that a Unit is not actionable — its just a definition — while a Task a consumable, actionable automation. A Task is an action item in a Plan, just like in real life. In the context of Rex, a Task is a Unit that has been loaded and incorporated into a Plan in an actionable state. Inactive Units can not be loaded into a Plan and thus can never be a Task. The primary difference between a Task and a Unit is that a Unit is not actionable — its just a definition — while a Task a consumable, actionable automation.
Suite Suite
A Suite is not visible to the user and this is only for informational purposes. A Suite is a collection of all available Unit definitions loaded from one or more UNIT FILES. Just as a Unit is the definition for a Task, a Suite is a collection of Units that define the Task components of a Plan. A Suite is not visible to the user and this is only for informational purposes. A Suite is a collection of all available Unit definitions loaded from one or more UNIT FILES. Just as a Unit is the definition for a Task, a Suite is a collection of Units that define the Task components of a Plan.
A Suite is consumed by a Plan during the conversion of Units to Tasks, though this is not visible to the user — it just simply helps to understand the kind of abstraction taking place in the conceptual model of Examplar. A Suite is consumed by a Plan during the conversion of Units to Tasks, though this is not visible to the user — it just simply helps to understand the kind of abstraction taking place in the conceptual model of Rex.
Plan Plan
A Plan is the glue of all the components of Examplar and is deceptively simple. A Plan loads a Suite for its Task definitions (Units), but the Tasks to actually execute are specified in the PLAN FILE. The Tasks are executed in the order specified in the PLAN FILE. A Plan is the glue of all the components of Rex and is deceptively simple. A Plan loads a Suite for its Task definitions (Units), but the Tasks to actually execute are specified in the PLAN FILE. The Tasks are executed in the order specified in the PLAN FILE.
### FILES ### FILES
There are several files used by Examplar. There are several files used by Rex.
#### CONFIG FILE and Attributes #### CONFIG FILE and Attributes
This is the one config file that Examplar uses. The default path it looks is /etc/Examplar/config.json. This is the one config file that Rex uses. The default path it looks is /etc/Rex/config.json.
A config file at the time of writing this specifies a single JSON object with 5 attributes: A config file at the time of writing this specifies a single JSON object with 5 attributes:
@ -75,7 +75,7 @@ A config file at the time of writing this specifies a single JSON object with 5
#### Configuration VERSION #### Configuration VERSION
The configuration version is checked to ensure that the configuration is consumable by that version of Examplar. This will pave the way for reverse compatibility if the project moves in that direction. The configuration version is checked to ensure that the configuration is consumable by that version of Rex. This will pave the way for reverse compatibility if the project moves in that direction.
#### UNIT FILE #### UNIT FILE
@ -123,10 +123,10 @@ Next, add the unit to the plan by name.
### 5. Set up your config file. ### 5. Set up your config file.
Point your config file at your plan file and your units directory. Point your config file at your plan file and your units directory.
### 6. Run Examplar pointing at that config file. ### 6. Run Rex pointing at that config file.
Execute examplar: Execute rex:
examplar --verbose --config path/to/your/config/file.json rex --verbose --config path/to/your/config/file.json
And you should see your 'hello world' script. And you should see your 'hello world' script.

227
Rex.cpp Normal file
View File

@ -0,0 +1,227 @@
/*
Rex - A unit-based automation, workflow, and testing system.
© SILO GROUP and Chris Punches, 2017.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include <iostream>
#include <unistd.h>
#include <getopt.h>
#include "src/loaders/abstract/loaders.h"
#include "src/Logger/Logger.h"
void version_info()
{
std::cout << "pre-release alpha" << std::endl;
}
void print_usage()
{
// commandline switches:
// -h help OPTIONAL
// -v verbose OPTIONAL
// -c CONFIG_FILE REQUIRED
// -p PLAN_FILE REQUIRED
fprintf( stderr, "\nUsage:\n\trex [ -h | --help ] [ -v | --verbose ] ( ( ( -c | --config ) CONFIG_PATH ) ( -p | plan ) PLAN_PATH ) )\n" );
fprintf( stderr, "\nOptional Arguments:\n");
fprintf( stderr, "\t-h | --help\n\t\tThis usage screen. Mutually exclusive to all other options.\n");
fprintf( stderr, "\t-v | --verbose\n\t\tSets verbose output. Generally more than you want to see.\n");
fprintf( stderr, "\nRequired Arguments:\n");
fprintf( stderr, "\t-c | --config\n\t\tSupply the directory path for the configuration file.\n");
fprintf( stderr, "\t-p | --plan\n\t\tSupply the directory path for the plan file to execute.\n\n");
}
int main( int argc, char * argv[] )
{
// default verbosity setting
int verbose_flag = false;
// whether to show usage screen
int help_flag = false;
// did the user supply an argument to config
int config_flag = false;
// did the user supply an argument to plan
int plan_flag = false;
// did the user ask for the version info
int version_flag = false;
// default config path
std::string config_path;
// default plan path
std::string plan_path;
// initialise for commandline argument processing
int c;
int digit_optind = 0;
if ( argc <= 1 )
{
help_flag = true;
}
// process commandline arguments
while ( 1 )
{
int this_option_optind = optind ? optind : 1;
int option_index = 0;
// commandline argument structure
static struct option long_options[] = {
{"verbose_flag", no_argument, 0, 'v' },
{"version_info", no_argument, 0, 'i' },
{"help", no_argument, 0, 'h' },
{"config", required_argument, 0, 'c' },
{"plan", required_argument, 0, 'p' },
{0,0,0,0}
};
c = getopt_long(argc, argv, "vihc:p:", long_options, &option_index );
if ( c == -1 )
{
break;
}
switch ( c )
{
case 'i':
version_flag = true;
case 'h':
help_flag = true;
break;
case 'v':
verbose_flag = true;
break;
case 'c':
config_flag = true;
config_path = std::string( optarg );
break;
case 'p':
plan_flag = true;
plan_path = std::string( optarg );
break;
case '?':
help_flag = true;
break;
default:
break;
} // end switch
} // end opts while
if ( version_flag ) {
version_info();
exit(0);
}
// if the user supplied no config file, there's nothing to do but teach the user how to use this tool
if (! config_flag ) {
std::cerr << "NOT SUPPLIED: CONFIG_PATH" << std::endl;
help_flag = true;
}
// if the user supplied no plan file, there's nothing to do but teach the user how to use this tool
if (! plan_flag ) {
std::cerr << "NOT SUPPLIED: PLAN_PATH" << std::endl;
help_flag = true;
}
// if the user wants the help screen, just show it and leave
if ( (help_flag) | (! config_flag) | (! plan_flag) )
{
print_usage();
exit( 0 );
}
// default logging level
int L_LEVEL = E_INFO;
// if set to verbose_flag mode, output with DEBUG level verbosity
if ( verbose_flag )
{
std::cout << "Setting verbosity level to 'DBUG'..." << std::endl;
L_LEVEL = E_DEBUG;
}
// the main scope logger
Logger slog = Logger( L_LEVEL, "_main_" );
slog.log( E_INFO, "* Initialising Logging...");
// configuration object that reads from config_path
Conf configuration = Conf( config_path, L_LEVEL );
// check if context override is set in the config file
if ( configuration.has_context_override() )
{
// if so, set the CWD.
chdir( configuration.get_execution_context().c_str() );
slog.log( E_DEBUG, "* Setting execution context: " + get_working_path() );
}
// The Rex Paradigm:
// - A Suite is made up of Units.
// - A Unit is a definition of an executable along with various options surrounding its context and behaviour.
// - A Plan is made up of Tasks.
// - A Unit becomes a Task when it is added to a Plan.
// A Plan contains what units are executed and a Suite contains the definitions of those units.
std::string plan_file = plan_path;
// load the filepaths to definitions of a plan and definitions of units.
std::string definitions_file = configuration.get_units_path();
// initialise an empty suite (unit definitions library)
slog.log( E_DEBUG, "* Initialising Suite...");
Suite available_definitions = Suite( L_LEVEL );
// load units into suite
slog.log( E_INFO, "* Loading all actionable Units into Suite..." );
available_definitions.load_units_file( definitions_file );
// initialise an empty plan
slog.log( E_DEBUG, "* Initialising Plan..." );
Plan plan = Plan( &configuration, L_LEVEL );
// load the plan the user supplied
slog.log( E_INFO, "* Loading Plan...");
plan.load_plan_file( plan_file );
// ingest the suitable Tasks from the Suite into the Plan
slog.log( E_INFO, "* Loading planned Tasks from Suite to Plan." );
plan.load_definitions( available_definitions );
slog.log( E_INFO, "* Ready to execute all actionable Tasks in Plan." );
try
{
plan.execute();
}
catch ( std::exception& e)
{
slog.log( E_FATAL, "Caught exception.");
slog.log( E_FATAL, e.what() );
return 1;
}
return 0;
}

View File

@ -1,6 +1,6 @@
# Logic Tree for Examplar Task Execution # Logic Tree for Rex Task Execution
Examplar - An automation and testing framework. Rex - An automation and testing framework.
© SURRO INDUSTRIES and Chris Punches, 2017. © SURRO INDUSTRIES and Chris Punches, 2017.

View File

@ -1,170 +0,0 @@
/*
Examplar - An automation and testing framework.
© SURRO INDUSTRIES and Chris Punches, 2017.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include <iostream>
#include <unistd.h>
#include <getopt.h>
#include "src/json/json.h"
#include "src/loaders/abstract/loaders.h"
#include "src/Logger/Logger.h"
#include "src/loaders/misc/helpers.h"
void print_usage()
{
printf("examplar [ -h | --help ] [ -v | --verbose ] [ -e | --execution-context EXECUTION_CONTEXT ][ -c | --config CONFIG_PATH ]\n\n");
}
int main( int argc, char * argv[] )
{
int opt;
bool verbose = false;
bool show_help = false;
// indicator of whether examplar should use a commandline argument for overriding the context
// instead of what's supplied in the test file
bool cli_context_supplied = false;
std::string config_path = "/etc/Examplar/config.json";
std::string execution_context;
// commandline switches:
// -h help
// -v verbose
// -c CONFIG_FILE_PATH -- defaults to '/etc/Examplar/config.json'
// -e EXECUTION_CONTEXT -- current working directory when executing unit targets
while (1)
{
static struct option long_options[] =
{
{"verbose", no_argument, 0, 'v'},
{"help", no_argument, 0, 'h'},
{"config", required_argument, 0, 'c'},
{"execution-context", required_argument, 0, 'e'},
{0, 0}
};
int option_index = 0;
opt = getopt_long( argc, argv, "vhec:", long_options, &option_index );
if ( opt == -1 )
break;
switch ( opt )
{
case 0:
if ( long_options[option_index].flag !=0 )
break;
case 'h':
show_help = true;
case 'v':
verbose = true;
break;
case 'c':
config_path = std::string( optarg );
break;
case '?':
print_usage();
exit( 1 );
case 'e':
cli_context_supplied = true;
execution_context = std::string( optarg );
break;
default:
break;
}
}
if ( show_help )
{
print_usage();
exit( 0 );
}
int L_LEVEL = E_INFO;
if ( verbose )
{
L_LEVEL = E_DEBUG;
std::cout << "Verbosity is DBUG." << std::endl;
} else {
L_LEVEL = E_INFO;
std::cout << "Verbosity is INFO." << std::endl;
}
Logger slog = Logger( L_LEVEL, "_main_" );
// A Plan is made up of Tasks, and a Suite is made up of Units.
// A Plan declares what units are executed and a Suite declares the definitions of those units.
Conf configuration = Conf(config_path, L_LEVEL );
// check if context override
if ( configuration.has_context_override() )
{
// if so, set the CWD.
chdir( configuration.get_execution_context().c_str() );
slog.log( E_DEBUG, "Set execution context: " + get_working_path() );
}
// if the user set this option as a commandline argument
if ( cli_context_supplied )
{
// override the test file's specified execution context
configuration.set_execution_context( execution_context );
slog.log( E_DEBUG, "Set execution context from commandline: " + execution_context );
}
// load the filepaths to definitions of a plan and definitions of units.
std::string definitions_file = configuration.get_units_path();
std::string plan_file = configuration.get_plan_path();
slog.log( E_DEBUG, "* Initialising suite (definition library).");
Suite available_definitions = Suite( L_LEVEL );
slog.log( E_INFO, "* Loading all actionable units into suite." );
available_definitions.load_units_file( definitions_file );
slog.log( E_DEBUG, "* Initialising plan." );
Plan plan = Plan( &configuration, L_LEVEL );
slog.log( E_INFO, "* Loading plan outline.");
plan.load_plan_file( plan_file );
slog.log( E_INFO, "* Loading planned tasks from suite to plan." );
plan.load_definitions( available_definitions );
slog.log( E_INFO, "* Ready to execute all actionable tasks in plan." );
try
{
plan.execute();
}
catch ( std::exception& e)
{
slog.log( E_FATAL, "Caught exception.");
slog.log( E_FATAL, e.what() );
return 1;
}
return 0;
}

View File

@ -10,7 +10,7 @@ Logger::Logger( int LOG_LEVEL, std::string mask )
this->mask = mask; this->mask = mask;
setlogmask( LOG_UPTO( this->LOG_LEVEL ) ); setlogmask( LOG_UPTO( this->LOG_LEVEL ) );
openlog( this->mask.c_str(), LOG_CONS | LOG_PID | LOG_NDELAY, LOG_PERROR | LOG_LOCAL1 ); openlog( "rex", LOG_CONS | LOG_PID | LOG_NDELAY, LOG_PERROR | LOG_LOCAL1 );
} }
@ -33,19 +33,10 @@ void Logger::log( int LOG_LEVEL, std::string msg )
if ( LOG_LEVEL == E_FATAL | LOG_LEVEL == E_WARN ) if ( LOG_LEVEL == E_FATAL | LOG_LEVEL == E_WARN )
{ {
std::cerr << "[" << this->get_8601() << "] [" << ERR << "] " << "[" << this->mask << "] " << msg.c_str() << std::endl; std::cerr << "[" << get_8601() << "] [" << ERR << "] " << "[" << this->mask << "] " << msg.c_str() << std::endl;
} else { } else {
std::cout << "[" << this->get_8601() << "] [" << ERR << "] " << "[" << this->mask << "] " << msg.c_str() << std::endl; std::cout << "[" << get_8601() << "] [" << ERR << "] " << "[" << this->mask << "] " << msg.c_str() << std::endl;
} }
} }
} }
std::string Logger::get_8601()
{
auto now = std::chrono::system_clock::now();
auto itt = std::chrono::system_clock::to_time_t(now);
std::ostringstream ss;
// ss << std::put_time(gmtime(&itt), "%FT%TZ");
ss << std::put_time(localtime(&itt), "%Y-%m-%d_%H:%M:%S");
return ss.str();
}

View File

@ -2,15 +2,15 @@
// Created by bagira on 6/13/20. // Created by bagira on 6/13/20.
// //
#ifndef EXAMPLAR_LOGGER_H #ifndef REX_LOGGER_H
#define EXAMPLAR_LOGGER_H #define REX_LOGGER_H
#include <syslog.h> #include <syslog.h>
#include <string> #include <string>
#include <iostream> #include <iostream>
#include <chrono>
#include <iomanip> #include <iomanip>
#include <sstream> #include <sstream>
#include "../loaders/misc/helpers.h"
enum L_LVL { enum L_LVL {
E_FATAL, E_FATAL,
@ -27,9 +27,8 @@ public:
private: private:
int LOG_LEVEL; int LOG_LEVEL;
std::string mask; std::string mask;
std::string get_8601();
}; };
#endif //EXAMPLAR_LOGGER_H #endif //REX_LOGGER_H

View File

@ -1,4 +1,6 @@
#include "Sproc.h" #include "Sproc.h"
#include "../loaders/misc/helpers.h"
#include "sys/stat.h"
#define PARENT default #define PARENT default
@ -141,7 +143,7 @@ int set_identity_context( std::string task_name, std::string user_name, std::str
/// ///
/// \param input - The commandline input to execute. /// \param input - The commandline input to execute.
/// \return - The return code of the execution of input in the calling shell. /// \return - The return code of the execution of input in the calling shell.
int Sproc::execute(std::string shell, std::string environment_file, std::string user_name, std::string group_name, std::string command, int LOG_LEVEL, std::string task_name ) int Sproc::execute(std::string shell, std::string environment_file, std::string user_name, std::string group_name, std::string command, int LOG_LEVEL, std::string task_name, bool log_to_file, std::string logs_dir )
{ {
// the logger // the logger
Logger slog = Logger( LOG_LEVEL, "_sproc" ); Logger slog = Logger( LOG_LEVEL, "_sproc" );
@ -164,10 +166,25 @@ int Sproc::execute(std::string shell, std::string environment_file, std::string
// potentially corrupting user interaction with TUIs in the processes. This should give us our log and our output // potentially corrupting user interaction with TUIs in the processes. This should give us our log and our output
// in as hands off a way as possible with as few assumptions as possible, while still doing this in a somewhat C++-y // in as hands off a way as possible with as few assumptions as possible, while still doing this in a somewhat C++-y
// way. // way.
if (! is_dir( logs_dir ) ) {
int check = mkdir( logs_dir.c_str(), 0777 );
if (! check ) {
slog.log( E_FATAL, "Sprocket couldn't create the logs parent directory." );
}
}
std::string timestamp = get_8601();
std::string contained_dir = logs_dir + "/" + task_name;
if (! is_dir( contained_dir ) ) {
int check = mkdir( contained_dir.c_str(), 0777 );
if (! check ) {
slog.log( E_FATAL, "Sprocket couldn't create the instance log directory.");
}
}
// set up the "Tee" with the parent // set up the "Tee" with the parent
std::string child_stdout_log_path = "./stdout.log"; std::string child_stdout_log_path = contained_dir + "/" + timestamp + ".stdout.log";
std::string child_stderr_log_path = "./stderr.log"; std::string child_stderr_log_path = contained_dir + "/" + timestamp + ".stderr.log";
std::ofstream stdout_log; std::ofstream stdout_log;
std::ofstream stderr_log; std::ofstream stderr_log;
@ -316,9 +333,13 @@ int Sproc::execute(std::string shell, std::string environment_file, std::string
set_stdout_break = true; set_stdout_break = true;
break; break;
default: default:
tee_out.write( stdout_buf, stdout_count ); if ( log_to_file ) {
tee_out.flush(); tee_out.write( stdout_buf, stdout_count );
tee_out.flush();
} else {
std::cout.write( stdout_buf, stdout_count );
std::cout.flush();
}
// clear the buffer to prevent artifacts from previous loop // clear the buffer to prevent artifacts from previous loop
memset( &stdout_buf[0], 0, sizeof( stdout_buf ) -1 ); memset( &stdout_buf[0], 0, sizeof( stdout_buf ) -1 );
} }

View File

@ -17,8 +17,8 @@
along with this program. If not, see <https://www.gnu.org/licenses/>. along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
#ifndef FTESTS_SPROC_H #ifndef REX_SPROCKET_H
#define FTESTS_SPROC_H #define REX_SPROCKET_H
#include "../Logger/Logger.h" #include "../Logger/Logger.h"
#include <iostream> #include <iostream>
@ -61,7 +61,9 @@ class Sproc {
std::string group_name, std::string group_name,
std::string command, std::string command,
int LOG_LEVEL, int LOG_LEVEL,
std::string task_name std::string task_name,
bool log_to_file,
std::string logs_dir
); );
}; };
@ -88,4 +90,4 @@ class teestream : public std::ostream
teebuf tbuf; teebuf tbuf;
}; };
#endif //FTESTS_SPROC_H #endif //REX_SPROCKET_H

View File

@ -1,5 +1,5 @@
/* /*
Examplar - An automation and testing framework. rex - An automation and testing framework.
© SURRO INDUSTRIES and Chris Punches, 2017. © SURRO INDUSTRIES and Chris Punches, 2017.
@ -66,7 +66,7 @@ protected:
/// TODO Expand to detect when a directory path is supplied for units_path or plan_path and import all Tasks and Units. /// TODO Expand to detect when a directory path is supplied for units_path or plan_path and import all Tasks and Units.
/// ///
/// \param filename - The filename to load the configuration from. /// \param filename - The filename to load the configuration from.
Conf::Conf( std::string filename, int LOG_LEVEL ): JSON_Loader( LOG_LEVEL ), slog( LOG_LEVEL, "e_conf" ) Conf::Conf(std::string filename, int LOG_LEVEL ): JSON_Loader(LOG_LEVEL ), slog(LOG_LEVEL, "_conf_" )
{ {
this->LOG_LEVEL = LOG_LEVEL; this->LOG_LEVEL = LOG_LEVEL;
@ -91,18 +91,18 @@ Conf::Conf( std::string filename, int LOG_LEVEL ): JSON_Loader( LOG_LEVEL ), slo
throw ConfigLoadException("config_version string expected was " + std::string(VERSION_STRING) + " in: " + filename); throw ConfigLoadException("config_version string expected was " + std::string(VERSION_STRING) + " in: " + filename);
} }
// find the path to the plan file
if ( this->get_serialized(this->plan_path, "plan_path" ) != 0 )
{
throw ConfigLoadException("plan_path string is not set in the config file supplied:" + filename);
}
// find the path to the unit definitions file // find the path to the unit definitions file
if (this->get_serialized(this->units_path, "units_path" ) != 0 ) if (this->get_serialized(this->units_path, "units_path" ) != 0 )
{ {
throw ConfigLoadException("units_path string is not set in the config file supplied: " + filename); throw ConfigLoadException("units_path string is not set in the config file supplied: " + filename);
} }
// find the path to logs directory
if (this->get_serialized(this->logs_path, "logs_path" ) != 0 )
{
throw ConfigLoadException("logs_path string is not set in the config file supplied: " + filename);
}
if ( this->get_serialized(this->override_execution_context, "execution_context_override" ) != 0 ) if ( this->get_serialized(this->override_execution_context, "execution_context_override" ) != 0 )
{ {
throw ConfigLoadException("execution_context_override boolean is not set in the config file supplied: " + filename); throw ConfigLoadException("execution_context_override boolean is not set in the config file supplied: " + filename);
@ -114,11 +114,15 @@ Conf::Conf( std::string filename, int LOG_LEVEL ): JSON_Loader( LOG_LEVEL ), slo
{ {
throw ConfigLoadException("execution_context string is not set in the config file supplied: " + filename); throw ConfigLoadException("execution_context string is not set in the config file supplied: " + filename);
} else { } else {
this->execution_context_literal = this->execution_context.asString(); if ( is_dir( this->execution_context.asString() ) ) {
this->execution_context_literal = this->execution_context.asString();
} else {
throw ConfigLoadException( "The execution context supplied is an invalid directory.");
}
} }
}; };
/// Conf::has_context_override - Specifies whether or not the override context function is enabled in the conf file. /// Conf::has_context_override - Specifies whether or not the override context function is enabled in the Conf file.
bool Conf::has_context_override() { bool Conf::has_context_override() {
return this->override_execution_context.asBool(); return this->override_execution_context.asBool();
} }
@ -128,14 +132,14 @@ std::string Conf::get_execution_context() {
return this->execution_context_literal; return this->execution_context_literal;
} }
/// Conf::get_plan_path - Retrieves the path to the Plan definition file from the application configuration file.
std::string Conf::get_plan_path() { return this->plan_path.asString(); }
/// Conf::get_units_path - Retrieves the path to the Unit definition file from the application configuration file. /// Conf::get_units_path - Retrieves the path to the Unit definition file from the application configuration file.
std::string Conf::get_units_path() { return this->units_path.asString(); } std::string Conf::get_units_path() { return this->units_path.asString(); }
/// Conf::get_units_path - Retrieves the path to the Unit definition file from the application configuration file.
std::string Conf::get_logs_path() { return this->logs_path.asString(); }
/// Conf::set_execution_context- Sets the execution context. /// Conf::set_execution_context- Sets the execution context.
void Conf::set_execution_context( std::string execution_context ) void Conf::set_execution_context(std::string execution_context )
{ {
this->execution_context_literal = execution_context; this->execution_context_literal = execution_context;
} }

View File

@ -1,5 +1,5 @@
/* /*
Examplar - An automation and testing framework. rex - An automation and testing framework.
© SURRO INDUSTRIES and Chris Punches, 2017. © SURRO INDUSTRIES and Chris Punches, 2017.
@ -18,8 +18,8 @@
*/ */
#ifndef FTESTS_CONF_H #ifndef REX_CONF_H
#define FTESTS_CONF_H #define REX_CONF_H
#include "../low_level/JSON_Loader.h" #include "../low_level/JSON_Loader.h"
#include <exception> #include <exception>
#include "../../Logger/Logger.h" #include "../../Logger/Logger.h"
@ -27,7 +27,7 @@
#define STRINGIZE2(s) #s #define STRINGIZE2(s) #s
#define STRINGIZE(s) STRINGIZE2(s) #define STRINGIZE(s) STRINGIZE2(s)
# define IMPL_CONFIG_VERSION 3 # define IMPL_CONFIG_VERSION 4
# define VERSION_STRING STRINGIZE(IMPL_CONFIG_VERSION) # define VERSION_STRING STRINGIZE(IMPL_CONFIG_VERSION)
class Conf: public JSON_Loader class Conf: public JSON_Loader
@ -37,23 +37,24 @@ private:
Json::Value units_path; Json::Value units_path;
Json::Value execution_context; Json::Value execution_context;
Json::Value config_version; Json::Value config_version;
Json::Value logs_path;
// flag to indicate if execution context should be overriden in config file // flag to indicate if execution context should be overriden in config file
// if set to true Examplar should use whats in the config file for current working directory // if set to true rex should use whats in the config file for current working directory
// if set to false, Examplar should use the current working directory at time of execution // if set to false, rex should use the current working directory at time of execution
Json::Value override_execution_context; Json::Value override_execution_context;
bool override_context; bool override_context;
std::string execution_context_literal; std::string execution_context_literal;
public: public:
Conf( std::string filename, int LOG_LEVEL ); Conf(std::string filename, int LOG_LEVEL );
bool has_context_override(); bool has_context_override();
std::string get_plan_path();
std::string get_units_path(); std::string get_units_path();
std::string get_execution_context(); std::string get_execution_context();
std::string get_logs_path();
void set_execution_context( std::string ); void set_execution_context( std::string );
@ -63,4 +64,4 @@ private:
}; };
#endif //FTESTS_CONF_H #endif //REX_CONF_H

View File

@ -1,5 +1,5 @@
/* /*
Examplar - An automation and testing framework. rex - An automation and testing framework.
© SURRO INDUSTRIES and Chris Punches, 2017. © SURRO INDUSTRIES and Chris Punches, 2017.
@ -120,7 +120,7 @@ protected:
/// Plan::Plan() - Constructor for Plan class. A Plan is a managed container for a Task vector. These tasks reference /// Plan::Plan() - Constructor for Plan class. A Plan is a managed container for a Task vector. These tasks reference
/// Units that are defined in the Units files (Suite). If Units are definitions, Tasks are selections of those /// Units that are defined in the Units files (Suite). If Units are definitions, Tasks are selections of those
/// definitions to execute, and if Units together form a Suite, Tasks together form a Plan. /// definitions to execute, and if Units together form a Suite, Tasks together form a Plan.
Plan::Plan( Conf * configuration, int LOG_LEVEL ): JSON_Loader( LOG_LEVEL ), slog( LOG_LEVEL, "e_plan" ) Plan::Plan(Conf * configuration, int LOG_LEVEL ): JSON_Loader(LOG_LEVEL ), slog(LOG_LEVEL, "_plan_" )
{ {
this->configuration = configuration; this->configuration = configuration;
this->LOG_LEVEL = LOG_LEVEL; this->LOG_LEVEL = LOG_LEVEL;

View File

@ -1,5 +1,5 @@
/* /*
Examplar - An automation and testing framework. rex - An automation and testing framework.
© SURRO INDUSTRIES and Chris Punches, 2017. © SURRO INDUSTRIES and Chris Punches, 2017.
@ -18,8 +18,8 @@
*/ */
#ifndef FTESTS_PLAN_H #ifndef REX_PLAN_H
#define FTESTS_PLAN_H #define REX_PLAN_H
#include <string> #include <string>
#include "../../json/json.h" #include "../../json/json.h"
@ -36,7 +36,7 @@ class Plan: public JSON_Loader
Conf * configuration; Conf * configuration;
public: public:
Plan( Conf * configuration, int LOG_LEVEL ); Plan(Conf * configuration, int LOG_LEVEL );
// append this->tasks from JSON file // append this->tasks from JSON file
void load_plan_file( std::string filename ); void load_plan_file( std::string filename );
@ -63,4 +63,4 @@ private:
Logger slog; Logger slog;
}; };
#endif //FTESTS_PLAN_H #endif //REX_PLAN_H

View File

@ -1,5 +1,5 @@
/* /*
Examplar - An automation and testing framework. rex - An automation and testing framework.
© SURRO INDUSTRIES and Chris Punches, 2017. © SURRO INDUSTRIES and Chris Punches, 2017.

View File

@ -1,5 +1,5 @@
/* /*
Examplar - An automation and testing framework. rex - An automation and testing framework.
© SURRO INDUSTRIES and Chris Punches, 2017. © SURRO INDUSTRIES and Chris Punches, 2017.
@ -18,8 +18,8 @@
*/ */
#ifndef FTESTS_UNITS_H #ifndef REX_UNITS_H
#define FTESTS_UNITS_H #define REX_UNITS_H
#include <vector> #include <vector>
#include "../../json/json.h" #include "../../json/json.h"
@ -54,4 +54,4 @@ private:
Logger slog; Logger slog;
}; };
#endif //FTESTS_UNITS_H #endif //REX_UNITS_H

View File

@ -1,5 +1,5 @@
/* /*
Examplar - An automation and testing framework. rex - An automation and testing framework.
© SURRO INDUSTRIES and Chris Punches, 2017. © SURRO INDUSTRIES and Chris Punches, 2017.
@ -80,7 +80,7 @@ protected:
/// Task::Task() - Constructor for the Task class. The Task is the building block of a Plan indicating of which Unit to /// Task::Task() - Constructor for the Task class. The Task is the building block of a Plan indicating of which Unit to
/// execute, and its dependencies on other units to have already been completed successfully. /// execute, and its dependencies on other units to have already been completed successfully.
Task::Task( int LOG_LEVEL ): Task::Task( int LOG_LEVEL ):
slog( LOG_LEVEL, "e_task" ), slog( LOG_LEVEL, "_task_" ),
definition( LOG_LEVEL ) definition( LOG_LEVEL )
{ {
// it hasn't executed yet. // it hasn't executed yet.
@ -165,9 +165,9 @@ bool Task::has_definition()
/// Task::execute - execute a task's unit definition. /// Task::execute - execute a task's unit definition.
/// See the design document for what flow control needs to look like here. /// See the design document for what flow control needs to look like here.
/// \param verbose - Verbosity level - not implemented yet. /// \param verbose - Verbosity level - not implemented yet.
void Task::execute( Conf * configuration ) void Task::execute(Conf * configuration )
{ {
// DUFFING - If Examplar is broken it's probably going to be in this block. // DUFFING - If rex is broken it's probably going to be in this block.
// Somebody come clean this up, eh? // Somebody come clean this up, eh?
// PREWORK // PREWORK
@ -219,7 +219,9 @@ void Task::execute( Conf * configuration )
this->definition.get_group(), this->definition.get_group(),
target_command, target_command,
this->LOG_LEVEL, this->LOG_LEVEL,
task_name task_name,
this->definition.get_stdout_log_flag(),
configuration->get_logs_path()
); );
// ********************************************** // **********************************************
@ -286,7 +288,9 @@ void Task::execute( Conf * configuration )
this->definition.get_group(), this->definition.get_group(),
rectifier_command, rectifier_command,
this->LOG_LEVEL, this->LOG_LEVEL,
task_name task_name,
this->definition.get_stdout_log_flag(),
configuration->get_logs_path()
); );
// ********************************************** // **********************************************
@ -335,7 +339,9 @@ void Task::execute( Conf * configuration )
this->definition.get_group(), this->definition.get_group(),
target_command, target_command,
this->LOG_LEVEL, this->LOG_LEVEL,
task_name task_name,
this->definition.get_stdout_log_flag(),
configuration->get_logs_path()
); );
// ********************************************** // **********************************************

View File

@ -1,5 +1,5 @@
/* /*
Examplar - An automation and testing framework. rex - An automation and testing framework.
© SURRO INDUSTRIES and Chris Punches, 2017. © SURRO INDUSTRIES and Chris Punches, 2017.
@ -18,8 +18,8 @@
*/ */
#ifndef FTESTS_TASK_H #ifndef REX_TASK_H
#define FTESTS_TASK_H #define REX_TASK_H
#include <string> #include <string>
#include <unistd.h> #include <unistd.h>
#include "../../json/json.h" #include "../../json/json.h"
@ -67,7 +67,7 @@ class Task
std::string get_name(); std::string get_name();
// execute this task's definition // execute this task's definition
void execute( Conf * configuration ); void execute(Conf * configuration );
void mark_complete(); void mark_complete();
@ -79,4 +79,4 @@ private:
int LOG_LEVEL; int LOG_LEVEL;
}; };
#endif //FTESTS_TASK_H #endif //REX_TASK_H

View File

@ -1,5 +1,5 @@
/* /*
Examplar - An automation and testing framework. rex - An automation and testing framework.
© SURRO INDUSTRIES and Chris Punches, 2017. © SURRO INDUSTRIES and Chris Punches, 2017.
@ -80,7 +80,7 @@ public:
/// required, which is used as a flag to halt or continue if rectifier does not heal the system in such a way that /// required, which is used as a flag to halt or continue if rectifier does not heal the system in such a way that
/// target can run successfully. /// target can run successfully.
/// rectify, which is used as a flag to determine in the rectifier runs. /// rectify, which is used as a flag to determine in the rectifier runs.
Unit::Unit( int LOG_LEVEL ): JSON_Loader( LOG_LEVEL ), slog( LOG_LEVEL, "e_unit" ) Unit::Unit( int LOG_LEVEL ): JSON_Loader( LOG_LEVEL ), slog( LOG_LEVEL, "_unit_" )
{ {
this->LOG_LEVEL; this->LOG_LEVEL;
} }
@ -117,6 +117,10 @@ int Unit::load_root(Json::Value loader_root)
{ this->required = loader_root.get("required", errmsg).asBool(); } else { this->required = loader_root.get("required", errmsg).asBool(); } else
throw UnitException("No required attribute specified when loading a unit."); throw UnitException("No required attribute specified when loading a unit.");
if ( loader_root.isMember("log") )
{ this->stdout_log_flag = loader_root.get("log", errmsg).asBool(); } else
throw UnitException("No log attribute specified when loading a unit.");
if ( loader_root.isMember("rectify") ) if ( loader_root.isMember("rectify") )
{ this->rectify = loader_root.get("rectify", errmsg).asBool(); } else { this->rectify = loader_root.get("rectify", errmsg).asBool(); } else
throw UnitException("No rectify boolean attribute specified when loading a unit."); throw UnitException("No rectify boolean attribute specified when loading a unit.");
@ -286,3 +290,12 @@ std::string Unit::get_env_vars_file()
return this->env_vars_file; return this->env_vars_file;
} }
/// Unit::get_stdout_log_flag() - retrieves the file path to use for the unit environment file. This is a file that is
/// sourced by the chosen shell to populate any environment variables.
/// \return the string value of the shell path.
bool Unit::get_stdout_log_flag()
{
if ( ! this->populated ) { throw UnitException("Attempted to access an unpopulated unit."); }
return this->stdout_log_flag;
}

View File

@ -1,5 +1,5 @@
/* /*
Examplar - An automation and testing framework. rex - An automation and testing framework.
© SURRO INDUSTRIES and Chris Punches, 2017. © SURRO INDUSTRIES and Chris Punches, 2017.
@ -19,12 +19,12 @@
*/ */
/* Unit.h /* Unit.h
* Unit is a type that represents a safely deserialized JSON object which defines what actions are taken as Examplar * Unit is a type that represents a safely deserialized JSON object which defines what actions are taken as rex
* iterates through it's Tasks in it's given Plan. They only define the behaviour on execution, while the tasks define * iterates through it's Tasks in it's given Plan. They only define the behaviour on execution, while the tasks define
* which Units are executed and in what order (and which Units a given Task depends on. * which Units are executed and in what order (and which Units a given Task depends on.
*/ */
#ifndef FTESTS_UNIT_H #ifndef FTEST_UNIT_H
#define FTESTS_UNIT_H #define FTEST_UNIT_H
#include <string> #include <string>
#include "../../json/json.h" #include "../../json/json.h"
#include "../low_level/JSON_Loader.h" #include "../low_level/JSON_Loader.h"
@ -59,6 +59,9 @@ private:
// if rectifier exits on non-zero return code, it should be trigger the behaviour indicated by required // if rectifier exits on non-zero return code, it should be trigger the behaviour indicated by required
bool rectify; bool rectify;
//indicator of whether stdout should log to file. used mainly to handle glitchy TUI systems when logs are being tailed.
bool stdout_log_flag;
// user to run process as. // user to run process as.
// not intended for protected accounts, handle your own security // not intended for protected accounts, handle your own security
std::string user; std::string user;
@ -91,6 +94,7 @@ public:
bool get_active(); bool get_active();
bool get_required(); bool get_required();
bool get_rectify(); bool get_rectify();
bool get_stdout_log_flag();
std::string get_user(); std::string get_user();
std::string get_group(); std::string get_group();
std::string get_shell(); std::string get_shell();
@ -100,4 +104,4 @@ private:
Logger slog; Logger slog;
}; };
#endif //FTESTS_UNIT_H #endif //FTEST_UNIT_H

View File

@ -1,5 +1,5 @@
/* /*
Examplar - An automation and testing framework. rex - An automation and testing framework.
© SURRO INDUSTRIES and Chris Punches, 2017. © SURRO INDUSTRIES and Chris Punches, 2017.

View File

@ -1,5 +1,5 @@
/* /*
Examplar - An automation and testing framework. rex - An automation and testing framework.
© SURRO INDUSTRIES and Chris Punches, 2017. © SURRO INDUSTRIES and Chris Punches, 2017.
@ -17,12 +17,12 @@
along with this program. If not, see <https://www.gnu.org/licenses/>. along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
#ifndef FTESTS_LOADERS_H #ifndef REX_LOADERS_H
#define FTESTS_LOADERS_H #define REX_LOADERS_H
#include "../low_level/JSON_Loader.h" #include "../low_level/JSON_Loader.h"
#include "Suite.h" #include "Suite.h"
#include "Plan.h" #include "Plan.h"
#include "Conf.h" #include "Conf.h"
#endif //FTESTS_LOADERS_H #endif //REX_LOADERS_H

View File

@ -1,5 +1,5 @@
/* /*
Examplar - An automation and testing framework. rex - An automation and testing framework.
© SURRO INDUSTRIES and Chris Punches, 2017. © SURRO INDUSTRIES and Chris Punches, 2017.
@ -38,7 +38,7 @@ class JSON_Loader_InvalidJSON: public std::runtime_error { public:
/// JSON_Loader::JSON_Loader - Constructor for JSON_Loader base class. Simply inits to an unpopulated state. /// JSON_Loader::JSON_Loader - Constructor for JSON_Loader base class. Simply inits to an unpopulated state.
/// ///
/// The JSON_Loader type is a base type. It is meant to provide the functionalities shared between Suite and Plan. /// The JSON_Loader type is a base type. It is meant to provide the functionalities shared between Suite and Plan.
JSON_Loader::JSON_Loader( int LOG_LEVEL ): slog( LOG_LEVEL, "e_json" ) JSON_Loader::JSON_Loader( int LOG_LEVEL ): slog( LOG_LEVEL, "_json_" )
{ {
this->populated = false; this->populated = false;
this->LOG_LEVEL = LOG_LEVEL; this->LOG_LEVEL = LOG_LEVEL;
@ -91,7 +91,7 @@ void JSON_Loader::load_json_file( std::string filename )
// first, check if the file exists // first, check if the file exists
if (! exists( filename ) ) if (! exists( filename ) )
{ {
this->slog.log( E_FATAL, "File '" + filename + "' does not exist." ); this->slog.log( E_DEBUG, "File '" + filename + "' does not exist." );
throw JSON_Loader_FileNotFound(); throw JSON_Loader_FileNotFound();
} }

View File

@ -1,5 +1,5 @@
/* /*
Examplar - An automation and testing framework. rex - An automation and testing framework.
© SURRO INDUSTRIES and Chris Punches, 2017. © SURRO INDUSTRIES and Chris Punches, 2017.
@ -17,8 +17,8 @@
along with this program. If not, see <https://www.gnu.org/licenses/>. along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
#ifndef FTESTS_JLOADER_H #ifndef REX_JLOADER_H
#define FTESTS_JLOADER_H #define REX_JLOADER_H
#include "../../json/json.h" #include "../../json/json.h"
#include <iostream> #include <iostream>
#include <fstream> #include <fstream>
@ -55,4 +55,4 @@ private:
Logger slog; Logger slog;
int LOG_LEVEL; int LOG_LEVEL;
}; };
#endif //FTESTS_JLOADER_H #endif //REX_JLOADER_H

View File

@ -1,5 +1,5 @@
/* /*
Examplar - An automation and testing framework. rex - An automation and testing framework.
© SURRO INDUSTRIES and Chris Punches, 2017. © SURRO INDUSTRIES and Chris Punches, 2017.
@ -45,3 +45,13 @@ bool is_dir( std::string path )
stat( path.c_str(), &buf ); stat( path.c_str(), &buf );
return S_ISDIR(buf.st_mode); return S_ISDIR(buf.st_mode);
} }
std::string get_8601()
{
auto now = std::chrono::system_clock::now();
auto itt = std::chrono::system_clock::to_time_t(now);
std::ostringstream ss;
// ss << std::put_time(gmtime(&itt), "%FT%TZ");
ss << std::put_time(localtime(&itt), "%Y-%m-%d_%H:%M:%S");
return ss.str();
}

View File

@ -1,5 +1,5 @@
/* /*
Examplar - An automation and testing framework. rex - An automation and testing framework.
© SURRO INDUSTRIES and Chris Punches, 2017. © SURRO INDUSTRIES and Chris Punches, 2017.
@ -18,13 +18,18 @@
*/ */
#ifndef FTESTS_HELPERS_H #ifndef REX_HELPERS_H
#define FTESTS_HELPERS_H #define REX_HELPERS_H
#include <string> #include <string>
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/param.h> #include <sys/param.h>
#include <unistd.h> #include <unistd.h>
#include <chrono>
#include <sstream>
#include <syslog.h>
#include <iostream>
#include <iomanip>
#include <sstream>
bool exists (const std::string& name); bool exists (const std::string& name);
@ -32,4 +37,7 @@ std::string get_working_path();
bool is_file( std::string ); bool is_file( std::string );
bool is_dir( std::string ); bool is_dir( std::string );
#endif //FTESTS_HELPERS_H std::string get_8601();
#endif //REX_HELPERS_JH

View File

@ -1,8 +1,7 @@
#!/usr/bin/env bash #!/usr/bin/env bash
echo CURSES DIALOG TEST
echo "This is a test of how curses dialogs are handled. Expect freaky behaviour."
dialog --title "Dialog title" --inputbox "Enter your name:" 0 0 dialog --title "Dialog title" --inputbox "Enter your name:" 0 0
echo test
#cat /dev/urandom
exit $? exit $?

View File

@ -1,2 +1,6 @@
#!/bin/bash
echo "dependent test" echo "dependent test"
echo This test depends on another test having succeeded in order to execute.
echo This tests dependencies.
exit $? exit $?

View File

@ -1,5 +1,5 @@
#!/usr/bin/env bash #!/usr/bin/env bash
echo "Failure handling test."
echo "This test will fail on purpose." echo "This test will fail on purpose."
>&2 echo "This test is printing to stderr." >&2 echo "This test is printing to stderr."
exit 1 exit 1

View File

@ -1,8 +1,5 @@
#!/bin/bash #!/bin/bash
echo "This is an independent test. It does not depend on other tests."
echo "Environment file check: TEST_VAR from environment file is set to: $TEST_VAR"
echo "TEST OUTPUT: Test var is: $TEST_VAR"
#dialog --stdout --title "Interact with me!" \
# --backtitle "This is user interaction." \
# --yesno "Yes: pass, No: fail" 7 60
exit $? exit $?

View File

@ -1,9 +1,7 @@
{ {
"execution_context_override": true, "execution_context_override": true,
"execution_context": "/home/bagira/development/internal/Examplar/test", "execution_context": "/home/bagira/development/internal/rex/test",
"units_path": "units/", "units_path": "units/",
"plan_path": "plans/test.plan", "logs_path": "logs/",
"config_version": "3", "config_version": "3"
"env_vars_file": "examplar.variables",
"shell": "/bin/bash"
} }

View File

@ -3,6 +3,7 @@
{ "name": "independent test 1", "dependencies": [ null ] }, { "name": "independent test 1", "dependencies": [ null ] },
{ "name": "independent test 2", "dependencies": [ null ] }, { "name": "independent test 2", "dependencies": [ null ] },
{ "name": "dependent test", "dependencies": [ "independent test 1" ] }, { "name": "dependent test", "dependencies": [ "independent test 1" ] },
{ "name": "curses dialog", "dependencies": [ "independent test 1" ] },
{ "name": "fail", "dependencies": [ null ] } { "name": "fail", "dependencies": [ null ] }
] ]
} }

View File

@ -1,2 +1,5 @@
This test is printing to stderr. This test is printing to stderr.
This test is printing to stderr.
This test is printing to stderr.
This test is printing to stderr.

View File

@ -1,46 +1,128 @@
[2021-04-03_20:20:26] [INFO] [_sproc] [ 'independent test 1' ] TEE Logging enabled. [2021-04-04_19:08:56] [INFO] [_sproc] [ 'independent test 1' ] TEE Logging enabled.
[2021-04-03_20:20:26] [DBUG] [_sproc] [ 'independent test 1' ] DUP2: child_*_pipe[1]->STD*_FILENO [2021-04-04_19:08:56] [DBUG] [_sproc] [ 'independent test 1' ] DUP2: child_*_pipe[1]->STD*_FILENO
[2021-04-03_20:20:26] [DBUG] [_sproc] [ 'independent test 1' ] Attempt: Running as user 'bagira'. [2021-04-04_19:08:56] [DBUG] [_sproc] [ 'independent test 1' ] Attempt: Running as user 'bagira'.
[2021-04-03_20:20:26] [DBUG] [_sproc] [ 'independent test 1' ] Attempt: Running as group_name 'bagira'. [2021-04-04_19:08:56] [DBUG] [_sproc] [ 'independent test 1' ] Attempt: Running as group_name 'bagira'.
[2021-04-03_20:20:26] [DBUG] [_sproc] [ 'independent test 1' ] UID of 'bagira' is '1000'. [2021-04-04_19:08:56] [DBUG] [_sproc] [ 'independent test 1' ] UID of 'bagira' is '1000'.
[2021-04-03_20:20:26] [DBUG] [_sproc] [ 'independent test 1' ] GID of 'bagira' is '1000'. [2021-04-04_19:08:56] [DBUG] [_sproc] [ 'independent test 1' ] GID of 'bagira' is '1000'.
[2021-04-03_20:20:26] [DBUG] [_sproc] [ 'independent test 1' ] Successfully set GID to '1000' (bagira). [2021-04-04_19:08:56] [DBUG] [_sproc] [ 'independent test 1' ] Successfully set GID to '1000' (bagira).
[2021-04-03_20:20:26] [DBUG] [_sproc] [ 'independent test 1' ] Successfully set UID to '1000' (bagira). [2021-04-04_19:08:56] [DBUG] [_sproc] [ 'independent test 1' ] Successfully set UID to '1000' (bagira).
[2021-04-03_20:20:26] [INFO] [_sproc] [ 'independent test 1' ] Identity context set as user 'bagira' and group 'bagira'. [2021-04-04_19:08:56] [INFO] [_sproc] [ 'independent test 1' ] Identity context set as user 'bagira' and group 'bagira'.
variables file says hello and set a variable named TEST_VAR variables file says hello and set a variable named TEST_VAR
TEST OUTPUT: Test var is: 999 TEST OUTPUT: Test var is: 999
[2021-04-03_20:20:26] [INFO] [_sproc] [ 'independent test 2' ] TEE Logging enabled. [2021-04-04_19:08:56] [INFO] [_sproc] [ 'independent test 2' ] TEE Logging enabled.
[2021-04-03_20:20:26] [DBUG] [_sproc] [ 'independent test 2' ] DUP2: child_*_pipe[1]->STD*_FILENO [2021-04-04_19:08:56] [DBUG] [_sproc] [ 'independent test 2' ] DUP2: child_*_pipe[1]->STD*_FILENO
[2021-04-03_20:20:26] [DBUG] [_sproc] [ 'independent test 2' ] Attempt: Running as user 'bagira'. [2021-04-04_19:08:56] [DBUG] [_sproc] [ 'independent test 2' ] Attempt: Running as user 'bagira'.
[2021-04-03_20:20:26] [DBUG] [_sproc] [ 'independent test 2' ] Attempt: Running as group_name 'bagira'. [2021-04-04_19:08:56] [DBUG] [_sproc] [ 'independent test 2' ] Attempt: Running as group_name 'bagira'.
[2021-04-03_20:20:26] [DBUG] [_sproc] [ 'independent test 2' ] UID of 'bagira' is '1000'. [2021-04-04_19:08:56] [DBUG] [_sproc] [ 'independent test 2' ] UID of 'bagira' is '1000'.
[2021-04-03_20:20:26] [DBUG] [_sproc] [ 'independent test 2' ] GID of 'bagira' is '1000'. [2021-04-04_19:08:56] [DBUG] [_sproc] [ 'independent test 2' ] GID of 'bagira' is '1000'.
[2021-04-03_20:20:26] [DBUG] [_sproc] [ 'independent test 2' ] Successfully set GID to '1000' (bagira). [2021-04-04_19:08:56] [DBUG] [_sproc] [ 'independent test 2' ] Successfully set GID to '1000' (bagira).
[2021-04-03_20:20:26] [DBUG] [_sproc] [ 'independent test 2' ] Successfully set UID to '1000' (bagira). [2021-04-04_19:08:56] [DBUG] [_sproc] [ 'independent test 2' ] Successfully set UID to '1000' (bagira).
[2021-04-03_20:20:26] [INFO] [_sproc] [ 'independent test 2' ] Identity context set as user 'bagira' and group 'bagira'. [2021-04-04_19:08:56] [INFO] [_sproc] [ 'independent test 2' ] Identity context set as user 'bagira' and group 'bagira'.
variables file says hello and set a variable named TEST_VAR variables file says hello and set a variable named TEST_VAR
independent test 2 output independent test 2 output
independent test says TEST_VAR is 999 independent test says TEST_VAR is 999
[2021-04-03_20:20:26] [INFO] [_sproc] [ 'dependent test' ] TEE Logging enabled. [2021-04-04_19:08:56] [INFO] [_sproc] [ 'dependent test' ] TEE Logging enabled.
[2021-04-03_20:20:26] [DBUG] [_sproc] [ 'dependent test' ] DUP2: child_*_pipe[1]->STD*_FILENO [2021-04-04_19:08:56] [DBUG] [_sproc] [ 'dependent test' ] DUP2: child_*_pipe[1]->STD*_FILENO
[2021-04-03_20:20:26] [DBUG] [_sproc] [ 'dependent test' ] Attempt: Running as user 'bagira'. [2021-04-04_19:08:56] [DBUG] [_sproc] [ 'dependent test' ] Attempt: Running as user 'bagira'.
[2021-04-03_20:20:26] [DBUG] [_sproc] [ 'dependent test' ] Attempt: Running as group_name 'bagira'. [2021-04-04_19:08:56] [DBUG] [_sproc] [ 'dependent test' ] Attempt: Running as group_name 'bagira'.
[2021-04-03_20:20:26] [DBUG] [_sproc] [ 'dependent test' ] UID of 'bagira' is '1000'. [2021-04-04_19:08:56] [DBUG] [_sproc] [ 'dependent test' ] UID of 'bagira' is '1000'.
[2021-04-03_20:20:26] [DBUG] [_sproc] [ 'dependent test' ] GID of 'bagira' is '1000'. [2021-04-04_19:08:56] [DBUG] [_sproc] [ 'dependent test' ] GID of 'bagira' is '1000'.
[2021-04-03_20:20:26] [DBUG] [_sproc] [ 'dependent test' ] Successfully set GID to '1000' (bagira). [2021-04-04_19:08:56] [DBUG] [_sproc] [ 'dependent test' ] Successfully set GID to '1000' (bagira).
[2021-04-03_20:20:26] [DBUG] [_sproc] [ 'dependent test' ] Successfully set UID to '1000' (bagira). [2021-04-04_19:08:56] [DBUG] [_sproc] [ 'dependent test' ] Successfully set UID to '1000' (bagira).
[2021-04-03_20:20:26] [INFO] [_sproc] [ 'dependent test' ] Identity context set as user 'bagira' and group 'bagira'. [2021-04-04_19:08:56] [INFO] [_sproc] [ 'dependent test' ] Identity context set as user 'bagira' and group 'bagira'.
variables file says hello and set a variable named TEST_VAR variables file says hello and set a variable named TEST_VAR
dependent test dependent test
[2021-04-03_20:20:26] [INFO] [_sproc] [ 'fail' ] TEE Logging enabled. [2021-04-04_19:08:56] [INFO] [_sproc] [ 'fail' ] TEE Logging enabled.
[2021-04-03_20:20:26] [DBUG] [_sproc] [ 'fail' ] DUP2: child_*_pipe[1]->STD*_FILENO [2021-04-04_19:08:56] [DBUG] [_sproc] [ 'fail' ] DUP2: child_*_pipe[1]->STD*_FILENO
[2021-04-03_20:20:26] [DBUG] [_sproc] [ 'fail' ] Attempt: Running as user 'bagira'. [2021-04-04_19:08:56] [DBUG] [_sproc] [ 'fail' ] Attempt: Running as user 'bagira'.
[2021-04-03_20:20:26] [DBUG] [_sproc] [ 'fail' ] Attempt: Running as group_name 'bagira'. [2021-04-04_19:08:56] [DBUG] [_sproc] [ 'fail' ] Attempt: Running as group_name 'bagira'.
[2021-04-03_20:20:26] [DBUG] [_sproc] [ 'fail' ] UID of 'bagira' is '1000'. [2021-04-04_19:08:56] [DBUG] [_sproc] [ 'fail' ] UID of 'bagira' is '1000'.
[2021-04-03_20:20:26] [DBUG] [_sproc] [ 'fail' ] GID of 'bagira' is '1000'. [2021-04-04_19:08:56] [DBUG] [_sproc] [ 'fail' ] GID of 'bagira' is '1000'.
[2021-04-03_20:20:26] [DBUG] [_sproc] [ 'fail' ] Successfully set GID to '1000' (bagira). [2021-04-04_19:08:56] [DBUG] [_sproc] [ 'fail' ] Successfully set GID to '1000' (bagira).
[2021-04-03_20:20:26] [DBUG] [_sproc] [ 'fail' ] Successfully set UID to '1000' (bagira). [2021-04-04_19:08:56] [DBUG] [_sproc] [ 'fail' ] Successfully set UID to '1000' (bagira).
[2021-04-03_20:20:26] [INFO] [_sproc] [ 'fail' ] Identity context set as user 'bagira' and group 'bagira'. [2021-04-04_19:08:56] [INFO] [_sproc] [ 'fail' ] Identity context set as user 'bagira' and group 'bagira'.
variables file says hello and set a variable named TEST_VAR variables file says hello and set a variable named TEST_VAR
This test will fail on purpose. This test will fail on purpose.
[2021-04-04_19:15:50] [INFO] [_sproc] [ 'independent test 1' ] TEE Logging enabled.
[2021-04-04_19:15:50] [DBUG] [_sproc] [ 'independent test 1' ] DUP2: child_*_pipe[1]->STD*_FILENO
[2021-04-04_19:15:50] [DBUG] [_sproc] [ 'independent test 1' ] Attempt: Running as user 'bagira'.
[2021-04-04_19:15:50] [DBUG] [_sproc] [ 'independent test 1' ] Attempt: Running as group_name 'bagira'.
[2021-04-04_19:15:50] [DBUG] [_sproc] [ 'independent test 1' ] UID of 'bagira' is '1000'.
[2021-04-04_19:15:50] [DBUG] [_sproc] [ 'independent test 1' ] GID of 'bagira' is '1000'.
[2021-04-04_19:15:50] [DBUG] [_sproc] [ 'independent test 1' ] Successfully set GID to '1000' (bagira).
[2021-04-04_19:15:50] [DBUG] [_sproc] [ 'independent test 1' ] Successfully set UID to '1000' (bagira).
[2021-04-04_19:15:50] [INFO] [_sproc] [ 'independent test 1' ] Identity context set as user 'bagira' and group 'bagira'.
variables file says hello and set a variable named TEST_VAR
TEST OUTPUT: Test var is: 999
[2021-04-04_19:15:50] [INFO] [_sproc] [ 'independent test 1' ] TEE Logging enabled.
[2021-04-04_19:15:50] [DBUG] [_sproc] [ 'independent test 1' ] DUP2: child_*_pipe[1]->STD*_FILENO
[2021-04-04_19:15:50] [DBUG] [_sproc] [ 'independent test 1' ] Attempt: Running as user 'bagira'.
[2021-04-04_19:15:50] [DBUG] [_sproc] [ 'independent test 1' ] Attempt: Running as group_name 'bagira'.
[2021-04-04_19:15:50] [DBUG] [_sproc] [ 'independent test 1' ] UID of 'bagira' is '1000'.
[2021-04-04_19:15:50] [DBUG] [_sproc] [ 'independent test 1' ] GID of 'bagira' is '1000'.
[2021-04-04_19:15:50] [DBUG] [_sproc] [ 'independent test 1' ] Successfully set GID to '1000' (bagira).
[2021-04-04_19:15:50] [DBUG] [_sproc] [ 'independent test 1' ] Successfully set UID to '1000' (bagira).
[2021-04-04_19:15:50] [INFO] [_sproc] [ 'independent test 1' ] Identity context set as user 'bagira' and group 'bagira'.
variables file says hello and set a variable named TEST_VAR
TEST OUTPUT: Test var is: 999
[2021-04-04_19:15:50] [INFO] [_sproc] [ 'dependent test' ] TEE Logging enabled.
[2021-04-04_19:15:50] [DBUG] [_sproc] [ 'dependent test' ] DUP2: child_*_pipe[1]->STD*_FILENO
[2021-04-04_19:15:50] [DBUG] [_sproc] [ 'dependent test' ] Attempt: Running as user 'bagira'.
[2021-04-04_19:15:50] [DBUG] [_sproc] [ 'dependent test' ] Attempt: Running as group_name 'bagira'.
[2021-04-04_19:15:50] [DBUG] [_sproc] [ 'dependent test' ] UID of 'bagira' is '1000'.
[2021-04-04_19:15:50] [DBUG] [_sproc] [ 'dependent test' ] GID of 'bagira' is '1000'.
[2021-04-04_19:15:50] [DBUG] [_sproc] [ 'dependent test' ] Successfully set GID to '1000' (bagira).
[2021-04-04_19:15:50] [DBUG] [_sproc] [ 'dependent test' ] Successfully set UID to '1000' (bagira).
[2021-04-04_19:15:50] [INFO] [_sproc] [ 'dependent test' ] Identity context set as user 'bagira' and group 'bagira'.
variables file says hello and set a variable named TEST_VAR
dependent test
[2021-04-04_19:15:50] [INFO] [_sproc] [ 'fail' ] TEE Logging enabled.
[2021-04-04_19:15:50] [DBUG] [_sproc] [ 'fail' ] DUP2: child_*_pipe[1]->STD*_FILENO
[2021-04-04_19:15:50] [DBUG] [_sproc] [ 'fail' ] Attempt: Running as user 'bagira'.
[2021-04-04_19:15:50] [DBUG] [_sproc] [ 'fail' ] Attempt: Running as group_name 'bagira'.
[2021-04-04_19:15:50] [DBUG] [_sproc] [ 'fail' ] UID of 'bagira' is '1000'.
[2021-04-04_19:15:50] [DBUG] [_sproc] [ 'fail' ] GID of 'bagira' is '1000'.
[2021-04-04_19:15:50] [DBUG] [_sproc] [ 'fail' ] Successfully set GID to '1000' (bagira).
[2021-04-04_19:15:50] [DBUG] [_sproc] [ 'fail' ] Successfully set UID to '1000' (bagira).
[2021-04-04_19:15:50] [INFO] [_sproc] [ 'fail' ] Identity context set as user 'bagira' and group 'bagira'.
variables file says hello and set a variable named TEST_VAR
This test will fail on purpose.
[2021-04-04_19:23:08] [INFO] [_sproc] [ 'independent test 1' ] TEE Logging enabled.
[2021-04-04_19:23:08] [INFO] [_sproc] [ 'independent test 1' ] Identity context set as user 'bagira' and group 'bagira'.
variables file says hello and set a variable named TEST_VAR
TEST OUTPUT: Test var is: 999
[2021-04-04_19:23:08] [INFO] [_sproc] [ 'independent test 2' ] TEE Logging enabled.
[2021-04-04_19:23:08] [INFO] [_sproc] [ 'independent test 2' ] Identity context set as user 'bagira' and group 'bagira'.
variables file says hello and set a variable named TEST_VAR
independent test 2 output
independent test says TEST_VAR is 999
[2021-04-04_19:23:08] [INFO] [_sproc] [ 'dependent test' ] TEE Logging enabled.
[2021-04-04_19:23:08] [INFO] [_sproc] [ 'dependent test' ] Identity context set as user 'bagira' and group 'bagira'.
variables file says hello and set a variable named TEST_VAR
dependent test
[2021-04-04_19:23:08] [INFO] [_sproc] [ 'fail' ] TEE Logging enabled.
[2021-04-04_19:23:08] [INFO] [_sproc] [ 'fail' ] Identity context set as user 'bagira' and group 'bagira'.
variables file says hello and set a variable named TEST_VAR
This test will fail on purpose.
[2021-04-04_19:45:01] [INFO] [_sproc] [ 'independent test 1' ] TEE Logging enabled.
[2021-04-04_19:45:01] [INFO] [_sproc] [ 'independent test 1' ] Identity context set as user 'bagira' and group 'bagira'.
variables file says hello and set a variable named TEST_VAR
This is an independent test. It does not depend on other tests.
Environment file check: TEST_VAR from environment file is set to: 999
[2021-04-04_19:45:01] [INFO] [_sproc] [ 'independent test 2' ] TEE Logging enabled.
[2021-04-04_19:45:01] [INFO] [_sproc] [ 'independent test 2' ] Identity context set as user 'bagira' and group 'bagira'.
variables file says hello and set a variable named TEST_VAR
independent test 2 output
independent test says TEST_VAR is 999
[2021-04-04_19:45:01] [INFO] [_sproc] [ 'dependent test' ] TEE Logging enabled.
[2021-04-04_19:45:01] [INFO] [_sproc] [ 'dependent test' ] Identity context set as user 'bagira' and group 'bagira'.
variables file says hello and set a variable named TEST_VAR
dependent test
This test depends on another test having succeeded in order to execute.
This tests dependencies.
[2021-04-04_19:45:01] [INFO] [_sproc] [ 'fail' ] TEE Logging enabled.
[2021-04-04_19:45:01] [INFO] [_sproc] [ 'fail' ] Identity context set as user 'bagira' and group 'bagira'.
variables file says hello and set a variable named TEST_VAR
Failure handling test.
This test will fail on purpose.

View File

@ -6,11 +6,12 @@
"rectifier": "", "rectifier": "",
"active": true, "active": true,
"required": true, "required": true,
"log": true,
"user": "bagira", "user": "bagira",
"group": "bagira", "group": "bagira",
"rectify": false, "rectify": false,
"shell": "/usr/bin/env bash", "shell": "/usr/bin/env bash",
"environment": "environments/examplar.variables" "environment": "environments/rex.variables"
}, },
{ {
"name": "independent test 2", "name": "independent test 2",
@ -18,11 +19,12 @@
"rectifier": "", "rectifier": "",
"active": true, "active": true,
"required": true, "required": true,
"log": true,
"user": "bagira", "user": "bagira",
"group": "bagira", "group": "bagira",
"rectify": false, "rectify": false,
"shell": "/usr/bin/env bash", "shell": "/usr/bin/env bash",
"environment": "environments/examplar.variables" "environment": "environments/rex.variables"
}, },
{ {
"name": "dependent test", "name": "dependent test",
@ -30,11 +32,12 @@
"rectifier": "", "rectifier": "",
"active": true, "active": true,
"required": true, "required": true,
"log": true,
"user": "bagira", "user": "bagira",
"group": "bagira", "group": "bagira",
"rectify": false, "rectify": false,
"shell": "/usr/bin/env bash", "shell": "/usr/bin/env bash",
"environment": "environments/examplar.variables" "environment": "environments/rex.variables"
}, },
{ {
"name": "curses dialog", "name": "curses dialog",
@ -42,11 +45,12 @@
"rectifier": "", "rectifier": "",
"active": true, "active": true,
"required": true, "required": true,
"log": false,
"user": "bagira", "user": "bagira",
"group": "bagira", "group": "bagira",
"rectify": false, "rectify": false,
"shell": "/usr/bin/env bash", "shell": "/usr/bin/env bash",
"environment": "environments/examplar.variables" "environment": "environments/rex.variables"
}, },
{ {
"name": "fail", "name": "fail",
@ -54,11 +58,12 @@
"rectifier": "", "rectifier": "",
"active": true, "active": true,
"required": false, "required": false,
"log": true,
"user": "bagira", "user": "bagira",
"group": "bagira", "group": "bagira",
"rectify": false, "rectify": false,
"shell": "/usr/bin/env bash", "shell": "/usr/bin/env bash",
"environment": "environments/examplar.variables" "environment": "environments/rex.variables"
} }
] ]
} }