1 - Overview

Komandan is a server automation tool designed for simplicity and ease of use. It leverages the Lua programming language to provide a flexible and powerful interface for managing remote servers. Inspired by Ansible, Komandan operates over SSH, eliminating the need for agents on managed servers.

Key Features

  • Agentless Operation: Connects to target servers via SSH, requiring no additional software installation on the managed nodes.
  • Lua-Based Scripting: Utilizes Lua for scripting, offering a lightweight yet powerful language for automation tasks.
  • Modular Design: Provides built-in modules for common tasks such as executing commands (cmd), running scripts (script), uploading files (upload), and downloading files (download).
  • Built-in Functions: Offers helper functions like komandan.filter_hosts for filtering hosts based on names or tags and komandan.set_defaults for setting default values for connection parameters.
  • Ansible-Inspired Approach: Follows Ansible’s philosophy of simplicity and agentless operation, making it easy to learn and use.

How Komandan Works

Komandan executes Lua scripts that define tasks to be performed on remote servers. The komando function is the core of Komandan, responsible for establishing SSH connections and executing modules on target hosts.

Typical Workflow

  1. Define Hosts: Specify the target servers’ details, including address, port, username, and private key path.
  2. Create Tasks: Define tasks using Komandan’s built-in modules, such as executing commands or transferring files.
  3. Run Scripts: Execute the Lua script using the komandan command, which processes the tasks and performs the specified actions on the target hosts.

Benefits of Using Komandan

  • Simplified Server Management: Automates repetitive tasks, making server management more efficient.
  • Easy to Learn: Leverages the simple and intuitive Lua scripting language.
  • Flexible and Extensible: Allows for the creation of custom scripts and modules to meet specific needs.
  • Agentless Architecture: Reduces the complexity of managing remote servers by eliminating the need for agent software.

Komandan is a powerful tool for automating server management tasks. Its simple design, combined with the flexibility of Lua scripting, makes it an excellent choice for both novice and experienced users.

Getting Started

To learn how to install and start using Komandan, refer to the Getting Started guide.

2 - Getting Started

To get started with Komandan, you first need to install it on your system. Komandan is written in Rust, so you will need to have the Rust toolchain installed.

Installation

Pre-built binaries for Komandan are available for Linux on the GitHub Releases page.

An installation script is provided for easy installation:

curl -fsSL https://raw.githubusercontent.com/hahnavi/komandan/main/install.sh | sh

This script will download the latest Komandan release for your system and install Komandan to $HOME/.local/bin. Please make sure that $HOME/.local/bin is in your $PATH environment variable.

After the installation is complete, you can verify it by running:

komandan --version

This command should print the version of Komandan that you have installed.

Basic Usage

Once Komandan is installed, you can start using it to manage your remote servers. Here’s a basic example:

  1. Create a Lua Script: Create a new file named main.lua (or any other name with a .lua extension) with the following content:

    local host = {
      address = "your_server_ip",
      user = "your_username",
      private_key_file = os.getenv("HOME") .. "/.ssh/id_rsa", -- Replace with your private key path
    }
    
    local task = {
        name = "Execute a simple command",
        komandan.modules.cmd({
            cmd = "hostname"
        })
    }
    
    komandan.komando(host, task)
    

    Replace "your_server_ip", "your_username", and the private_key_file with your server’s IP address, your username, and the path to your SSH private key, respectively.

  2. Run the Script: Execute the script using the following command:

    komandan main.lua
    

    This command will connect to the specified server, execute the hostname command, and print the output.

This is a very basic example, but it demonstrates the fundamental principles of using Komandan. You can explore the Modules and Built-in Functions sections to learn more about the available features and how to use them.

komandan.komando function

The komando function is a function to execute a task on a host. It takes two arguments:

  • host: a table that contains the following fields:
    • address: the IP address or hostname of the target server.
    • port: the SSH port to use for the connection (default is 22).
    • user: the username to connect to the server.
    • private_key_file: the path to the private key file for authentication.
    • private_key_pass: the passphrase for the private key file (optional).
    • password: the password to use for authentication if no private key is provided.
    • known_hosts_file: the path to the known_hosts file (optional).
    • host_key_check: a boolean that indicates whether to check the host key (default is true).
  • task: a table that contains the following fields:
    • name: a string that describes the task. It is used for logging purposes. (optional)
    • module: a table that contains the module to be executed and its arguments. This field is defined without a key.
    • elevate: a boolean that indicates whether to run the task with elevated privileges. (default is false)
    • as_user: a string that contains the username to run the task as. (optional)
    • ignore_exit_code: a boolean that indicates whether to ignore the exit code of the task. If true, the script will continue even if the task returns a non-zero exit code. (default is false)

