small pixel drawing of a pufferfish zoa

README

                ?????????????????
                ?  what is zoa  ?
                ?????????????????

  zoa is the best config management tool. it's the
  best because it's the simplest. it's written in shell, has
  a few nice little VERY USEFUL helpers, and doesn't go out
  of its way to make you hate yourself.

  zoa requires access to a git repo, though it may be used
  in local development mode.

  zoa is intended for human-scale deployment, and generally
  works best if you're not trying to manage a crazy complicated
  fleet of systems between multiple teams. zoa is for small,
  tight-knit teams who want to keep it all in their heads with
  minimal effort.

  it's also for individuals who like understanding their systems,
  and hate maintaining them when things break for no reason.



              why did you write this?

       i manage systems with config management tools for a living.
     i know how impossible they all are firsthand, i have 6+ years
  of painful, painful experience. i wrote zoa to use on my personal
systems, because i hate the primary config management tools a lot. 



             ~~ zoa doesn't pretend ~~

    chef & ansible & puppet & salt all pretend to be
    fully idempotent & declarative, but leave
    actual declarative-ness and idempotence as
    an exercise to the user.

      zoa doesn't pretend. zoa is not idempotent. zoa
      is not declarative. it assumes your state changes
      over time, and zoa makes it easy to track those
      changes. you won't have to look at horrible docs
      or commit a week to learning a god
                                       damned
                                        stupid
                                          DSL

    other config management systems make their users
    feel stupid.

      zoa doesn't. zoa uses shell scripts like every
      single linux distro has used for millenia. it
      has exactly zero dependencies besides zoa itself.

      zoa adheres to standards, and uses well-known
      distro conventions.

      because it's easy. and simple. and honestly,
      it's not very big.

    chef & ansible & puppet pretend that you can
    work around bad packaging & applications
    with config management!

      zoa doesn't! if a package is packaged poorly
      or an application sucks to install, it will
      equally suck in zoa. sorry.

    the other tools want to do a lot - search across
    your nodes, deploy via their tooling, automate
    testing, etc
  
      zoa is only concerned with _managing configuration_

      and nothing else. forever.



               !!!!!!!!!!!!!!!!!!
               !   QUICKSTART   !
               !!!!!!!!!!!!!!!!!!

  - ~ - understand: there are three components - ~ -

          everything you need to know:

                1: the utility
                2: env vars and helpers
                3: the layout

         --- 1: the utility ---

  simply run the "zoa" tool on all of your systems. it's a simple binary (XXX: or
  shell script?) that can be installed trivially. it expects to run as root, and
  it should run on a cron/timer/whatever schedule at whatever interval you want.

  install:
    wget -O  https://j3s.sh/zoa/zoa # TODO
    mv zoa /usr/local/sbin
    chmod +x /usr/local/sbin/zoa

  design guideline: all configuration data comes from a git repository.
                             the nodes themselves keep no state at all.

  to run zoa:
    zoa https://git.j3s.sh/config main
    ^              ^                ^
  zoa, duh.       git repo          git ref (branch or tag)

  zoa will clone the repo+branch specified in your config (or attempt to
  fetch it, if it's already cloned) to /var/lib/zoa/<repo>/<branch>

  zoa will then execute /var/lib/zoa/<repo>/<branch>/main

  if you run "zoa" with 0 arguments, it will execute if it finds exactly 1
  repo/branch combo in /var/lib/zoa - if you run zoa this way, it will not
  clone the remote repo. this can be very useful for local testing. note
  that the next run of zoa with a repo+branch will wipe any changes you
  make in /var/lib.

  that's it, you've configured your server! set up a cronjob/systemd timer to
  run zoa on a schedule, if that's your thing. or just login and run it
  periodically. you could even have the first zoa run set up a cronjob that runs it
  on a schedule, to make your life even easier.
  👀👀👀👀👀👀👀👀👀👀👀👀👀👀👀👀👀👀👀👀👀👀👀👀👀👀👀👀👀👀👀👀👀👀👀👀👀👀


         --- 2: environment variables and helper functions ---

  in zoa, you write plain POSIX shell. Why?
  - posix shell is productive
  - posix shell is portable
  - posix shell is hard to make too complicated

                        ENVIRONMENT VARIABLES

  before zoa runs, it sets a few standard environment variables for your usage.
  here are _all of them_ on my dev machine:
    OS="Linux"
    DISTRO="arch"
    NODENAME="nostromo.j3s.sh"
    # design guideline: everything in zoa uses base units. MEM_TOTAL is in bytes.
    MEM_TOTAL="16165548000"

                          HELPER FUNCTIONS

     zoa has some little helper functions for common operations.

      zoa-file
        usage:
          zoa-file example.conf /etc/example.conf optional-command
                    ^                ^                  ^
                  source         destination      executed if the file changes

        description:
          zoa-file takes a file from $zoa_root/files/ and places it on-disk
          somewhere. if the file is changed, a command is optionally executed.
          this can be very useful for things like reloading configurations.
      
          when you think about zoa-file, just think of it as regular cp, with an
          extra argument for running arbitrary code if the cp does anything.

        examples:
          zoa-file motd /etc/motd
          zoa-file nginx/j3s.sh /etc/nginx/conf.d/j3s.sh systemctl reload nginx

        todo:
          add a template processing step of some kind?

      zoa-script
        usage:
          zoa-script scriptname
                         ^
                   script to execute 

        description:
          zoa-script executes the given script from $zoa_root/scripts/

        examples:
          zoa-script nginx
          zoa-script 420/69


         --- 3: the layout ---

  ah. finally. how do i lay out my zoa repo?

  here's the canonical fs layout:
  main     <-- entrypoint
  files/   <-- arbitrary text files you'd like to place
  scripts/ <-- arbitrary shell scripts

  you need at least main! everything else is optional.

  main is your entrypoint. if your whole vibe is just doing basic configuration
  of nodes, you _could_ put literally everything in main and just be done with it.

  however, you may want main to be a little more complex:

      case $NODENAME in
          git.j3s.sh)
              . ./
      esac

  scripts/ contains arbitrary shell scripts. you can organize them how you'd like.

  files/ contains text files (no big files or binaries pls) that you might be
  interested in placing on hosts. these can be accessed with the zoa_file function.

               *~~~ warts ~~~*
   * zoa doesn't support function definitions YET
   * zoa reserved words must be all-on-one-line atm



                  BONUS SECTION! :3 :3 <3 :3
          common config management patterns to zoa

### install a package,
### but only if the distro is debian

      zoa
if [ "$DISTRO" = "debian" ]; then
  apt install -y cowsay
fi

    ansible
- name: add package
  when: ansible_facts['os_family'] == "Debian"
  package:
    name: 'cowsay'
    state: present
  
### place sshd_config,
### and reload ssh when it changes
      ansible
# handlers/main.yml
- name: restart-sshd
  service:
    name: sshd
    state: restarted

# tasks/main.yml
- name: Configure sshd
  template:
    src: sshd_config.j2
    dest: /etc/ssh/sshd_config
    owner: root
    group: root
    mode: 0644
  notify: restart-sshd

      zoa
zoa-file sshd /etc/ssh/sshd_config systemctl restart sshd
chown root:root /etc/ssh/sshd_config
chmod 0644 /etc/ssh/sshd_config

### append an iptables rule to the input chain
    ansible
- name: do the thing
  iptables:
    chain: INPUT
    protocol: tcp
    destination_port: '22'
    ctstate: NEW
    syn: match
    jump: ACCEPT

    zoa
rule='INPUT --protocol tcp --dport 69 --jump ACCEPT'
iptables --check $rule || iptables --append $rule