• Stars
    star
    654
  • Rank 66,326 (Top 2 %)
  • Language
    Go
  • License
    MIT License
  • Created over 5 years ago
  • Updated 11 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

NixOS deployment tool

morph

Build

Morph is a tool for managing existing NixOS hosts - basically a fancy wrapper around nix-build, nix copy, nix-env, /nix/store/.../bin/switch-to-configuration, scp and more. Morph supports updating multiple hosts in a row, and with support for health checks makes it fairly safe to do so.

Notable features

  • multi host support
  • health checks
  • no state

Installation and prerequisites

Morph requires nix (at least v2), ssh and scp to be available on $PATH. It should work on any modern Linux distribution, but NixOS is the only one we test on.

Pre-built binaries are not provided, since we install morph through an overlay.

The easiest way to get morph up and running is to fork this repository and run nix-build, which should result in a store path containing the morph binary. Consider checking out a specific tag, or at least pin the version of morph you're using somehow.

Using morph

All commands support a --help flag; morph --help as of v1.0.0:

$ morph --help
usage: morph [<flags>] <command> [<args> ...]

NixOS host manager

Flags:
  --help     Show context-sensitive help (also try --help-long and --help-man).
  --version  Show application version.
  --dry-run  Don't do anything, just eval and print changes

Commands:
  help [<command>...]
    Show help.

  build [<flags>] <deployment>
    Evaluate and build deployment configuration to the local Nix store

  push [<flags>] <deployment>
    Build and transfer items from the local Nix store to target machines

  deploy [<flags>] <deployment> <switch-action>
    Build, push and activate new configuration on machines according to switch-action

  check-health [<flags>] <deployment>
    Run health checks

  upload-secrets [<flags>] <deployment>
    Upload secrets

  exec [<flags>] <deployment> <command>...
    Execute arbitrary commands on machines

Notably, morph deploy requires a <switch-action>. The switch-action must be one of dry-activate, test, switch or boot corresponding to nixos-rebuild arguments of the same name. Refer to the NixOS manual for a detailed description of switch-actions.

For help on this and other commands, run morph <cmd> --help.

Example deployments can be found in the examples directory, and built as follows:

$ morph build examples/simple.nix
Selected 2/2 hosts (name filter:-0, limits:-0):
	  0: db01 (secrets: 0, health checks: 0)
	  1: web01 (secrets: 0, health checks: 0)

<probably lots of nix-build output>

/nix/store/grvny5ga2i6jdxjjbh2ipdz7h50swi1n-morph
nix result path:
/nix/store/grvny5ga2i6jdxjjbh2ipdz7h50swi1n-morph

The result path is written twice, which is a bit silly, but the reason is that only the result path is written to stdout, and everything else (including nix-build output) is redirected to stderr. This makes it easy to use morph for scripting, e.g. if one want to build using morph and then nix copy the result path somewhere else.

Note that examples/simple.nix contain two different hosts definitions, and a lot of copy paste. All the usual nix tricks can of course be used to avoid duplication.

Hosts can be deployed with the deploy command as follows: morph deploy examples/simple.nix (this will fail without modifying examples/simple.nix).

Selecting/filtering hosts to build and deploy

All hosts defined in a deployment file is returned to morph as a list of hosts, which can be manipulated with the following flags:

  • --on glob can be used to select hosts by name, with support for glob patterns
  • --limit n puts an upper limit on the number of hosts
  • --skip n ignore the first n hosts
  • --every n selects every n'th host, useful for e.g. selecting all even (or odd) numbered hosts

(all relevant commands should already support these flags.)

The ordering currently can't be changed, but should be deterministic because of nix.

Most commands output a header like this:

Selected 4/17 hosts (name filter:-6, limits:-7):
	  0: foo-p02 (secrets: 0, health checks: 1)
	  1: foo-p05 (secrets: 0, health checks: 1)
	  2: foo-p08 (secrets: 0, health checks: 1)
	  3: foo-p11 (secrets: 0, health checks: 1)

