#configuration-management #automation #devops #task-automation #configuration #task-management #task

duxcore

Embed an automation engine right in your code and adapt it to your specific use cases

11 releases

new 0.1.9 Nov 16, 2024
0.1.8 Nov 7, 2024
0.1.6 Sep 5, 2024
0.1.4 Aug 31, 2024
0.0.0 Jul 2, 2024

#112 in Configuration

Download history 11/week @ 2024-07-29 140/week @ 2024-08-05 241/week @ 2024-08-26 333/week @ 2024-09-02 24/week @ 2024-09-16 14/week @ 2024-09-23 8/week @ 2024-09-30 15/week @ 2024-10-28 260/week @ 2024-11-04 115/week @ 2024-11-11

390 downloads per month

Apache-2.0

1MB
3.5K SLoC

Duxcore : embed an ansible-like automation engine right in your Rust code

The goal

Instead of having one big configuration management tool trying to handle all scenarios, we prefer to build one flexible automation engine (this crate) and make it as easy as possible to embed in a codebase, ready to be adapted to one's specific need.

Documentation

A book has been opened about the Dux project. Especially, modules list and documentation can be found here.

Basic example

Import the crate

cargo add duxcore

Now let's perform the usual example : setup a webserver (but, this time, right from your Rust code !)

use duxcore::prelude::*;

fn main() {

    // First we need to define what the expected state of the target host is.
    let my_tasklist = r#"---
- name: Let's install a web server !
  steps:
    - name: First, we test the connectivity and authentication with the host.
      ping:
      
    - name: Then we can install the package...
      with_sudo: true
      apt:
        package: '{{ package_name }}'
        state: present
        
    - name: ... and start & enable the service.
      with_sudo: true
      service:
        name: '{{ service_name }}'
        state: started
        enabled: true

    - name: What date is it on this host by the way ?
      register: host_date
      command:
        content: date +%Y-%m-%d" "%Hh%M

    - name: Let's see...
      debug:
        msg: 'date: {{ host_date.output }}'
        
        "#;

    // Then we create a 'Job'.
    let mut my_job = Job::new();

    // We set who the target host of this Job is, and how to connect to it.
    my_job
        .set_address("10.20.0.203")
        .set_connection(HostConnectionInfo::ssh2_with_key_file("dux", "./controller_key")).unwrap();
    
    // We give it some context and the task list.
    my_job
        .add_var("package_name", "apache2")
        .add_var("service_name", "apache2")
        .set_tasklist_from_str(my_tasklist, TaskListFileType::Yaml).unwrap()
    ;
    // We can finally apply the task list to this host.
    my_job.apply();

    // Let's see the result.
    println!("{}", my_job.display_pretty());
}

Output

{
  "host": "10.20.0.203",
  "timestamp_start": "2024-11-15T23:14:09.114229853+00:00",
  "timestamp_end": "2024-11-15T23:14:09.864756326+00:00",
  "final_status": "ApplySuccesful",
  "tasks": [
    {
      "name": "Let's install a web server !",
      "steps": [
        {
          "name": "First, we test the connectivity and authentication with the host.",
          "expected_state": {
            "ping": {}
          },
          "status": "ApplySuccessful"
        },
        {
          "name": "Then we can install the package...",
          "expected_state": {
            "apt": {
              "state": "present",
              "package": "apache2"
            }
          },
          "status": "ApplySuccessful"
        },
        {
          "name": "... and start & enable the service.",
          "expected_state": {
            "service": {
              "name": "apache2",
              "state": "started",
              "enabled": true
            }
          },
          "status": "ApplySuccessful"
        },
        {
          "name": "What date is it on this host by the way ?",
          "expected_state": {
            "command": {
              "content": "date +%Y-%m-%d\" \"%Hh%M"
            }
          },
          "status": "ApplySuccessful"
        },
        {
          "name": "Let's see...",
          "expected_state": {
            "debug": {
              "msg": "date: 2024-11-16 00h14\n"
            }
          },
          "status": "ApplySuccessful"
        }
      ]
    }
  ]
}

This is the basic workflow of Dux. The Job type, around which the whole automation revolves, is serializable/deserializable. It is then up to you to parallelize, distribute the work, display the results in some web interface or send the workload to workers via a message broker... Whatever suits you best ! To handle multiple hosts at once, use a JobList instead.

More examples

More complex examples of how the Dux crate can be used are being built as separate projects. These are proofs of concept and can be used as a starting point for your own implementation. You can also start from scratch.

Standard implementation

One binary doing everything

Dux standard project : dux-standard

Agent implementation

A Dux agent running as a background service, regularly fetching a remote tasklist (http/https, git...) and applying it to itself

Dux agent project : dux-agent

Distributed implementation

Workload split between a controller which generates Jobs and workers which actually run them on targetted hosts

Dux distributed controller project : dux-distributed-controller
Dux distributed worker project : dux-distributed-worker

Scalable implementation

Workload split between a controller and workers nodes, with a message broker in the middle to allow scaling up and down the number of workers

Dux scalable controller project : dux-scalable-controller
Dux scalable worker project : dux-scalable-worker

Contribution

Want some help to use this crate for your own situation ? Open to suggestions, feedback, requests and any contribution ! Will gladly exchange ideas and help you build your own implementation right there !

Dependencies

~15–45MB
~726K SLoC