Extending ModusKube
ModusKube is designed to be used as a framework for building custom infrastructure. The extension methods discussed below can be used to customize ModusKube to fit your needs in a clean and modular way.
Cluster state extensions
Cluster state extensions let you run arbitrary code during cluster bootstrapping and run on the master node directly. You can use this extension method to install custom Kubernetes resources, Helm charts, tools or anything else that needs to be run during cluster bootstrapping.
Once defined in your cluster configuration, these extensions will be stored as part of the cluster’s state definition during the creation process. If the cluster is replaced, it will automatically run all of its defined state extensions when re-provisioned.
Creating a state extension
State extensions can be added to a cluster configuration with the extra_cluster_addons variable. Like other cluster configuration settings, this can be overridden in a config file and passed to cluster commands like create or replace with the -f option:
# /shared/my-addons.yaml
extra_cluster_addons:
- name: my-addon
enabled: yes
install_script: |
#!/usr/bin/env bash
echo "Hello from my addon"
$ moduskube cluster create -f /shared/my-addons.yaml
State extensions can also include text files that can be referenced within the install_script section. The extension below uses kubectl to install a Kubernetes StorageClass resource by pointing to its YAML manifest included in the files section of the extension:
# /shared/my-addons.yaml
extra_cluster_addons:
- name: storageclass
enabled: yes
install_script: |
#!/usr/bin/env bash
set -eo pipefail
kubectl apply -f files/standard-storageclass.yaml
files:
- name: standard-storageclass.yaml
contents: |
kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
name: standard
annotations:
storageclass.kubernetes.io/is-default-class: 'true'
provisioner: kubernetes.io/aws-ebs
parameters:
type: gp2
You can define multiple extensions within the extra_cluster_addons variable and they will be loaded in the order defined. And like all configuration variables, Jinja templating can be used to reference any other cluster configuration variable within an extension.
Built-in state extensions
ModusKube uses a number of built-in state extensions to install things like Tiller into the clusters it provisions. These base extensions are defined with the base_cluster_addons variable in /moduskube/ansible/inventory/group_vars/all/addons.yml. You shouldn’t normally need to override these extensions, but the option is available if required.
Base extensions can be enabled or disabled with a configuration variable. For example, you could disable Tiller by setting helm_tiller_enabled to no on the command line:
$ moduskube cluster create -e helm_tiller_enabled no
CLI extensions
CLI extensions let you access the ModusKube API and create powerful custom commands. A CLI extension can be as simple as a custom command that adds a useful shortcut or a complete command group hierarchy for managing complex infrastructure projects.
A common pattern is to create a CLI extension for managing custom infrastructure on top of the ModusKube API. This extension would expose a top level command group that holds multiple commands and groups for managing all aspects of the infrastructure. For an example of this, see the PortX CLI Extension.
Loading extensions
A CLI extension is any Python module or package containing top-level subclasses of click.Command or click.Group. If a module or package contains multiple commands, ModusKube will load all of them.
By default ModusKube looks for CLI extensions in /moduskube/plugins and /shared/plugins. To install an extension, you can simply copy it to /shared/plugins, or you can use ModusKube as a base image in your own Dockerfile and build the extension directly into the container by copying it to the /moduskube/plugins directory.
Important
CLI extension code must be compatible with Python 3.10.
Creating a CLI extension
To get familiar with how CLI extensions work, we’ll create a very basic “Hello World” extension that adds a single hello command to ModusKube.
First, create a Python file called hello_world.py in /shared/plugins containing the following code:
import click
@click.command()
def hello():
"""Hello World command"""
click.echo('Hello World')
Now, run moduskube to see your new hello command:
$ moduskube
Usage: moduskube [OPTIONS] COMMAND [ARGS]...
ModusKube v0.15
(C) Copyright 2018 - 2019 ModusBox, Inc.
https://github.com/modusintegration/moduskube
Options:
-f, --conf-file FILENAME Load command configuration from a YAML file
-v, --verbose
-q, --quiet
--version Show the version and exit.
-h, --help Show this message and exit.
Commands:
cloud Cloud utilities
cluster Manage clusters
hello Hello World command
ip Get IP address of cluster nodes or bastion server
lb Manage load balancers
run Run COMMAND inside ModusKube container
shell Start ModusKube shell
ssh SSH to cluster instances
tests Run tests
...
And finally, run the hello command:
$ moduskube hello
Hello World
Using the ModusKube API
CLI extensions give you access to the ModusKube API. This contrived example creates a simple create-multiple command that uses the create() method of the Cluster class to create multiple clusters:
import click
from moduskube.resources.cluster import Cluster
@click.command()
@click.option('--region', '-r', required=True)
@click.argument('clusters', nargs=-1)
def create_multiple(region, clusters):
"""Create multiple clusters"""
for name in clusters:
cluster = Cluster(name, region=region)
cluster.create()
You can then use the new create-multiple command like this:
$ moduskube create-multiple -r us-west-2 cluster-1 cluster-2 cluster-3
Importing objects from your extension
To import objects from your CLI extension in Python code you need to use the moduskube.plugins module namespace along with the name of your extension’s package. For example, a CLI extension package called hello_world would be importable in Python code as moduskube.plugins.hello_world.
This is only needed if you are building your extension as a package and want to import other parts of the package internally. For example:
from moduskube.plugins.hello_world.utils import my_function
CLI extension examples
A collection of CLI extension examples is available in the examples directory at the root of the project. For a look at a complex CLI extension used for managing production infrastructure, see the PortX CLI Extension.