Skip to content

Instantly share code, notes, and snippets.

@craig-m-unsw
Last active January 22, 2024 05:09
Show Gist options
  • Save craig-m-unsw/072dda4f670103e9eb34aaf997c20b63 to your computer and use it in GitHub Desktop.
Save craig-m-unsw/072dda4f670103e9eb34aaf997c20b63 to your computer and use it in GitHub Desktop.
The scaffold that starts all my projects - for my own copy/paste convenience

minimum project

For a minimum viable proof of concept or as a place to run some tools, I keep coming back to this same pattern.

foo-bar/
    ├── .gitignore
    ├── README.md
    ├── playbook.yml
    └── Vagrantfile

Sometimes containers are more suitable than a virtual machine, but the same pattern is still utilised.

git

optional: put this folder under revision control (just locally - no remote) so we can make changes and roll back.

Create a .gitignore with something like:

# a .gitignore file
#
.vagrant/
localdata/*
!localdata/.gitkeep
.DS_Store
*.log
*.pyc

Run the commands:

git init
git add .
git commit -m 'setup is working'

I like to see file changes over time in VS-Code.

setup

On your MacOS / Windows / Linux system you need Vagrant.

And a hypervisor for it to drive - see the Vagrant docs.

I am using Roboxes for the Base box.

Start VM

Create the new virtual machine:

vagrant validate Vagrantfile
vagrant up
vagrant rsync-auto

This will run the single-file ansible playbook.yml inside our VM. Open a browser and you can now login to Cockpit at http://localhost:9090/ (vagrant:vagrant).

Use VM

Login to the VM with an SSH port forward:

vagrant ssh -- -L 8888:127.0.0.1:8888
tmux

This is, if you don't like the Web Terminal provided by Cockpit.

clean up:

exit 
vagrant destroy -f
---
- name: "Dev VM ansible playbook"
hosts: all
gather_facts: yes
vars:
package_install: [
{ add_item: 'curl' },
{ add_item: 'wget' },
{ add_item: 'lsof' },
{ add_item: 'dos2unix' },
{ add_item: 'tmux' },
{ add_item: 'vim' },
{ add_item: 'git' },
{ add_item: 'make' },
{ add_item: 'tcpdump' },
{ add_item: 'socat' },
{ add_item: 'expect' },
{ add_item: 'sshfs' },
{ add_item: 'acl' },
{ add_item: 'python3-virtualenv' },
{ add_item: 'python3-pip' }
]
package_remove: [
{ del_item: 'telnet' }
]
create_folders: [
{ dir: '/etc/ansible/facts.d/', mode: '0755', owner: 'root', group: 'root' },
{ dir: '/opt/data/', mode: '0775', owner: 'root', group: 'vagrant' },
{ dir: '/root/tmp/', mode: '0700', owner: 'root', group: 'root' },
{ dir: '/mnt/sshfs/', mode: '0775', owner: 'root', group: 'root' }
]
newuser_name: "pyuser"
newuser_home: "/opt/pyapp"
tasks:
- name: "checks and debug"
block:
- name: "OS check"
assert:
that:
- ansible_os_family == "Debian"
- ansible_architecture in ['x86_64', 'arm64']
- name: "check new user var"
assert:
that:
- 'newuser_name is defined'
- name: Display hostname
debug:
msg: "inventory_hostname {{ inventory_hostname }}"
- name: "system config"
block:
- name: "create new user"
ansible.builtin.user:
name: "{{ newuser_name }}"
comment: "py apps"
home: "{{ newuser_home }}"
shell: /usr/bin/bash
create_home: false
- name: "homedir for {{ newuser_name }}"
ansible.builtin.file:
path: "{{ newuser_home }}"
state: directory
mode: "0775"
owner: "{{ newuser_name}}"
group: "{{ newuser_name }}"
- name: "files and folders"
block:
- name: "create common folders"
ansible.builtin.file:
path: "{{ item.dir }}"
state: directory
mode: "{{ item.mode }}"
owner: "{{ item.owner }}"
group: "{{ item.group }}"
with_items: "{{ create_folders }}"
become: true
- name: "custom local facts"
block:
- name: "create customtest.fact"
ansible.builtin.copy:
dest: "/etc/ansible/facts.d/customtest.fact"
mode: 0755
owner: root
group: root
content: |
#!/usr/bin/env python3
# random custom ansible facts
import os
import sys
import platform
import datetime
import json
# var:
date = str(datetime.datetime.now())
# output
print(json.dumps({
"time" : date
}))
become: true
- name: "reload facts"
ansible.builtin.setup:
fact_path: "/etc/ansible/facts.d"
- name: "show fact"
debug:
msg: "customtest {{ ansible_local.customtest.time }}"
- name: "check custom fact was set"
assert:
that:
- ansible_local.customtest.time is defined
- name: "install software"
block:
- name: "install OS packages"
ansible.builtin.package:
name: "{{ item.add_item }}"
state: present
retries: 3
with_items: "{{ package_install }}"
become: true
tags:
- software
- name: "remove OS packages"
ansible.builtin.package:
name: "{{ item.del_item }}"
state: absent
with_items: "{{ package_remove }}"
become: true
tags:
- software
- name: "install python tools"
block:
- name: "create requirements file"
ansible.builtin.copy:
dest: "{{ newuser_home }}/requirements.txt"
mode: 0644
owner: "{{ newuser_name }}"
group: "{{ newuser_name }}"
content: |
# -- ansible managed file --
pyyaml
pytest
dnspython
invoke
become: true
tags:
- software
- name: "install requirements"
pip:
requirements: requirements.txt
virtualenv: "{{ newuser_home }}/venv/"
chdir: "{{ newuser_home }}"
become: true
become_method: sudo
become_user: "{{ newuser_name }}"
tags:
- software
- name: "Cockpit Web UI setup"
block:
- name: "install cockpit"
ansible.builtin.package:
name: cockpit
state: present
tags:
- software
- name: "turn on cockpit socket"
service:
name: cockpit.socket
state: started
enabled: yes
- name: "Pause until web ui is up"
uri:
url: "http://localhost:9090/"
follow_redirects: none
method: GET
register: _result
until: _result.status == 200
retries: 30
delay: 5 # seconds
become: true
- name: "Finish up"
block:
- name: "script to run ansible"
ansible.builtin.copy:
dest: /home/vagrant/run-playbook.sh
mode: 0755
content: |
echo "running playbook.yml on localhost";
if [[ root = "$(whoami)" ]]; then
echo "Error: do not run as root";
exit 1;
fi
export ANSIBLE_PYTHON_INTERPRETER=/usr/bin/python3
ansible-playbook -v --connection=local -i "127.0.0.1," -- /vagrant/playbook.yml;
- name: "create readme"
ansible.builtin.copy:
dest: /home/vagrant/readme.txt
mode: 0644
content: |
# --- Dev VM details ---
# distro: {{ ansible_distribution }} {{ ansible_distribution_version }} {{ ansible_architecture }}
# ansible: {{ ansible_version }}
# python: {{ ansible_playbook_python }}
- name: "debug note"
debug:
msg: "-- playbook.yml has finished --"
Vagrant.require_version ">= 2.2.14"
$inlinescript_post = <<-SCRIPT
echo '-----------------------';
uname -a;
uptime;
echo '-----------------------';
SCRIPT
Vagrant.configure("2") do |config|
config.vm.box = "generic/ubuntu2004"
config.vm.box_check_update = false
config.vm.synced_folder ".", "/vagrant", type: "rsync"
config.ssh.keep_alive = true
config.ssh.compression = false
config.ssh.forward_agent = false
config.ssh.insert_key = true
config.vm.provision "ansible_local" do |ansible|
ansible.playbook = "/vagrant/playbook.yml"
ansible.extra_vars = { ansible_python_interpreter: "/usr/bin/python3" }
ansible.verbose = true
end
config.trigger.after [:up, :resume, :reload] do |t|
t.info = "running inlinescript_post"
t.run_remote = { inline: $inlinescript_post, :privileged => false }
end
config.vm.network :forwarded_port, guest: 9090, host_ip: '127.0.0.1', host: 9090, protocol: "tcp"
# -- provider specifics --
config.vm.provider :hyperv do |hpv, override|
hpv.vmname = "minimum-project"
hpv.enable_virtualization_extensions = false
end
config.vm.provider :virtualbox do |virtualbox, override|
virtualbox.name = "minimum-project"
virtualbox.gui = false
end
config.vm.provider :libvirt do |libv, override|
end
config.vm.provider :parallels do |para, override|
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment