Finish Pulumi sandbox environment

Switch to an Ansible role for copying the Pulumi scenario files. Add a Packer build file for building preconfigured Pulumi AMIs. Add README with instructions.

Signed-off-by: Scott Lowe <scott.lowe@scottlowe.org>
This commit is contained in:
Scott Lowe 2019-04-21 22:20:27 -06:00
parent 50f996346d
commit ea9d0670a7
No known key found for this signature in database
GPG key ID: 949F43F6E6C11780
14 changed files with 326 additions and 93 deletions

View file

@ -0,0 +1,63 @@
# A Pulumi "Sandbox" Environment
This set of files was created to help users establish a "sandbox" environment for playing around with [Pulumi](https://pulumi.io).
**NOTE:** At some point in the future the Ansible playbooks here will be reconciled with the Pulumi role found in the `ansible/pulumi-env` directory of this repository.
## Contents
* **ansible.cfg**: This Ansible configuration file configures Ansible to work with local Vagrant-powered VMs. Modifications are needed to this file if you wish to use Ansible with a different inventory source (like EC2 instances, for example).
* **configure.yml**: This Ansible playbook is used to configure the baseline Pulumi environment. It is written for Ubuntu 18.04, and can be used with either local Vagrant VMs or remote cloud instances (such as EC2 instances). If you wish to use it with the latter, you will need to modify the Ansible configuration file to specify an appropriate inventory source.
* **js-simple-ec2.yml**: This Ansible playbook installs the files necessary to use Pulumi with JavaScript to instantiate an EC2 instance.
* **machines.yml**: This YAML file contains a list of VM definitions and associated configuration data. It is referenced by `Vagrantfile` when Vagrant instantiates the VMs.
* **packer.json**: This Packer build file allows you to create an AWS AMI that is pre-configured with Pulumi.
* **README.md**: This file you're currently reading.
* **roles**: This directory contains all the various Ansible roles for the particular Pulumi example files. Each role directory corresponds to the name of an Ansible playbook, like `js-simple-ec2.yml`.
* **vagrant.py**: This dynamic inventory source for Ansible pulls inventory data from Vagrant. If you wish to use the Ansible playbooks with a different target, you'll need to find an equivalent dynamic inventory source (or create a static inventory) and modify the Ansible configuration file accordingly.
* **Vagrantfile**: This file is used by Vagrant to spin up the virtual machines. This file is fairly extensively commented to help explain what's happening. You should be able to use this file unchanged; all the VM configuration options are stored outside this file.
## Instructions
These instructions assume you've already installed Ansible, your virtualization provider (VMware Fusion/Workstation or VirtualBox), Vagrant, and any necessary plugins (such as the Vagrant VMware plugin). Please refer to the documentation for those products for more information on installation or configuration. Similarly, if you wish to use these Ansible playbooks with a cloud provider such as AWS, these instructions assume all necessary tools and configuration have already been handled.
### Using Vagrant
1. Use `vagrant box add` to add an Ubuntu 18.04 base box for your particular virtualization provider. Some sample boxes are provided in `machines.yml`.
2. If you decided to use a box _other_ than one of the boxes listed here, you'll need to edit `machines.yml` to specify the correct box. In `machines.yml`, the "vmw" line is for a VMware-formatted box, the "vb" line is for the name of a VirtualBox-formatted box, and the "lv" line is for the name of a box that supports the Libvirt provider. Edit the appropriate line based on your virtualization provider and the name of the box you added in step 1.
3. Run `vagrant up` to instantiate the VM.
### Using AWS
1. Download a dynamic inventory script for EC2 (see [here](https://github.com/ansible/ansible/blob/devel/contrib/inventory/ec2.py) for one such script). Perform any necessary configuration to make sure the inventory script is returning the desired results.
2. Modify the `ansible.cfg` to specify the dynamic inventory script downloaded and configured in step 1.
3. Create an EC2 instance using the tooling of your choice (AWS CLI, AWS console, Terraform, CloudFormation, etc.).
Optionally, you can create a preconfigured Pulumi AMI using `packer`:
packer build packer.json
You'll need to have `packer` installed and configured appropriately.
### For All Platforms
Once you've established a VM or instance using one of the previous two sections, then you can continue here.
1. Run `ansible-playbook configure.yml` to configure the Pulumi base components on the target VM or instance. If you are using a preconfigured AMI built using `packer`, this step isn't needed as it has already been done.
2. Run `ansible-playbook <scenario>.yml` to copy files needed for the particular Pulumi scenario you'd like to use. For example, `js-simple-ec2.yml` is one example scenario that shows using JavaScript with Pulumi to create an EC2 instance.
3. Log into the VM or instance using SSH (for Vagrant use `vagrant ssh`, for example) and switch to the `pulumi` directory in the home directory. The files for the scenario you selected in step 2 will be found there.
4. Play around with Pulumi. Enjoy!

5
pulumi/Vagrantfile vendored
View file

@ -74,9 +74,4 @@ Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
end # srv.vm.provider 'libvirt'
end # config.vm.define
end # machines.each
# Provision the VM with Ansible
config.vm.provision 'ansible' do |ansible|
ansible.playbook = 'provision.yml'
end # config.vm.provision
end # Vagrant.configure

View file

@ -1,5 +1,5 @@
[defaults]
inventory = ./.vagrant/provisioners/ansible/inventory/vagrant_ansible_inventory
inventory = ./vagrant.py
private_key_file = ~/.vagrant.d/insecure_private_key
remote_user = vagrant
host_key_checking = False

59
pulumi/configure.yml Normal file
View file

@ -0,0 +1,59 @@
---
- hosts: "all"
become: "yes"
remote_user: "vagrant"
gather_facts: false
pre_tasks:
- name: "Install Python 2 if not present"
raw: "apt -qqy update; apt -qqy install python"
register: output
retries: 3
delay: 3
until: output.rc == 0
tasks:
- name: "Ensure /usr/local/bin exists"
file:
state: "directory"
path: "/usr/local/bin"
owner: "root"
group: "root"
- name: "Check for existence of Pulumi binary in /usr/local/bin"
stat:
path: "/usr/local/bin/pulumi"
register: pulumi_present
- name: Install NodeJS
apt:
state: "present"
name: "{{ item }}"
with_items:
- nodejs
- npm
- block:
- name: "Download and extract Pulumi 0.17.5"
unarchive:
src: "https://get.pulumi.com/releases/sdk/pulumi-v0.17.5-linux-x64.tar.gz"
remote_src: true
dest: "/tmp"
owner: "root"
group: "root"
- name: "Move binaries to /usr/local/bin"
copy:
src: "/tmp/pulumi/{{ item }}"
remote_src: true
dest: "/usr/local/bin"
owner: "root"
group: "root"
mode: 0755
with_items:
- "pulumi"
- "pulumi-language-go"
- "pulumi-language-nodejs"
- "pulumi-language-python"
- "pulumi-language-python-exec"
- "pulumi-resource-pulumi-nodejs"
when: pulumi_present.stat.exists == False

5
pulumi/js-simple-ec2.yml Normal file
View file

@ -0,0 +1,5 @@
---
- name: Prepare sandbox environment for js-simple-ec2
hosts: all
roles:
- role: js-simple-ec2

View file

@ -1,22 +0,0 @@
---
- hosts: "all"
remote_user: "vagrant"
tasks:
- name: "Create directory to store files"
file:
state: "directory"
path: "/home/vagrant/pulumi"
owner: "vagrant"
group: "vagrant"
- name: "Copy files into sandbox environment"
copy:
src: "{{ item }}"
dest: "/home/vagrant/pulumi"
owner: "vagrant"
group: "vagrant"
with_items:
- "index.js"
- "package.json"
- "Pulumi.yaml"

View file

@ -1,9 +1,9 @@
---
- box:
vmw: "generic/ubuntu1604"
vb: "ubuntu/xenial64"
lv: "generic/ubuntu1604"
name: "xenial"
vmw: "generic/ubuntu1804"
vb: "ubuntu/bionic64"
lv: "generic/ubuntu1804"
name: "pulumi"
nested: false
ram: "1024"
vcpu: "1"

34
pulumi/packer.json Normal file
View file

@ -0,0 +1,34 @@
{
"variables": {
"aws_access_key": "",
"aws_secret_key": "",
"aws_region": "us-west-2",
"ubuntu_1804_ami": "ami-03804ed633fe58109"
},
"builders": [
{
"name": "ami-pulumi-ubuntu",
"type": "amazon-ebs",
"instance_type": "t2.small",
"source_ami": "{{user `ubuntu_1804_ami`}}",
"ami_name": "ami-pulumi-ubuntu-{{timestamp}}",
"access_key": "{{user `aws_access_key`}}",
"secret_key": "{{user `aws_secret_key`}}",
"region": "{{user `aws_region`}}",
"ssh_username": "ubuntu",
"tags": {
"source_ami": "{{user `ubuntu_1804_ami`}}",
"build_date": "{{isotime}}",
"distribution": "Ubuntu",
"distribution_release": "bionic",
"distribution_version": "18.04"
}
}
],
"provisioners": [
{
"type": "ansible",
"playbook_file": "./configure.yml"
}
]
}

View file

@ -1,59 +0,0 @@
---
- hosts: "all"
become: "yes"
remote_user: "vagrant"
gather_facts: false
pre_tasks:
- name: "Install Python 2 if not present"
raw: "apt -qqy update; apt -qqy install python"
register: output
retries: 3
delay: 3
until: output.rc == 0
tasks:
- name: "Ensure /usr/local/bin exists"
file:
state: "directory"
path: "/usr/local/bin"
owner: "root"
group: "root"
- name: "Check for existence of Pulumi binary in /usr/local/bin"
stat:
path: "/usr/local/bin/pulumi"
register: pulumi_present
- name: Install NodeJS
apt:
state: "present"
name: "{{ item }}"
with_items:
- nodejs
- npm
- block:
- name: "Download and extract Pulumi 0.17.5"
unarchive:
src: "https://get.pulumi.com/releases/sdk/pulumi-v0.17.5-linux-x64.tar.gz"
remote_src: true
dest: "/tmp"
owner: "root"
group: "root"
- name: "Move binaries to /usr/local/bin"
copy:
src: "/tmp/pulumi/{{ item }}"
remote_src: true
dest: "/usr/local/bin"
owner: "root"
group: "root"
mode: 0755
with_items:
- "pulumi"
- "pulumi-language-go"
- "pulumi-language-nodejs"
- "pulumi-language-python"
- "pulumi-language-python-exec"
- "pulumi-resource-pulumi-nodejs"
when: pulumi_present.stat.exists == False

View file

@ -6,7 +6,11 @@ const aws = require("@pulumi/aws");
let size = "t2.micro"; // t2.micro is available in the AWS free tier
// Specify AMI
let ami = "ami-c55673a0"; // AMI for Ubuntu 16.04 in us-west-2 (Oregon)
let ami = "ami-09b42c38b449cfa59"; // AMI for Ubuntu 16.04 in us-west-2 (Oregon)
// Specify key pair to use
// YOU MUST REPLACE THIS VALUE WITH THE CORRECT NAME FOR YOUR ACCOUNT!
let keypair = "aws_rsa";
// Create a new security group for port 80
let group = new aws.ec2.SecurityGroup("pulumi-secgrp", {
@ -27,7 +31,8 @@ let server = new aws.ec2.Instance("pulumi-ubuntu", {
instanceType: size,
securityGroups: [ group.name ], // reference the group object above
ami: ami,
userData: userData // start a simple web server
userData: userData, // start a simple web server
keyName: keypair
});
exports.publicIp = server.publicIp;

View file

@ -0,0 +1,22 @@
---
- name: "Create directory to store files"
file:
state: "directory"
path: "{{ ansible_env.HOME }}/pulumi"
owner: "vagrant"
group: "vagrant"
- name: "Copy files into sandbox environment"
copy:
src: "{{ item }}"
dest: "{{ ansible_env.HOME }}/pulumi"
owner: "vagrant"
group: "vagrant"
with_items:
- "index.js"
- "package.json"
- "Pulumi.yaml"
- name: "Install Pulumi SDK"
npm:
path: "{{ ansible_env.HOME }}/pulumi"

131
pulumi/vagrant.py Executable file
View file

@ -0,0 +1,131 @@
#!/usr/bin/env python
"""
Vagrant external inventory script. Automatically finds the IP of the booted vagrant vm(s), and
returns it under the host group 'vagrant'
Example Vagrant configuration using this script:
config.vm.provision :ansible do |ansible|
ansible.playbook = "./provision/your_playbook.yml"
ansible.inventory_file = "./provision/inventory/vagrant.py"
ansible.verbose = true
end
"""
# Copyright (C) 2013 Mark Mandel <mark@compoundtheory.com>
# 2015 Igor Khomyakov <homyakov@gmail.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# Thanks to the spacewalk.py inventory script for giving me the basic structure
# of this.
#
import sys
import os.path
import subprocess
import re
from paramiko import SSHConfig
from optparse import OptionParser
from collections import defaultdict
import json
from ansible.module_utils._text import to_text
from ansible.module_utils.six.moves import StringIO
_group = 'vagrant' # a default group
_ssh_to_ansible = [('user', 'ansible_user'),
('hostname', 'ansible_host'),
('identityfile', 'ansible_ssh_private_key_file'),
('port', 'ansible_port')]
# Options
# ------------------------------
parser = OptionParser(usage="%prog [options] --list | --host <machine>")
parser.add_option('--list', default=False, dest="list", action="store_true",
help="Produce a JSON consumable grouping of Vagrant servers for Ansible")
parser.add_option('--host', default=None, dest="host",
help="Generate additional host specific details for given host for Ansible")
(options, args) = parser.parse_args()
#
# helper functions
#
# get all the ssh configs for all boxes in an array of dictionaries.
def get_ssh_config():
return dict((k, get_a_ssh_config(k)) for k in list_running_boxes())
# list all the running boxes
def list_running_boxes():
output = to_text(subprocess.check_output(["vagrant", "status"]), errors='surrogate_or_strict').split('\n')
boxes = []
for line in output:
matcher = re.search(r"([^\s]+)[\s]+running \(.+", line)
if matcher:
boxes.append(matcher.group(1))
return boxes
# get the ssh config for a single box
def get_a_ssh_config(box_name):
"""Gives back a map of all the machine's ssh configurations"""
output = to_text(subprocess.check_output(["vagrant", "ssh-config", box_name]), errors='surrogate_or_strict')
config = SSHConfig()
config.parse(StringIO(output))
host_config = config.lookup(box_name)
# man 5 ssh_config:
# > It is possible to have multiple identity files ...
# > all these identities will be tried in sequence.
for id in host_config['identityfile']:
if os.path.isfile(id):
host_config['identityfile'] = id
return dict((v, host_config[k]) for k, v in _ssh_to_ansible)
# List out servers that vagrant has running
# ------------------------------
if options.list:
ssh_config = get_ssh_config()
meta = defaultdict(dict)
for host in ssh_config:
meta['hostvars'][host] = ssh_config[host]
print(json.dumps({_group: list(ssh_config.keys()), '_meta': meta}))
sys.exit(0)
# Get out the host details
# ------------------------------
elif options.host:
print(json.dumps(get_a_ssh_config(options.host)))
sys.exit(0)
# Print out help
# ------------------------------
else:
parser.print_help()
sys.exit(0)