This function will execute the module on the target server and return the results:

  • stdout: a string that contains the standard output of the module.
  • stderr: a string that contains the standard error output of the module.
  • exit_code: an integer that contains the exit code of the module.

3 - Modules

Komandan has several built-in modules that can be used to perform various tasks on the target server.

3.1 - cmd

The cmd module allows you to execute a shell command on the target server.

Arguments

  • cmd: a string that contains the shell command to be executed.

Usage Example

local task = {
    name = "Execute a command",
    komandan.modules.cmd({
        cmd = "ls -l"
    })
}

komandan.komando(host, task)

3.2 - script

The script module allows you to execute a script on the target server.

Arguments

  • script: a string that contains the script to be executed.
  • from_file: a string that contains the local path to the script file to be executed on the target server. (script and from_file parameters are mutually exclusive)
  • interpreter: a string that specifies the interpreter to use for the script. If not specified, the script will be executed using the default shell.

Usage Example

local task = {
    name = "Execute a script",
    komandan.modules.script({
        script = "print('Hello from script')",
        interpreter = "python"
    })
}

komandan.komando(host, task)

3.3 - upload

The upload module allows you to upload a file to the target server.

Arguments

  • src: a string that contains the path to the file to be uploaded.
  • dst: a string that contains the path to the destination file on the target server.

Usage Example

local task = {
    name = "Upload a file",
    komandan.modules.upload({
        src = "/path/to/local/file",
        dst = "/path/to/remote/file"
    })
}

komandan.komando(host, task)

3.4 - download

The download module allows you to download a file from the target server.

Arguments

  • src: a string that contains the path to the file to be downloaded.
  • dst: a string that contains the path to the destination file on the local machine.

Usage Example

local task = {
    name = "Download a file",
    komandan.modules.download({
        src = "/path/to/remote/file",
        dst = "/path/to/local/file"
    })
}

komandan.komando(host, task)

3.5 - apt

The apt module allows you to install an APT package on the target server.

Arguments

  • package: a string that contains the name of the package to be installed.
  • action: a string that specifies the action to be taken on the package. (default is install. Supported actions: install, remove, purge, upgrade, autoremove)
  • update_cache: a boolean that indicates whether to update the package cache before installing the package. (default is false)
  • install_recommends: a boolean that indicates whether to install recommended packages. (default is true)

Usage Example

local task = {
    name = "Install apache2",
    komandan.modules.apt({
        package = "apache2",
        update_cache = true
    })
}

komandan.komando(host, task)

3.6 - lineinfile

The lineinfile module allows you to modify a file by adding or removing lines.

Arguments

  • path: a string that contains the path to the file to be modified.
  • line: a string that contains the line to be added or removed.
  • state: a string that contains the state of the line. Can be “present” or “absent”. (default is “present”)
  • pattern: a string that contains the pattern (regular expression) to search for in the file. (optional)
  • insert_after: a string that contains the line to insert the new line after. (optional)
  • insert_before: a string that contains the line to insert the new line before. (optional)
  • create: a boolean that indicates whether to create the file if it does not exist. (default is false)
  • backup: a boolean that indicates whether to create a backup of the file before modifying it. (default is false)

Usage Example

local task = {
    name = "Add a line in a file",
    komandan.modules.lineinfile({
        path = "/tmp/target_file.txt",
        line = "This is a new line",
        state = "present",
        create = true,
        backup = true,
    }),
}

3.7 - template

The template module allows you to create a file using a jinja template.

Arguments

  • src: a string that contains the path to the template file on the local machine.
  • dst: a string that contains the path to the destination file on the target server.
  • vars: a table that contains the variables to be used in the template.

Usage Example

local task = {
    name = "Create a file using a template",
    komandan.modules.template({
        src = "/path/to/template.jinja",
        dst = "/tmp/target_file.txt",
        vars = {
            name = "John Doe",
            age = 30,
        },
    }),
}

4 - Functions