The output is pretty self explanatory, except probably for the last bit of the first line. name filter shows the change in number of hosts after glob matching on the hosts name, and limits shows the change after applying --limit, --skip and --every.

Tagging hosts

Each host can be tagged with an arbitrary amount of tags, which can be used to select and sort hosts.

To tag a host, use the deployment.tags option, e.g. deployment.tags = [ "prod" "master" "rack-17" ]. Hosts can now be selected with the --tagged option, e.g.--tagged="prod,master" will only select hosts tagged both prod and master.

To sort hosts based on tags, use the network.ordering.tags option, e.g. network.ordering.tags = [ "master" "slave"]. This ordering can be changed at runtime using the --order-by-tags option, eg. --order-by-tags="slave,master" (this also works when network.ordering.tags isn't defined). Hosts without matching tags will end up at the end of the list.

Environment Variables

Morph supports the following (optional) environment variables:

  • SSH_IDENTITY_FILE the (local) path to the SSH private key file that should be used
  • SSH_USER specifies the user that should be used to connect to the remote system
  • SSH_SKIP_HOST_KEY_CHECK if set disables host key verification
  • SSH_CONFIG_FILE allows to change the location of the ~/.ssh/config file
  • MORPH_NIX_EVAL_CMD morph will invoke this command instead of default: "nix-instantiate" on PATH
  • MORPH_NIX_BUILD_CMD morph will invoke this command instead of default: "nix-build" on PATH
  • MORPH_NIX_SHELL_CMD morph will invoke this command instead of default: "nix-shell" on PATH
  • MORPH_NIX_EVAL_MACHINES path to a custom eval-machines.nix. Defaults to the eval-machines.nix bundled with morph

Secrets

Files can be uploaded without ever ending up in the nix store, by specifying each file as a secret. This will use scp for copying a local file to the remote host.

See examples/secrets.nix or the type definitions in data/options.nix.

To upload secrets, use the morph upload-secrets subcommand, or pass --upload-secrets to morph deploy.

Note: Morph will automatically create directories parent to secret.Destination if they don't exist. New dirs will be owned by root:root and have mode 755 (drwxr-xr-x). Automatic directory creation can be disabled by setting secret.mkDirs = false.

Health checks

Morph has support for two types of health checks:

  • command based health checks, which are run on the target host (success defined as exit code == 0)
  • HTTP based health checks, which are run from the host Morph is running on (success defined as HTTP response codes in the 2xx range)

See examples/healthchecks.nix for an example.

There are no guarantees about the order health checks are run in, so if you need something complex you should write a script for it (e.g. using pkgs.writeScript). Health checks will be repeated until success, and the interval can be configured with the period option (see data/options.nix for details).

It is currently possible to have expressions like "test \"$(systemctl list-units --failed --no-legend --no-pager |wc -l)\" -eq 0" (count number of failed systemd units, fail if non-zero) as the first argument in a cmd-healthcheck. This works, but is discouraged, and might break at any time.

Advanced configuration

nix.conf-options: The "network"-attrset supports a sub-attrset named "nixConfig". Options configured here will pass --option <name> <value> to all nix commands. Note: these options apply to an entire deployment and are not configurable on per-host basis. The default is an empty set, meaning that the nix configuration is inherited from the build environment. See man nix.conf.

network.buildShell By passing --allow-build-shell and setting network.buildShell to a nix-shell compatible derivation (eg. pkgs.mkShell ...), it's possible to make morph execute builds from within the defined shell. This makes it possible to have arbitrary dependencies available during the build, say for use with nix build hooks. Be aware that the shell can potentially execute any command on the local system.

special deployment options:

(per-host granularity)

buildOnly makes morph skip the "push" and "switch" steps for the given host, even if "morph deploy" or "morph push" is executed. (default: false)

substituteOnDestination Sets the --substitute-on-destination flag on nix copy, allowing for the deployment target to use substitutes. See nix copy --help. (default: false)

Example usage of nixConfig and deployment module options:

network = {
    nixConfig = {
        "extra-sandbox-paths" = "/foo/bar";
    };
};

machine1 = { ... }: {
    deployment.buildOnly = true;
};

machine2 = { ... }: {
    deployment.substituteOnDestination = true;
};

mutually recursive configurations Each host's configuration has access to a nodes argument, which contains the compiled configurations of all hosts.

machine1 = { nodes, ... }: {
    hostnames.machine2 = 
        (builtins.head nodes.machine2.networking.interfaces.foo.ipv4.addresses).address;
    networking.interfaces.foo.ipv4.addresses = [
        {
            address = "10.0.0.10";
            prefixLength = 32;
        }
    ];
}

machine2 = { nodes, ... }: {
    hostnames.machine1 = 
        (builtins.head nodes.machine1.networking.interfaces.foo.ipv4.addresses).address;
    networking.interfaces.foo.ipv4.addresses = [
        {
            address = "10.0.0.20";
            prefixLength = 32;
        }
    ];
}

Hacking morph

All commands mentioned below is available in the nix-shell, if you run nix-shell with working dir = project root. The included shell.nix uses the latest nixos-unstable from GitHub by default, but you can override this by passing in another, eg. nix-shell --arg nixpkgs '<nixpkgs>' for your $NIX_PATH nixpkgs.

Go dependency management

From within nix-shell, go get -u updates all go modules. Remember to update the vendorSha256 in ./default.nix

Building the project with pinned dependencies

$ nix-build --arg nixpkgs "builtins.fetchTarball https://github.com/NixOS/nixpkgs/archive/<rev>.tar.gz"

About the project

We needed a tool for managing our NixOS servers, and ended up writing one ourself. This is it. We use it on a daily basis to build and deploy our NixOS fleet, and when we need a feature we add it.

Morph is by no means done. The CLI UI might (and probably will) change once in a while. The code is written by humans with an itch to scratch, and we're discussing a complete rewrite (so feel free to complain about the source code since we don't like it either). It probably wont accidentally switch your local machine, so you should totally try it out, but do consider pinning to a specific git revision.

