diff --git a/README.md b/README.md index 6412df8..d3546f6 100644 --- a/README.md +++ b/README.md @@ -1,104 +1,102 @@ # Exeiac -## Description -`exeiac` is a tool that enables infrastructure folks to handle several -different provisionning IaC (Infrastructure as Code) tools, under a -single CLI, and helps solving some recurrent paintpoints with IaC. +## Why use exeIaC +`exeiac` is a tool that enables infrastructure folks to handle several +different provisioning IaC (Infrastructure as Code) tools under a single CLI +and helps solve some recurrent pain points with IaC. -It follows the brick convention, which describes an infrastructure as -a set of bricks. A brick is a piece of infrastructure, it -simultaneously is the actual infrastructure element, the code that -describes it and a piece code that allows its execution; be it a -terraform state, an ansible playbook or a helm chart for instance. +It follows the brick convention, which describes an infrastructure as a set of +bricks. A brick is a piece of infrastructure; it simultaneously is the actual +infrastructure element, the code that describes it, and a piece of code that +allows its execution; be it a terraform state, an ansible playbook, or a helm +chart, for instance. This project was born from the following needs: - solve dependencies issues -- increase transparency as to how a piece of infrastructure should be - deployed. No matter the provisionning tool you use, if there's a - module for it, `exeiac` will handle it -- allow for a clean way to interact with only a part of your - infrastructure in a safe way, without breaking the dependency tree, - even if your infrastructure management tool doesn't provide that - feature +- increase transparency as to how a piece of infrastructure should be deployed. + No matter the provisioning tool you use, if there's a module for it, `exeiac` + will handle it +- allow for a clean way to interact with only a part of your infrastructure in + a safe way, without breaking the dependency tree, even if your infrastructure + management tool doesn't provide that feature -## DOCUMENTATION +## What is a brick +First, exeIaC deals with infra bricks. What is an infra brick? Basically, it's +a directory that contains some infra code. It can be a terraform directory to +deploy a VM, an ansible playbook to configure a host, a helm chart, or simply a +template that describes some instructions to do manually. -- philosophy: theoretical approach useful to write your infra code and understand - exeiac best practices - - common infra code problems we try to solve with exeiac - - define infra as a set of bricks - - define the brick concept - - explain how bricks depends of each other - - vocabulary -- development: contain specs and schema to understand how it is coded -- user: contain all you need to use the tool and create an infra code that - respect the convention and best practices -- examples: examples of simple infra code and module +This summary should be enough for you to be able to read the following +paragraphs and have a basic understanding of exeIaC. But if you want to get a +better grasp of the brick concept itself and its genius simplicity, you can +find a deeper insight on +[this documentation page](./docs/brick_concept_and_dependencies). ## Get started -### Installation - -Clone the git repository and build: -``` bash -$ git clone github.com/arthur91f/exeiac/src/exeiac -$ cd exeiac -$ go install src/exeiac -``` +- **1. Get exeiac binary and install completion** + ```bash + asdf plugin add golang https://github.com/asdf-community/asdf-golang.git + asdf install golang 1.19 + git clone https://github.com/arthur91f/exeiac.git + cd exeiac/ + asdf local golang 1.19 + go install src/exeiac + ``` + - bash completion: `cp './src/completion/scripts/exeiac.sh' '/usr/share/bash-completion/completions/exeiac'` + - zsh completion: `cp './src/completion/scripts/exeiac.zsh' '/usr/share/zsh/site-functions/_exeiac'` + - fish completion: `cp './src/completion/scripts/exeiac.fish' '/usr/share/fish/vendor_completions.d/exeiac.fish'` -There is no release process yet, but on Go version 1.16 or later you can: -``` bash -# Install at tree head: -$ go install github.com/arthur91f/exeiac/src/exeiac/src/exeiac@main -``` +- **2. Write your modules** in whatever language you want. A module can be seen + as a makefile to deploy your brick. Basically, it's a shell script that + follows some conventions described here: + [How to write module](./docs/howto_write_module.md). You have to implement + three commands: + - *describe_module_for_exeiac* (that displays a json) + - *lay* to deploy your brick + - *output* to display some specs of your brick such as IP address, login... + - ... you can implement other commands like plan, remove, lint, help... -- get the exeiac binary -- have an infra code that follow some conventions (see below for more details) - - each brick is a directory prefixed by a number to make the apply order - transparent - - each elementary brick should have a brick.yml to define how it will be - executed and the input it needed from dependencies - - each elementary brick should reference in brick.yml an executable or module - to execute itself. (a module is simply an executable that is not in the - brick directory and that can be called by many bricks) - - Read [howto_write_brick](./docs/howto_write_brick.md) -- create a conf file in /etc/exeiac/exeiac.yml or $HOME/.config/exeiac.yml - ```yaml - modules_path: - terraform: $HOME/git-repos/exeiac-modules/terraform - ansible: $HOME/git-repos/exeiac-modules/terraform - room_paths_list: - - $HOME/git-repos/infra-ground - - $HOME/git-repos/applications - - $HOME/git-repos/users - ``` +- **3. Put a YAML file in each IaC directory** to describe your bricks as here + [How to write brick](./docs/howto_write_brick.md). It will let: + - exeiac identify your IaC directory as an infra brick + - associate your brick to its module + - exeiac understand your brick's dependencies and how to present them to the + brick -### Simple command line examples +- **4. Write your exeiac conf file** in your home or in /etc to let exeiac + binary find your module and your infra code. + [How to write config file](./docs/howto_write_configuration_file.md) -- display a brick output - ```json - $ exeiac output ./infra-core/2-staging/2-ssh_bastion - { - "instance_id": "bastion-staging-221022", - "private_ip": "10.11.3.2", - "public_ip": "34.33.31.30" - } - ``` -- deploy a brick and recursively deploy all bricks that depends on an output - that have changed. Note that here we have used the brickname and not the path - ```bash - exeiac lay infra-core/staging/ssh_bastion --bricks-specifier=selected+needed_dependents - ``` -- destroy a higher level brick. It will destroy all elementary bricks - contained in the higher level bricks in the right order. - ```bash - exeiac remove infra-core/staging - ``` -- get more help - ```bash - exeiac help - ``` +- **5. Enjoy exeiac** + Here are some examples of basic commands you can execute: + - Display output of a brick + ```bash + exeiac output infra-ground/envs/staging/network + ``` + - Plan all sub-bricks of infra-ground/envs/staging + ```bash + exeiac output infra-ground/envs/staging + ``` + - Display all bricks that should be re-deployed after the change of the + brick's network output .network.ip_range + ```bash + exeiac get-depends infra-ground/envs/staging/network -j $.network.ip_range + ``` + - Display all bricks that can be impacted by the re-deploy of the brick network + ```bash + exeiac show infra-ground/envs/staging/network --bricks-specifiers linked_next --format name + ``` + - Deploy/re-deploy a drift in brick staging/network and all other bricks + impacted by that drift recursively. + ```bash + exeiac smart-lay infra-ground/envs/staging/network --non-interactive + ``` -### Create a module or an executable +## Useful links -Read [howto_write_module](./docs/howto_write_module.md) +- **local** + - [How to join us](./docs/to_write.md) +- **external** + - [Download exeiac](https://download-exeiac.91f.ovh) + - [exeIaC presentation](https://drive.google.com/blabla) diff --git a/docs/brick_concept_and_dependencies.md b/docs/brick_concept_and_dependencies.md new file mode 100644 index 0000000..ab48c77 --- /dev/null +++ b/docs/brick_concept_and_dependencies.md @@ -0,0 +1,223 @@ +# Brick Concept and Dependencies + +This documentation will help you understand how to represent a brick and its +sub-elements and how manipulating these elements will enable us to manage the +three types of dependencies. + +To grasp the rationale behind this concept, you should read this article: +[philosophy](./philosophy.md). + +## Defining the Brick Concept + +### In Brief + +Firstly, exeIaC deals with infra bricks. What is an infra brick? Essentially, +it's a directory containing infrastructure code. This could be a terraform +directory for deploying a VM, an ansible playbook for configuring a host, a +helm chart, or simply a template describing manual instructions. + +### Delving Deeper + +In order to manage our infrastructure in a simple way, exeIaC solely focuses on +one concept : the brick. Therefore, an infra can be seen as one super brick +containing bricks, which in turn can contain more bricks and so on. + +Bricks are layed on other bricks and thus depend on previously laid bricks. +Where are these dependencies? Since we only have one concept, these dependencies +are also within bricks. + +From a DevOps perspective, we aim for a comprehensive approach to infra. Thus, +infrastructure should contain all elements and process to deploy and maintain an +application on line. Therefore, a brick encompasses not only the Terraform code +but also includes the Makefile for deployment (and not just deployment but also +planning, linting, etc.). + +If we write the last two ingredients in our brick, the infrastructure will know +the correct deploying order. However, something is still missing. Infra evolves, +and sometimes updates are necessary. Similar to a wall, you can't change a brick +in the middle of the wall without updating surrounding bricks. Similarly, in +infra, changes require updates to dependent bricks, and we may have events that +trigger redeployment. + +Thus, a brick contains: +- Code description: The infra code (terraform, ansible...) +- Commands: The brick interface for deploying, planning, obtaining specific + brick state, etc. +- Output: Infra-specific states such as generated passwords or assigned IP + addresses during brick deployment. +- State: The real infra state - whether the infra has drifted or has already + been deployed. +- Input: Environment data (output from other bricks) used as input for some + commands. +- Event: command data output that could trigger other bricks commands. +- Trigger: Event observer that will redploy if necessary. + +Brick inputs and outputs are static, describing infra states. Events and +triggers are ephemeral, describing occurrences and actions. + +![Brick Elements](./illustrations/brick_elements.drawio.png) + +Commands can be executed by humans after modifying code or triggered by an other +bricks' event. + +Commands' input are other bricks' outputs and the code description. + +Commands also generate events as output. These events could indicate: +- Successful deployment correcting a drift or having nothing to do +- Changes in output values during deployment +- Recreation of a VM instance due to unchanged specs (not reflected in brick's + outputs) + +## How exeIaC Implements It + +For exeIaC, the brick command is a module that can be reuse by other bricks. So +it permits factorization. Essentially, a module is an executable with a +standardized interface that can be enriched. The module interface should contain +a method to specify the implemented interface and how to retrieve events for +each method. [Learn more about writing modules](./howto_write_module.md). + +The input needed for each command method and how to present it is defined in a +`brick.yml` file inside the brick directory. +[Learn how to write a brick](./howto_write_brick.md). + +## Dependency Types + +### Examples + +To explore all three dependency types, we'll base our discussion on an example +involving the interconnection of three bricks within an infra. Here's the list +of bricks and their outputs: +- A: a cloud VM instance + - Output: + - `hw_spec.ram` + - `hw_spec.cpu` + - Event: + - `vm_recreated` +- B: configuration of that VM instance + - Input: + - `hw_spec.ram` +- C: a hop SSH server needed to connect and configure the instance + - Output: + - `ip_address` + - `credentials` + +The outputs list is not exhaustive but sufficient to explain the three types of +dependencies. + +![Dependency Schema](./illustrations/infra_example.drawio.png) + +### Precising Dependency Definitions + +Ok brick depends of each other. Actually it's logical because we can't configure +a VM that doesn't exist. But why defining different types of dependencies can be +usefull? + +We seek to define different types of dependencies in order to know when we have +to re-deploy a brick. Actually a brick changes doesn't mean all bricks above it +need redeployment. For example, installing vim on an SSH server does not +necessitate redeploying configurations. + +A dependency refers to data from a brick's output or event. It can be an input, +a trigger, or both (a trigger which is not only a boolean). + +But in terms of implementation, what brick's sub-element use dependencies datas? +It's the command element (module) that needs dependencies to execute itself. + +While all command methods may need input, they don't necessary needs the same +inputs. For instance : +- a lint method for code checking likely requires no input +- a `terraform apply` may need a `tfvars` file +- a `terraform output` only needs credentials to access the state. + +Dependency is data retrieved from another brick's output or event, necessary for +specific module methods. Each dependency can potentially trigger a specific +brick's module method. + +![Dependency Schema](./illustrations/brick_elements_dependency.drawio.png) + +### The Classic Dependency + +This is the most common type : when the input changes, it triggers a redeploy. +For example, for brick B, the dependency could be `A.hw_specs.ram` triggering a +re-deployment in order to reconfigurate the java heap size. + +### The Weak Dependency + +The module needs data as input to execute, but changes in that data won't +trigger a redeploy. For instance, to deploy brick B, you need to access +`C.ip_address` and `C.credentials` to connect to the VM. However, if +`C.credentials` change, it won't impact brick B's configuration. + +### The Special Dependency + +This category includes dependencies triggering a redeploy but not due to input +changes. For instance, destroying and recreating the VM of brick A without +altering any output read as input for brick B necessitates redeploying brick +B's configuration. + +It exists 3 way to implement this kind of dependency: +- testing the boolean event `A.recreated_vm` +- testing if event list `A.recreated_vms_list` contains your vm name. Here the + event has a value. +- testing if output `A.vm-id` or `A.creation-timestamp` has changed. Here you + manage special dependency as a classic dependency with the difference that + the data is not needed as input. + +For exeIaC, a special dependency is merely a test unrelated to value changes. + +### Dependencies that don't trigger a simple redeploy + +The system permits actions other than simple deployment (lay). This flexibility +is useful for implementing various deployment functions, such as 1-10-100 +deployments or a special redeployment for password renewals. + +## From Elementary Brick to Infra + +### Elementary Brick + +An elementary brick is a brick that contain infra code. It is the smallest infra +element. What we've defined as +[Sub-elements of a Brick](#sub-element-of-a-brick) describe actually an +elementary brick. + +### Higher-Order Brick + +A higher-order brick only contains elementary bricks. Executing a higher-order +brick involves executing all sub-bricks in the correct order. The input of a +higher-order brick comprises the inputs of all sub-bricks (excluding those +referencing other sub-bricks' outputs). The output of a higher-order brick +includes all outputs of its contained elementary bricks. + +An higher-order brick is not seen very differently from an elementary brick. +Sometimes, exeIaC's vision is solely based on elementary bricks, but let's +redefine brick elements for a higher-order brick: +- **Input & Triggers**: All inputs and triggers of its sub-bricks, minus inputs + and events referencing other sub-bricks. +- **Output & Events**: All outputs and events of its sub-bricks. +- **Command**: Deploying a higher-order brick involves deploying all its + sub-bricks in the correct order. + + +**NB: Execution Order** to rework + +As you can see on the page [how to write a brick](./howto_write_brick.md), each +brick has its directory name prefixed by a priority number. So, take care in +composing your higher-order bricks. In a higher-order brick, you will execute +all bricks according to the priority you see. A higher-order brick will be +perceived as an elementary brick. Therefore, it's straightforward for a human to +read their infra code. You won't need to understand how to execute a brick to +execute it. + +### Room + +A room is a higher-order brick that corresponds to a directory and must be +listed in the [exeIaC configuration file](./howto_write_configuration_file.md) +to be recognized by exeIaC. Its conventions are slightly different from other +bricks, but it can be considered as a brick. + +Usually, it is a git repository. + +### Infra + +An infra is an ordered list of rooms. So, it's also a brick. It's a +self-sufficient brick with no input or triggers and with no output or events. diff --git a/docs/howto_write_brick.md b/docs/howto_write_brick.md index 38ee001..d6261e5 100644 --- a/docs/howto_write_brick.md +++ b/docs/howto_write_brick.md @@ -1,71 +1,71 @@ -# Write your brick +```markdown +# Write Your Brick ## Generality -A brick is an element of your infra and it's IaC representation. It can be a -terraform state, an ansible playbook, an helm charts, a combination all thoose -things or even other things depending of your brick's module. +A brick is an element of your infra and its IaC representation. It can be a +terraform state, an ansible playbook, a helm chart, a combination of all those +things, or even other elements depending on your brick's module. A brick is represented by a directory. -But a brick is not only the code describing the real infra element. -The elements describing a brick are : +But a brick is not only the code describing the real infra element. +The elements describing a brick are: - code: content of the directory -- outputs: the state of the brick, the value taken by things declared in codes - as genereated password, given public ipaddress or just some value that can be - used by other bricks -- inputs: describe the context of the brick, it is composed by parts of outputs +- outputs: the state of the brick, the values taken by things declared in codes + as generated passwords, given public IP addresses, or just some values that + can be used by other bricks +- inputs: describe the context of the brick, it is composed of parts of outputs of other bricks -- module events: output of the module action they are describing what have been +- module events: output of the module action, they are describing what has been done not the state of the brick (that is described by outputs) - triggers: input change or other brick module's event that will trigger a lay or another action on the brick -**Note: elementary bricks vs higher order bricks:** Every brick is represented -by a directory. Some brick contains other bricks and some other just contain a -brick.yml file that describe its module, inputs, triggers ... +**Note: Elementary Bricks vs Higher-Order Bricks:** Every brick is represented +by a directory. Some bricks contain other bricks and some others just contain a +brick.yml file that describes its module, inputs, triggers... -## Directory convention +## Directory Convention -You can identify brick from their path: -- A brick must be in a "room" (listed in exeiac conf files) or -- A brick directory name must begin with a priority number that describe the +You can identify bricks from their path: +- A brick must be in a "room" (listed in + [exeiac configuration file](./howto_write_configuration_file.md)) or be a room. +- A brick directory name must begin with a priority number that describes the building sequence of bricks inside the same room or super brick. -**Note: why the priority number ?** Actually it's redundant with dependencies -information and exeIaC should be able to build its valid laying sequence. But -we wanted to have a convention that let humans easily partially guess some layer -and dependency tree. It is also useful to identify easily what directory is a -brick. Based on this you can add documentation directory or a directory with -maintenance script as dump and restore database that will be ignored by exeiac. +**Note: Why the Priority Number?** Actually, it's redundant with dependency +information, and exeIaC should be able to build its valid laying sequence. But +we wanted to have a convention that lets humans easily partially guess some +layer and dependency tree. It is also useful to identify easily what directory +is a brick. Based on this, you can add a documentation directory or a directory +with maintenance scripts like dump and restore database that will be ignored by exeiac. +## Writing brick.yml File -## Writing brick.yml file - -In each elementary brick directory you have to write a brick.yml file. -It needs to contains thoose fields: -- **version**: the version of the file. At the moment we are in 1.0.0 -- **module**: the module name or a relative path to the module beginning by - ./ -- **dependencies**: a dictionnary of input values needed for execute the brick. +In each elementary brick directory, you have to write a brick.yml file. +It needs to contain these fields: +- **version**: the version of the file. At the moment, we are in 1.0.0 +- **module**: the module name or a relative path to the module beginning by ./ +- **dependencies**: a dictionary of input values needed to execute the brick. - **from**: the format is brickname:source:jsonpath - _brickname_: the brick name of the dependencies - _source_: can be *"output"* or *"event"* - - _jsonpath_: the json path of the value for example: *$.network.ip_range* - - **needed_for**: list of actions that needs that dependency. - The default is setted in exeiac configuration files - - **not_needed_for**: list of actions that don't needs that dependency. - The default is setted in exeiac configuration files + - _jsonpath_: the JSON path of the value, for example: *$.network.ip_range* + - **needed_for**: list of actions that need that dependency. The default is + set in exeiac configuration files + - **not_needed_for**: list of actions that don't need that dependency. The + default is set in exeiac configuration files - **triggered_actions**: what action will be triggered by that dependency. Default is lay - **trigger_type**: default is classic. Can be: - - _classic_: if the value change it will trigger the action in *trigger_action* - To use if the input is also a spec of your brick - - _weak_: if the value change it won't trigger anything. - To use if the input is needed for execution but is not a spec of your brick. - For example the hop ssh server credentials used to connect to your VM. - - It can also be a special trigger type usually used with events. It will be a - function as: + - _classic_: if the value changes, it will trigger the action in + *trigger_action*. To use if the input is also a spec of your brick + - _weak_: if the value changes, it won't trigger anything. To use if the + input is needed for execution but is not a spec of your brick. For example, + the hop SSH server credentials used to connect to your VM. + - It can also be a special trigger type usually used with events. It will + be a function as: - is_true() - is_false() - is_not_null() @@ -76,9 +76,8 @@ It needs to contains thoose fields: - not_contains(string) - match(string) - not_match(string) - - **inputs**: how to present dependencies value to the module. see below - **type**: can be *env_vars* or *file* - **format**: can be *env* or *json* - - **path**: the relative path if it's type file else let to default "" + - **path**: the relative path if it's type file else left to default "" - **datas**: the list of the dependencies keys diff --git a/docs/howto_write_configuration_file.md b/docs/howto_write_configuration_file.md new file mode 100644 index 0000000..272a3e1 --- /dev/null +++ b/docs/howto_write_configuration_file.md @@ -0,0 +1,60 @@ +```markdown +# Write Your Brick + +## Generality + +A brick is an element of your infra and its IaC representation. It can be a Terraform state, an Ansible playbook, a Helm chart, a combination of all those things, or even other elements depending on your brick's module. + +A brick is represented by a directory. + +But a brick is not only the code describing the real infra element. The elements describing a brick are: +- code: content of the directory +- outputs: the state of the brick, the values taken by things declared in codes as generated passwords, given public IP addresses, or just some values that can be used by other bricks +- inputs: describe the context of the brick, it is composed of parts of outputs of other bricks +- module events: output of the module action, they are describing what has been done not the state of the brick (that is described by outputs) +- triggers: input change or other brick module's event that will trigger a lay or another action on the brick + +**Note: Elementary Bricks vs Higher-Order Bricks:** Every brick is represented by a directory. Some bricks contain other bricks and some others just contain a brick.yml file that describes its module, inputs, triggers... + +## Directory Convention + +You can identify bricks from their path: +- A brick must be in a "room" (listed in [exeiac configuration file](./howto_write_configuration_file.md)) or be a room. +- A brick directory name must begin with a priority number that describes the building sequence of bricks inside the same room or super brick. + +**Note: Why the Priority Number?** Actually, it's redundant with dependency information, and exeIaC should be able to build its valid laying sequence. But we wanted to have a convention that lets humans easily partially guess some layer and dependency tree. It is also useful to identify easily what directory is a brick. Based on this, you can add a documentation directory or a directory with maintenance scripts like dump and restore database that will be ignored by exeiac. + +## Writing brick.yml File + +In each elementary brick directory, you have to write a brick.yml file. It needs to contain these fields: +- **version**: the version of the file. At the moment, we are in 1.0.0 +- **module**: the module name or a relative path to the module beginning by ./ +- **dependencies**: a dictionary of input values needed to execute the brick. + - **from**: the format is brickname:source:jsonpath + - _brickname_: the brick name of the dependencies + - _source_: can be "output" or "event" + - _jsonpath_: the JSON path of the value, for example: *$.network.ip_range* + - **needed_for**: list of actions that need that dependency. The default is set in exeiac configuration files + - **not_needed_for**: list of actions that don't need that dependency. The default is set in exeiac configuration files + - **triggered_actions**: what action will be triggered by that dependency. Default is lay + - **trigger_type**: default is classic. Can be: + - _classic_: if the value changes, it will trigger the action in *trigger_action*. To use if the input is also a spec of your brick + - _weak_: if the value changes, it won't trigger anything. To use if the input is needed for execution but is not a spec of your brick. For example, the hop SSH server credentials used to connect to your VM. + - It can also be a special trigger type usually used with events. It will be a function as: + - is_true() + - is_false() + - is_not_null() + - is_null() + - is_equal(string) + - is_different(string) + - contains(string) + - not_contains(string) + - match(string) + - not_match(string) +- **inputs**: how to present dependencies value to the module. see below + - **type**: can be env_vars or file + - **format**: can be env or json + - **path**: the relative path if it's type file else left to default "" + - **datas**: the list of the dependencies keys +``` +Ce texte peut être copié et collé dans un fichier Markdown pour être utilisé. \ No newline at end of file diff --git a/docs/howto_write_module.md b/docs/howto_write_module.md index 7946b9c..fb7a2eb 100644 --- a/docs/howto_write_module.md +++ b/docs/howto_write_module.md @@ -1,36 +1,21 @@ -# Writting your module +# Writing Your Module ## Generality -A module is an executable that will be called by exeIaC to run an action. -The module will implement an interface. Some of the method of this interface -have to be implemented some other can be omitted. You can also overload this -interface with some other actions. -These actions will be describe more precisely later but here some : -- **describe_module_for_exeiac**: mandatory, used by exeIaC to get action - implemented by the module. -- **lay**: mandatory, to deploy or correct drift on a brick -- **remove**: mandatory, rollback a lay. Is less mandatory than the 2 previous - action, because you can manage your infra by removing by hand. -- **output**: display output information of a brick - (mandatory if you want some brick depends of brick using this module) -- **plan**: check if a lay is needed and/or what the lay will do -- **help**: display a specific help for the module or more specific for the - brick -- **init**: install or check all prerequisite for the module -- **clean**: remove all files created by the module -- **validate_code**: validate teh syntax of the brick's code +A module is an executable that will be called by exeIaC to run an action. The module will implement an interface. Some of the methods of this interface have to be implemented, while others can be omitted. You can also overload this interface with additional actions. These actions will be described more precisely later, but here are some examples: +- **describe_module_for_exeiac**: mandatory, used by exeIaC to get actions implemented by the module. +- **lay**: mandatory, to deploy or correct drift on a brick. +- **remove**: mandatory, rollback a lay. This action is less mandatory than the previous two because you can manage your infra by removing manually. +- **output**: display output information of a brick (mandatory if you want some brick depends on a brick using this module). +- **plan**: check if a lay is needed and/or what the lay will do. +- **help**: display specific help for the module or more specific for the brick. +- **init**: install or check all prerequisites for the module. +- **clean**: remove all files created by the module. +- **validate_code**: validate the syntax of the brick's code. - ... it can be overloaded -**Note: module actions aren't exeiac commands !** -You may recognize some exeIaC actions but they are not exactly the -same. For example the module lay can be just a _terraform apply_. The exeIaC -_exeiac lay_ will of course execute the module lay but before it will search -all the bricks it needs to output to get the input of the lay. Then it will -output the current brick and register this output and after the module lay -it will output again the current brick to compare the output before and after -and say if it has changed. But happily it's exeIaC that do all of that, you -just have to code the module lay and output and specify the inputs needed. +**Note: module actions aren't exeiac commands!** +You may recognize some exeIaC actions, but they are not exactly the same. For example, the module lay can be just a _terraform apply_. The exeIaC _exeiac lay_ will of course execute the module lay, but before it will search all the bricks it needs to output to get the input of the lay. Then it will output the current brick and register this output and after the module lay, it will output again the current brick to compare the output before and after and determine if it has changed. But happily it's exeIaC that does all of that; you just have to code the module lay and output and specify the inputs needed. So you haven't to implement: - show @@ -39,96 +24,74 @@ So you haven't to implement: ## Think to specify your module in your exeIaC and bricks conf -Find a name to your module. It will be used in : +Find a name for your module. It will be used in: - brick.yml files: at field .module - exeIaC conf: at field modules[].name and specify the full path of your module +## Module's Inputs Conventions -## Module's inputs conventions - -- **Brick path**: before call module exeIaC will change directory to brick path -- **Action**: module takes one positionnal argument that is the *action* name -- **Options**: module can takes some other options as following - - --non-interactive (is passed to module by exeiac interpretating -i, - -interactive, -I or --non-interactive options) - - other option of your invention can be passed with -o or --other-options -- **exeIaC env variables**: exeIaC define some env variables that can be used - by modules +- **Brick Path**: before calling module exeIaC will change directory to the brick path. +- **Action**: module takes one positional argument that is the *action* name. +- **Options**: module can take some other options as follows: + - --non-interactive (is passed to module by exeiac interpreting -i, -interactive, -I or --non-interactive options). + - other options of your invention can be passed with -o or --other-options. +- **exeIaC Env Variables**: exeIaC defines some env variables that can be used by modules: - EXEIAC_BRICK_PATH - EXEIAC_BRICK_NAME - EXEIAC_ROOM_PATH - EXEIAC_ROOM_NAME - EXEIAC_MODULE_PATH - EXEIAC_MODULE_NAME -- **Brick inputs**: brick can define some inputs. It can be a file or env vars - (look at howto_write_brick_yaml.md) - +- **Brick Inputs**: brick can define some inputs. It can be a file or env vars (look at howto_write_brick_yaml.md). -## Module's outputs conventions +## Module's Outputs Conventions -The different actions have different behaviour. For some the stdout will be -displayed, for other it will be registered inside. For some the status code -will be interpretated, for some other not. +The different actions have different behavior. For some, the stdout will be displayed; for others, it will be registered inside. For some, the status code will be interpreted; for some others not. ### Stdout -Except for *describe_module_for_exeiac* and *output* where the stdout will be -consumed by exeIaC (and so need to follow some convention), all other actions -will just display the stdout without modify it. +Except for *describe_module_for_exeiac* and *output*, where the stdout will be consumed by exeIaC (and so need to follow some convention), all other actions will just display the stdout without modifying it. -For output and describe_module_for_exeiac, the expected format is json. -For output no other convention is needed, for describe_module_for_exeiac, check -the proper part +For output and describe_module_for_exeiac, the expected format is json. For output, no other convention is needed. For describe_module_for_exeiac, check the proper part. ### Stderr -The stderr will always be displayed without beeing consumed +The stderr will always be displayed without being consumed. -### Status code +### Status Code -By default the satus code understanding will be that -- 0: when action succeed -- 1-255: when a problem occurs (it will leads exeIaC to stop runs) +By default, the status code understanding will be that: +- 0: when the action succeeds. +- 1-255: when a problem occurs (it will lead exeIaC to stop runs). -But you can define some events that won't be consider as a fail for an action -by setting diplayed with describe_module_for_exeiac. -For example 0 correspond to no drift and 2 correspond there is/was a drift. +But you can define some events that won't be considered as a fail for an action by setting displayed with describe_module_for_exeiac. For example, 0 corresponds to no drift and 2 corresponds to there is/was a drift. ### describe_module_for_exeiac stdout -It displays a dictionnary of implemented actions: -Each action is a dictionnary with thoose fields: -- behaviour: that can be ommitted nowaday because exeIaC only implement one - valid default behaviour by action. But in may change in future. -- status_code_fail: default is "1-255" but you can change it -- events: dictionnary that contain thoose fields +It displays a dictionary of implemented actions: Each action is a dictionary with those fields: +- behaviour: that can be omitted nowadays because exeIaC only implements one valid default behavior by action. But it may change in the future. +- status_code_fail: default is "1-255" but you can change it. +- events: dictionary that contains those fields: - type: - - *status_code*: boolean set to true if it correspond to field - *status_code* - - *file*: string represent the content of the file - - *json*: the content of the file can represent whatever you want as - long as it is in json format - - *yaml*: the content of the file can represent whatever you want as - long as it is in yaml format - - status_code: only for type=*status_code* that is a number sequence as - "2,5-10" - - path: only for type=*file*, *json*, *yaml*. It can be a full path or a relative - path from the brick path. It can also be a classic file or a named pipe. - - For plan exeiac can interpret some special event to know if there is a drift - or not: + - *status_code*: boolean set to true if it corresponds to the field status_code. + - *file*: string represent the content of the file. + - *json*: the content of the file can represent whatever you want as long as it is in json format. + - *yaml*: the content of the file can represent whatever you want as long as it is in yaml format. + - status_code: only for type=*status_code* that is a number sequence as "2,5-10". + - path: only for type=*file*, *json*, *yaml*. It can be a full path or a relative path from the brick path. It can also be a classic file or a named pipe. + + For plan exeiac can interpret some special event to know if there is a drift or not: - for type *status_code*: - exeiac_plan_no_drift - exeiac_plan_drift - exeiac_plan_unknown - - for type *file*, *json* or *yaml*: the event name should be exeiac_plan - and the content should be in equal to: + - for type *file*, *json* or *yaml*: the event name should be exeiac_plan and the content should be in equal to: - no_drift - drift - unknown - A success will be considered as an exeiac_plan_unkown except if a previous event - is catched. + A success will be considered as an exeiac_plan_unknown except if a previous event is caught. + Example 1: ```json diff --git a/docs/illustrations/brick_elements.drawio.png b/docs/illustrations/brick_elements.drawio.png new file mode 100644 index 0000000..24b7421 Binary files /dev/null and b/docs/illustrations/brick_elements.drawio.png differ diff --git a/docs/illustrations/brick_elements_dependency.drawio.png b/docs/illustrations/brick_elements_dependency.drawio.png new file mode 100644 index 0000000..273214c Binary files /dev/null and b/docs/illustrations/brick_elements_dependency.drawio.png differ diff --git a/docs/illustrations/infra_example.drawio.png b/docs/illustrations/infra_example.drawio.png new file mode 100644 index 0000000..da74cb1 Binary files /dev/null and b/docs/illustrations/infra_example.drawio.png differ