Komandan provides several built-in functions that can be used to help write scripts.

4.1 - komando

The komando function is a function to execute a task on a host. It takes two arguments:

  • host: a table that contains the following fields:
    • address: the IP address or hostname of the target server.
    • port: the SSH port to use for the connection (default is 22).
    • user: the username to connect to the server.
    • private_key_file: the path to the private key file for authentication.
    • private_key_pass: the passphrase for the private key file (optional).
    • password: the password to use for authentication if no private key is provided.
  • task: a table that contains the following fields:
    • name: a string that describes the task. It is used for logging purposes. (optional)
    • module: a table that contains the module to be executed and its arguments. This field is defined without a key.
    • elevate: a boolean that indicates whether to run the task with elevated privileges. (default is false)
    • as_user: a string that contains the username to run the task as. (optional)
    • ignore_exit_code: a boolean that indicates whether to ignore the exit code of the task. If true, the script will continue even if the task returns a non-zero exit code. (default is false)
    • env: a table that contains environment variables to be set for the task. (optional)

Usage Example

local host = {
  address = "10.20.30.41",
  user = "user1",
  private_key_file = "/path/to/private/key",
}

local task = {
  name = "install neovim package",
  komandan.modules.apt({
    package = "neovim",
    update_cache = true
  }),
  elevate = true,
}

komandan.komando(host, task)

This function will execute the module on the target server and return the results:

  • stdout: a string that contains the standard output of the module.
  • stderr: a string that contains the standard error output of the module.
  • exit_code: an integer that contains the exit code of the module.

4.2 - komando_parallel_hosts

The komando_parallel_hosts function is a function to execute a task on multiple hosts in parallel. It takes two arguments:

  • hosts: a table that contains the hosts to execute the task on. Each host is represented by a table that contains the following fields:
    • address: the IP address or hostname of the target server.
    • port: the SSH port to use for the connection (default is 22).
    • user: the username to connect to the server.
    • private_key_file: the path to the private key file for authentication.
    • private_key_pass: the passphrase for the private key file (optional).
    • password: the password to use for authentication if no private key is provided.
  • task: a table that contains the following fields:
    • name: a string that describes the task. It is used for logging purposes. (optional)
    • module: a table that contains the module to be executed and its arguments. This field is defined without a key.
    • elevate: a boolean that indicates whether to run the task with elevated privileges. (default is false)
    • as_user: a string that contains the username to run the task as. (optional)
    • ignore_exit_code: a boolean that indicates whether to ignore the exit code of the task. If true, the script will continue even if the task returns a non-zero exit code. (default is false)
    • env: a table that contains environment variables to be set for the task. (optional)

Usage Example

local hosts = {
  {
    address = "10.20.30.41",
    user = "user1",
    private_key_file = "/path/to/private/key",
  },
  {
    address = "10.20.30.42",
    user = "user2",
    private_key_file = "/path/to/private/key",
  },
  {
    address = "10.20.30.43",
    user = "user3",
    private_key_file = "/path/to/private/key",
  }
}

local task = {
  name = "install neovim package",
  komandan.modules.apt({
    package = "neovim",
    update_cache = true
  }),
  elevate = true,
}

komandan.komando_parallel_hosts(hosts, task)

This function will execute the module on the target server and return a table with the following results:

  • stdout: a string that contains the standard output of the module.
  • stderr: a string that contains the standard error output of the module.
  • exit_code: an integer that contains the exit code of the module.

4.3 - komando_parallel_tasks

The komando_parallel_hosts function is a function to execute multiple task on a host in parallel. It takes two arguments:

  • host: a table that contains the following fields:
    • address: the IP address or hostname of the target server.
    • port: the SSH port to use for the connection (default is 22).
    • user: the username to connect to the server.
    • private_key_file: the path to the private key file for authentication.
    • private_key_pass: the passphrase for the private key file (optional).
    • password: the password to use for authentication if no private key is provided.
  • tasks: a table that contains the tasks to be executed on the target host. Each task is represented by a table that contains the following fields:
    • name: a string that describes the task. It is used for logging purposes. (optional)
    • module: a table that contains the module to be executed and its arguments. This field is defined without a key.
    • elevate: a boolean that indicates whether to run the task with elevated privileges. (default is false)
    • as_user: a string that contains the username to run the task as. (optional)
    • ignore_exit_code: a boolean that indicates whether to ignore the exit code of the task. If true, the script will continue even if the task returns a non-zero exit code. (default is false)
    • env: a table that contains environment variables to be set for the task. (optional)