More Repositories

1

kubernixos

Simple kubernetes manifest reconciler configured by NixOS modules
Go
19
star
2

serviceprovider

API for the danish public libraries
JavaScript
11
star
3

bibdk

PHP
7
star
4

biblo

GitHub repo for biblo.dk
JavaScript
7
star
5

passport-unilogin

Passport strategy for UNI-Login
JavaScript
5
star
6

faythe

Rust
5
star
7

artesis

Artesis installation profile
PHP
4
star
8

drush-cached-repos

Shell
4
star
9

dataio

Data handler system
Java
3
star
10

bibliotekdk-next-frontend

JavaScript
3
star
11

artesis_demo_content

Provides custom content for artesis web
PHP
3
star
12

fbi-api-gateway

JavaScript
3
star
13

dbcore

Core make file specifying which common modules in what versions both Artesis and Bibliotek.dk should download
3
star
14

drush-dbc-utils

Utilities for Drush. Release build scripts
PHP
3
star
15

suggester-laesekompas-webservice

Webservice exposing the outwards API for læsekompas suggester
Java
2
star
16

hejmdal

Adgangsplatform
JavaScript
2
star
17

ding_omega_frontend

Panels configuration for Ding Omega based sites.
PHP
2
star
18

opencat-business

JavaScript
2
star
19

rawrepo-record-service

Java
2
star
20

solr-document-store

Solr Document Store for Corepo & HoldingsItems index Documents
Java
2
star
21

rust-modules

Rust
2
star
22

opensearch-graphql-demo

Using graphql width opensearch
JavaScript
2
star
23

smaug

