fixed logic flow bug in Task execution, cleaned up exceptions, improved exception verbosity, and added a logic tree for task execution flow
parent
e8889c3088
commit
14d526d95c
|
@ -2,10 +2,10 @@
|
||||||
"units": [
|
"units": [
|
||||||
{
|
{
|
||||||
"name": "independent test 1",
|
"name": "independent test 1",
|
||||||
"target": "/usr/bin/false",
|
"target": "/usr/bin/true",
|
||||||
"rectifier": "/usr/bin/true",
|
"rectifier": "/usr/bin/true",
|
||||||
"active": true,
|
"active": true,
|
||||||
"required": false,
|
"required": true,
|
||||||
"rectify": true
|
"rectify": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -26,10 +26,10 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "dependent test",
|
"name": "dependent test",
|
||||||
"target": "/usr/bin/true",
|
"target": "/usr/bin/false",
|
||||||
"rectifier": "/usr/bin/false",
|
"rectifier": "/usr/bin/true",
|
||||||
"active": true,
|
"active": true,
|
||||||
"required": false,
|
"required": true,
|
||||||
"rectify": true
|
"rectify": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
@ -0,0 +1,60 @@
|
||||||
|
# Logic Tree for Examplar Task Execution
|
||||||
|
|
||||||
|
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/>.
|
||||||
|
|
||||||
|
## Key:
|
||||||
|
-Actions are labled by indices in a zero-indexed array 'a'.
|
||||||
|
-Decisions are labeled by indices in a zero-indexed array 'd'.
|
||||||
|
-Decisions have two possible states, TRUE or FALSE (0 or 1 respectively).
|
||||||
|
-Decision determinations are labeled with the decision label and a subspecifier.
|
||||||
|
|
||||||
|
## Diagram:
|
||||||
|
|
||||||
|
a[0] Execute Target
|
||||||
|
d[0] Error Code Check
|
||||||
|
d[0].0 ZERO
|
||||||
|
a[1] NEXT
|
||||||
|
d[0].1 NON-ZERO
|
||||||
|
d[1] Rectify Check
|
||||||
|
d[1].0 FALSE
|
||||||
|
d[2] Required Check
|
||||||
|
d[2].0 FALSE
|
||||||
|
a[2] NEXT
|
||||||
|
d[2].1 TRUE
|
||||||
|
a[3] EXCEPTION
|
||||||
|
d[1].1 TRUE
|
||||||
|
a[4] Execute Rectifier
|
||||||
|
d[3] Error Code Check
|
||||||
|
d[3].1 NON-ZERO
|
||||||
|
d[4] Required Check
|
||||||
|
d[4].0 FALSE
|
||||||
|
a[5] NEXT
|
||||||
|
d[4].1 TRUE
|
||||||
|
a[6] EXCEPTION
|
||||||
|
d[3].0 ZERO
|
||||||
|
a[7] Re-Execute Target
|
||||||
|
d[5] Error Code Check
|
||||||
|
d[5].0 ZERO
|
||||||
|
a[8] NEXT
|
||||||
|
d[5].1 NON-ZERO
|
||||||
|
d[6] Required Check
|
||||||
|
d[6].0 FALSE
|
||||||
|
a[9] NEXT
|
||||||
|
d[6].1 TRUE
|
||||||
|
a[10] EXCEPTION
|
||||||
|
|
|
@ -17,11 +17,11 @@
|
||||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include "src/json/json.h"
|
#include "src/json/json.h"
|
||||||
#include "src/loaders/loaders.h"
|
#include "src/loaders/loaders.h"
|
||||||
|
|
||||||
|
|
||||||
int main( )
|
int main( )
|
||||||
{
|
{
|
||||||
bool verbose = true;
|
bool verbose = true;
|
||||||
|
|
|
@ -27,8 +27,6 @@
|
||||||
#include "Unit.h"
|
#include "Unit.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class Suite: public JSON_Loader
|
class Suite: public JSON_Loader
|
||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
|
|
|
@ -35,18 +35,49 @@ public:
|
||||||
Task_NotReady(): std::runtime_error("Task: Attempted to access a unit of a Task that is not defined.") {}
|
Task_NotReady(): std::runtime_error("Task: Attempted to access a unit of a Task that is not defined.") {}
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Task_RequiredButFailedTask - Exception thrown when a Task is failed but required, and rectification also failed.
|
|
||||||
class Task_RequiredButFailedTask: public std::runtime_error {
|
/// Task_RequiredButFailedTask - Exception thrown when a Task fails but should not.
|
||||||
|
class TaskException: public std::exception
|
||||||
|
{
|
||||||
public:
|
public:
|
||||||
Task_RequiredButFailedTask(): std::runtime_error("Task: Attempted to execute a Task that failed and was required.") {}
|
/** Constructor (C strings).
|
||||||
|
* @param message C-style string error message.
|
||||||
|
* The string contents are copied upon construction.
|
||||||
|
* Hence, responsibility for deleting the char* lies
|
||||||
|
* with the caller.
|
||||||
|
*/
|
||||||
|
explicit TaskException(const char* message):
|
||||||
|
msg_(message)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Constructor (C++ STL strings).
|
||||||
|
* @param message The error message.
|
||||||
|
*/
|
||||||
|
explicit TaskException(const std::string& message):
|
||||||
|
msg_(message)
|
||||||
|
{}
|
||||||
|
|
||||||
|
/** Destructor.
|
||||||
|
* Virtual to allow for subclassing.
|
||||||
|
*/
|
||||||
|
virtual ~TaskException() throw (){}
|
||||||
|
|
||||||
|
/** Returns a pointer to the (constant) error description.
|
||||||
|
* @return A pointer to a const char*. The underlying memory
|
||||||
|
* is in posession of the Exception object. Callers must
|
||||||
|
* not attempt to free the memory.
|
||||||
|
*/
|
||||||
|
virtual const char* what() const throw (){
|
||||||
|
return msg_.c_str();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
/** Error message.
|
||||||
|
*/
|
||||||
|
std::string msg_;
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Task_RequiredButFailedTask - Exception thrown when a Task is failed but required, and rectification also failed but
|
|
||||||
/// returned with a zero exit code (dont try to fool the check).
|
|
||||||
class Task_RequiredButRectifierDoesNotHeal: public std::runtime_error {
|
|
||||||
public:
|
|
||||||
Task_RequiredButRectifierDoesNotHeal(): std::runtime_error("Task: The rectification script was executed and reported success, but did not actually heal the faulty condition of the Task target.") {}
|
|
||||||
};
|
|
||||||
|
|
||||||
/// 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.
|
||||||
|
@ -134,6 +165,11 @@ bool Task::has_definition()
|
||||||
return this->defined;
|
return this->defined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/// 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.
|
||||||
|
@ -143,7 +179,8 @@ void Task::execute( bool verbose )
|
||||||
|
|
||||||
// PREWORK
|
// PREWORK
|
||||||
// throw if unit not coupled to all necessary values since Task is stateful (yes, stateful is okay)
|
// throw if unit not coupled to all necessary values since Task is stateful (yes, stateful is okay)
|
||||||
if (! this->has_definition() ) {
|
if ( ! this->has_definition() )
|
||||||
|
{
|
||||||
throw Task_NotReady();
|
throw Task_NotReady();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -156,81 +193,160 @@ void Task::execute( bool verbose )
|
||||||
std::string target_command = this->definition.get_target();
|
std::string target_command = this->definition.get_target();
|
||||||
|
|
||||||
// if we're in verbose mode, do some verbose things
|
// if we're in verbose mode, do some verbose things
|
||||||
if ( verbose ) {
|
if ( verbose )
|
||||||
|
{
|
||||||
std::cout << "\tUsing unit \"" << task_name << "\"." << std::endl;
|
std::cout << "\tUsing unit \"" << task_name << "\"." << std::endl;
|
||||||
std::cout << "\tExecuting target \"" << target_command << "\"." << std::endl;
|
std::cout << "\tExecuting target \"" << target_command << "\"." << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
// execute target
|
// a[0] execute target
|
||||||
int return_code = Sproc::execute( target_command );
|
int return_code = Sproc::execute( target_command );
|
||||||
|
|
||||||
// d[0] check exit code of target
|
// **********************************************
|
||||||
if (return_code == 0) {
|
// d[0] Error Code Check
|
||||||
// Zero d[0] return from target execution, good to return
|
// **********************************************
|
||||||
|
if ( return_code == 0 )
|
||||||
|
{
|
||||||
|
// d[0].0 ZERO
|
||||||
|
|
||||||
if ( verbose ) {
|
if ( verbose ) {
|
||||||
std::cout << "\tTarget " << task_name << " succeeded." << std::endl;
|
std::cout << "\tTarget " << task_name << " succeeded." << std::endl;
|
||||||
}
|
}
|
||||||
this->mark_complete();
|
this->mark_complete();
|
||||||
// next
|
|
||||||
} else {
|
// a[1] NEXT
|
||||||
// Non-Zero d[0] from initial target execution, get to d[1]
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( return_code != 0 )
|
||||||
|
{
|
||||||
|
// d[0].1 NON-ZERO
|
||||||
|
|
||||||
std::cout << "\tTarget \"" << task_name << "\" failed with exit code " << return_code << "." << std::endl;
|
std::cout << "\tTarget \"" << task_name << "\" failed with exit code " << return_code << "." << std::endl;
|
||||||
|
|
||||||
// check if rectify pattern is enabled d[1]
|
// **********************************************
|
||||||
if ( this->definition.get_rectify() ) {
|
// d[1] Rectify Check
|
||||||
// yes d[1]
|
// **********************************************
|
||||||
|
if (! this->definition.get_rectify() )
|
||||||
|
{
|
||||||
|
// d[1].0 FALSE
|
||||||
|
|
||||||
|
// **********************************************
|
||||||
|
// d[2] Required Check
|
||||||
|
// **********************************************
|
||||||
|
if (! this->definition.get_required() )
|
||||||
|
{
|
||||||
|
// d[2].0 FALSE
|
||||||
|
// a[2] NEXT
|
||||||
|
std::cout << "\tThis task is not required to continue the plan. Moving on." << std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( this->definition.get_required() )
|
||||||
|
{
|
||||||
|
// d[2].1 TRUE
|
||||||
|
// a[3] EXCEPTION
|
||||||
|
throw TaskException("Task \"" + task_name + "\" is required, and failed, and rectification is not enabled.");
|
||||||
|
}
|
||||||
|
// **********************************************
|
||||||
|
// end - d[2] Required Check
|
||||||
|
// **********************************************
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if ( this->definition.get_rectify() )
|
||||||
|
{
|
||||||
|
// d[1].1 TRUE (Rectify Check)
|
||||||
std::cout << "\tRectification pattern is enabled for \"" << task_name << "\"." << std::endl;
|
std::cout << "\tRectification pattern is enabled for \"" << task_name << "\"." << std::endl;
|
||||||
// execute RECTIFIER
|
|
||||||
|
// a[4] Execute RECTIFIER
|
||||||
std::string rectifier_command = this->definition.get_rectifier();
|
std::string rectifier_command = this->definition.get_rectifier();
|
||||||
std::cout << "\tExecuting rectification: " << rectifier_command << "." << std::endl;
|
std::cout << "\tExecuting rectification: " << rectifier_command << "." << std::endl;
|
||||||
int rectifier_error = Sproc::execute( rectifier_command );
|
int rectifier_error = Sproc::execute( rectifier_command );
|
||||||
|
|
||||||
// d[3] check exit code of rectifier
|
// **********************************************
|
||||||
if (rectifier_error) {
|
// d[3] Error Code Check for Rectifier
|
||||||
//d[3] non-zero
|
// **********************************************
|
||||||
|
if ( rectifier_error != 0 )
|
||||||
|
{
|
||||||
|
// d[3].1 Non-Zero
|
||||||
std::cout << "\tRectification of \"" << task_name << "\" failed with exit code "
|
std::cout << "\tRectification of \"" << task_name << "\" failed with exit code "
|
||||||
<< rectifier_error << "." << std::endl;
|
<< rectifier_error << "." << std::endl;
|
||||||
|
|
||||||
// d[2] check if REQUIRED
|
// **********************************************
|
||||||
if ( this->definition.get_required() ) {
|
// d[4] Required Check
|
||||||
// d[2] yes
|
// **********************************************
|
||||||
// halt/exception
|
if ( ! this->definition.get_required() ) {
|
||||||
throw Task_RequiredButFailedTask();
|
// d[4].0 FALSE
|
||||||
|
// a[5] NEXT
|
||||||
|
std::cout << "\tThis task is not required to continue the plan. Moving on." << std::endl;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
// d[2] no
|
|
||||||
// next
|
|
||||||
}
|
|
||||||
// d[3] zero
|
|
||||||
|
|
||||||
// execute target
|
if ( this->definition.get_required() )
|
||||||
|
{
|
||||||
|
// d[4].1 TRUE
|
||||||
|
// a[6] EXCEPTION
|
||||||
|
throw TaskException("Task \"" + task_name + "\" is required, and failed, then rectified but rectification failed.");
|
||||||
|
}
|
||||||
|
// **********************************************
|
||||||
|
// end - d[4] Required Check
|
||||||
|
// **********************************************
|
||||||
|
}
|
||||||
|
|
||||||
|
// d[3] check exit code of rectifier
|
||||||
|
if ( rectifier_error == 0 )
|
||||||
|
{
|
||||||
|
// d[3].0 Zero
|
||||||
|
std::cout << "\tRectification returned successfully." << std::endl;
|
||||||
|
|
||||||
|
// a[7] Re-execute Target
|
||||||
std::cout << "\tRe-Executing target \"" << this->definition.get_target() << "\"." << std::endl;
|
std::cout << "\tRe-Executing target \"" << this->definition.get_target() << "\"." << std::endl;
|
||||||
int retry_code = Sproc::execute( target_command );
|
int retry_code = Sproc::execute( target_command );
|
||||||
|
|
||||||
// d[4] exit code of target retry
|
// **********************************************
|
||||||
if (retry_code == 0) {
|
// d[5] Error Code Check
|
||||||
// d[4] zero
|
// **********************************************
|
||||||
|
if ( retry_code == 0 )
|
||||||
|
{
|
||||||
|
// d[5].0 ZERO
|
||||||
|
// a[8] NEXT
|
||||||
|
std::cout << "\tRe-execution was successful." << std::endl;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
// d[4] non-zero
|
|
||||||
// d[5] required check
|
if ( retry_code != 0 )
|
||||||
if ( this->definition.get_required() ) {
|
{
|
||||||
// d[5] yes
|
// d[5].1 NON-ZERO
|
||||||
std::cout << "\tTask \"" << task_name << "\" is required but rectification did not heal." << std::endl;
|
std::cout << "\tRe-execution failed with exit code " << retry_code << "." << std::endl;
|
||||||
throw Task_RequiredButRectifierDoesNotHeal();
|
|
||||||
|
|
||||||
|
// **********************************************
|
||||||
|
// d[6] Required Check
|
||||||
|
// **********************************************
|
||||||
|
if ( ! this->definition.get_required() )
|
||||||
|
{
|
||||||
|
// d[6].0 FALSE
|
||||||
|
// a[9] NEXT
|
||||||
|
std::cout << "\tThis task is not required to continue the plan. Moving on." << std::endl;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
// d[5] no
|
|
||||||
// next
|
if ( this->definition.get_required() )
|
||||||
|
{
|
||||||
|
// d[6].1 TRUE
|
||||||
|
// a[10] EXCEPTION
|
||||||
|
throw TaskException("Task \"" + task_name + "\" is required, and failed, then rectified but rectifier did not heal the condition causing the target to fail. Cannot proceed with Plan.");
|
||||||
}
|
}
|
||||||
// no d[1]
|
// **********************************************
|
||||||
std::cout << "\tRectification is not enabled for \"" << task_name << "\"." << std::endl;
|
// end - d[6] Required Check
|
||||||
// required d[2]
|
// **********************************************
|
||||||
if ( this->definition.get_required() ) {
|
}
|
||||||
// d[2] yes
|
|
||||||
// This is executing.....
|
}
|
||||||
std::cout << "\tThis task is required to continue the plan." << std::endl;
|
}
|
||||||
// but these are NOT executing?????
|
// **********************************************
|
||||||
throw Task_RequiredButFailedTask();
|
// end d[1] Rectify Check
|
||||||
} // d[2] no
|
// **********************************************
|
||||||
std::cout << "\tThis task is not required to continue the plan." << std::endl;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -27,6 +27,7 @@
|
||||||
|
|
||||||
class Task
|
class Task
|
||||||
{
|
{
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// the name of this task
|
// the name of this task
|
||||||
std::string name;
|
std::string name;
|
||||||
|
|
Loading…
Reference in New Issue