Usage Example

local host = {
  address = "10.20.30.41",
  user = "user1",
  private_key_file = "/path/to/private/key",
}

local tasks = {
  {
    name = "install neovim package",
    komandan.modules.apt({
      package = "neovim",
      update_cache = true
    }),
    elevate = true,
  },
  {
    name = "mkdir",
    komandan.modules.cmd({
      cmd = "mkdir /tmp/new_dir"
    })
  },
  {
    name = "ls",
    komandan.modules.cmd({
      cmd = "ls -hal"
    })
  }
}

komandan.komando_parallel_tasks(host, tasks)

This function will execute the module on the target server and return a table with the following results:

  • stdout: a string that contains the standard output of the module.
  • stderr: a string that contains the standard error output of the module.
  • exit_code: an integer that contains the exit code of the module.

4.4 - filter_hosts

The filter_hosts function takes two arguments:

  • hosts: a table that contains the hosts to filter.
  • pattern: a string that contains the name or tag to filter the hosts. It can be a regular expression by adding ~ at the beginning of the pattern.

The function returns a table that contains the filtered hosts.

Usage Example

local hosts = {
  {
    name = "server1",
    address = "10.20.30.41",
    tags = { "webserver", "database" },
  },
  {
    name = "server2",
    address = "10.20.30.42",
    tags = { "webserver" },
  },
  {
    name = "server3",
    address = "10.20.30.43",
    tags = { "database" },
  },
}

local filtered_hosts = komandan.filter_hosts(hosts, "webserver")

This will return the table filtered_hosts that contains only the hosts that have the name or tag webserver.

4.5 - parse_hosts_json_file

The parse_hosts_json_file function takes one argument:

  • src: a string that contains the path to the JSON file.

The function returns a table that contains the parsed hosts.

Usage Example

hosts.json:

[
  {
    "name": "server1",
    "user": "user1",
    "address": "10.20.30.41",
    "tags": ["webserver", "database"]
  },
  {
    "name": "server2",
    "user": "user2",
    "address": "10.20.30.42",
    "tags": ["webserver"]
  },
  {
    "name": "server3",
    "user": "user3",
    "address": "10.20.30.43",
    "tags": ["database"]
  }
]

main.lua:

local hosts = komandan.parse_hosts_json_file("/path/to/hosts.json")

This will return the table hosts that contains the parsed hosts.

4.6 - parse_hosts_json_url

The parse_hosts_json_url function takes one argument:

  • src: a http/https URL that contains the path to the JSON file.

The function returns a table that contains the parsed hosts.

Usage Example

main.lua:

local hosts = komandan.parse_hosts_json_url("https://example.com/hosts.json")

This will return the table hosts that contains the parsed hosts.

4.7 - Default Values

Komandan provides default values for various parameters, such as the user, private key file path, and SSH port. These values can be set using the komandan.defaults userdata using setters.

-- set default values
komandan.defaults:set_port(22)
komandan.defaults:set_user("user1")
komandan.defaults:set_private_key_file(os.getenv("HOME") .. "/.ssh/id_ed25519")
komandan.defaults:set_private_key_pass("passphrase")
komandan.defaults:set_host_key_check(false)
komandan.defaults:set_env("ENV_VAR", "value")

-- get default values
local port = komandan.defaults:get_port()
local user = komandan.defaults:get_user()
local private_key_file = komandan.defaults:get_private_key_file()
local private_key_pass = komandan.defaults:get_private_key_pass()
local host_key_check = komandan.defaults:get_host_key_check()
local env = komandan.defaults:get_env("ENV_VAR")
local env_all = komandan.defaults:get_all_env()

5 - Custom Module

Komandan allows you to create your own modules for Komandan task that can be used to extend the functionality of Komandan.

5.1 - Create a Custom Module

To create a custom module, you can create a function that returns a komandan.KomandanModule object. The argument should contain the name field of the module.

The main function of the module is the run function. This function should contain the code that will be executed. To send a command or another action to the target server, you can use the helper functions provided by Komando. (see Module Helper Functions)

To create a cleanup function, you can use the cleanup function. This function will be executed after the run function.