authentication service for DBCDK/serviceprovider
JavaScript
2
star
24

ding2omega

Base Omega subtheme
PHP
2
star
25

fcrepo-3.5-patched

writeaheadlog
Java
1
star
26

ting_openformat

PHP
1
star
27

codename-nighthawk

cmdb prototype
Python
1
star
28

dbc-node-serviceprovider

Node module that provides access to the DBC webservices through a series of client modules
JavaScript
1
star
29

mesos-tools

A collection of tools to manage mesos/marathon deployments
Python
1
star
30

pg-queue

Database based queue system
Java
1
star
31

rawrepo

Java
1
star
32

bibdk_searchhistory

Bibdk search history module
PHP
1
star
33

bibliOmega

Omega theme for Bibliotek.dk installations
JavaScript
1
star
34

ding_wayf

Addes support for WAYF login
PHP
1
star
35

merkur

JavaScript
1
star
36

bibdkcaptcha

This module extends the functionality of the CAPTHA module to support audio playback of challenges in either danish or english
PHP
1
star
37

holdings-items

Java
1
star
38

subject_hierarchy

1
star
39

bibdk_helpdesk

Provides a teaser block and a helpdesk form page for biblioteksvagten.dk
PHP
1
star
40

communityservice

The mighty community service
JavaScript
1
star
41

bibliomega_frontend_blocks

Blocks configuration for biblioOmega based sites.
PHP
1
star
42

bibdk_custom_search

Customize the default search, add custom search pages, and change input types, labels, values & ordering.
PHP
1
star
43

ding_persistent_login

Provide persistent login credentials for Ding users.
PHP
1
star
44

devpi-web

flake overlay for devpi-web plugin
Nix
1
star
45

xdebug_redirect

Rewritting location header when debugging through ssh
PHP
1
star
46

artesis_basetheme

Base Omege theme for Artesis installations
PHP
1
star
47

ding_omega_frontend_blocks

Blocks configuration for Ding Omega based sites.
PHP
1
star
48

microcurl

PHP
1
star
49

ding_bookmark

PHP
1
star
50

drush-features-export-page-variant

Export a panel page variant to another feature than the original
PHP
1
star
51

mobilsoeg

Mobil Søg
JavaScript
1
star
52

rr-oai

Rawrepo oai migrated to payara-full
Java
1
star
53

CowBrow

jms queue browser
Java
1
star
54

artesis_user_frontend

PHP
1
star
55

bibdk_vejviser

PHP
1
star
56

updateservice

Java
1
star
57

docker-volume-cephfs

Volume plugin to use CephFS as distributed data storage
Go
1
star
58

kubeconsole

Lowkey shell overlay for making life with kubectl a bit more bearable
Shell
1
star
59

bibdk_provider

a provider for bibliotek.dk
PHP
1
star
60

bibliomega_frontend

Panels configuration for biblioOmega based sites.
PHP
1
star
61

rje

Rasmus Jensens(rje) experimental projects
JavaScript
1
star
62

dbc-hack4dk-client

This is a sample client created for use during Hack4DK 2016. It uses a test version of the Open Platform as a datasource.
JavaScript
1
star
63

dbc-community-service

Biblo's helt egen community
JavaScript
1
star
64

rawrepo-introspect

The new rawrepo Introspect
Java
1
star
65

z3950-ill-proxy-master-moved-to-gitlab

Z39.50 protocol proxy for DBC ILL
Java
1
star
66

bibdk_custom_search_preprocess

Bibliotek.dk custom search preprocess functions for combined form values.
PHP
1
star
67

OpenSearch-webservice

Open Search is a web service used for searching and retrieving records from a data repository. Depending on the specific record, there may be metadata, or full text and metadata, as well as relations for the records.
PHP
1
star
68

orholm-prediction-demo

Code snippets demonstrating model building and prediction for the National Museum of Denmark's storage facilities, Ørholm.
Jupyter Notebook
1
star