Example of a Custom Module

function my_module(params)
  let module = komandan.KomandanModule:new({ name = "My Module", params = params })

  module.run = function(self)
    self.ssh:cmd("mkdir /tmp/" .. self.params.dirname)
  end

  module.cleanup = function(self)
    -- Cleanup code
  end

  return module
end

In the example above, the my_module function returns a komandan.KomandanModule object. The name field of the module is set to “My Module”. The run function of the module creates a directory on the target server with name from the dirname argument.

Usage Example of a Custom Module

local host = {
  address = "10.20.30.41",
  user = "user1",
}

local task = {
  name = "Run custom module",
  my_module({
    dirname = "test"
  })
}

komandan.komando(host, task)

5.2 - Module Helper Functions

Komandan provides several helper functions that can be used to send commands or other actions to the target server from a custom module. (see Create a Custom Module)

module.ssh:cmd

This function sends a command to the target server using SSH. The function takes one arguments:

  • cmd: a string that contains the shell command to be executed.

The function returns a table with the following fields:

  • exit_code: an integer that contains the exit code of the command.
  • stdout: a string that contains the standard output of the command.
  • stderr: a string that contains the standard error of the command.

module.ssh:write_remote_file

This function writes a file to the target server using SSH. The function takes two arguments:

  • remote path: a string that contains the path to the destination file on the target server.
  • content: a string that contains the content to be written to the file.

The function does not return any value.

module.ssh:upload

This function uploads a file to the target server using SSH. The function takes two arguments:

  • local path: a string that contains the path to the local file to be uploaded.
  • remote path: a string that contains the path to the destination file on the target server.

The function does not return any value.

module.ssh:download

This function downloads a file from the target server using SSH. The function takes two arguments:

  • remote path: a string that contains the path to the remote file to be downloaded.
  • local path: a string that contains the path to the destination file on the local machine.

The function does not return any value.

module.ssh:get_remote_env

This function gets the environment variables from the target server using SSH. The function takes one argument:

  • var: a string that contains the name of the environment variable to be retrieved.

The function returns a string that contains the value of the environment variable.

module.ssh:get_tmpdir

This function returns the path to the temporary directory for Komandan on the target server. The function does not take any arguments. The function returns a string that contains the path to the temporary directory. The default tmpdir is $HOME/.komandan/tmp, otherwise it will be /tmp/komandan.

module.ssh:chmod

This function changes the permissions of a file on the target server using SSH. The function takes two arguments:

  • remote_path: a string that contains the path to the file to be changed.
  • mode: a string that contains the permissions to be set.

6 - Examples

Komandan provides several examples that can be used to help you get started with Komandan.

6.1 - Simple Task

This example shows how to use the komando function to execute a simple command task on a remote server.

local host = {
    address = "10.20.30.41",
    user = "user1",
    private_key_file = "/path/to/private/key",
}

local task = {
    name = "Create a new directory",
    komandan.modules.cmd({
        cmd = "mkdir /tmp/new_dir"
    })
}

komandan.komando(host, task)

6.2 - Multi Host and Task

This example shows how to use multiple hosts and tasks in using Komandan.

hosts.lua:

return {
  {
    name = "server1",
    address = "10.20.30.41",
    user = "user2",
    tags = { "webserver" },
  },
  {
    name = "server2",
    address = "10.20.30.42",
    user = "user2",
    tags = { "dbserver" },
  },
  {
    address = "10.20.30.43",
    private_key_file = os.getenv("HOME") .. "/.ssh/id_ed25519",
    tags = { "dbserver" },
  },
}

main.lua:

local hosts = require("hosts")

komandan.set_defaults({
  user = "user1",
  private_key_file = os.getenv("HOME") .. "/.ssh/id_ed25519",
})

local tasks = {
  {
    name = "Create a directory",
    komandan.modules.cmd({
      cmd = "mkdir /tmp/newdir",
    }),
  },
  {
    name = "Delete a directory",
    komandan.modules.cmd({
      cmd = "rm -rf /tmp/newdir",
    }),
  },
}

local filtered_hosts = komandan.filter_hosts(hosts, "dbserver")

for _, task in pairs(tasks) do
  for _, host in pairs(filtered_hosts) do
    komandan.komando(host, task)
  end
end

This example will create a directory on all hosts that has name or tag dbserver and then delete the directory.