Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implemented Cookiecutter #416

Merged
merged 2 commits into from
Aug 29, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
148 changes: 70 additions & 78 deletions molecule/command/init.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,9 @@
# THE SOFTWARE.

import os
import re
import subprocess

import jinja2
import sh
import cookiecutter
import cookiecutter.main

from molecule import util
from molecule.command import base
Expand All @@ -42,88 +40,82 @@ class Init(base.Base):
def main(self):
pass

def clean_meta_main(self, role_path):
main_path = os.path.join(role_path, 'meta', 'main.yml')
temp_path = os.path.join(role_path, 'meta', 'main.yml.tmp')
with open(temp_path, 'w') as temp:
for line in file(main_path):
line = re.sub(r'[ \t]*$', '', line)
if line != '\n':
temp.write(line)
os.rename(temp_path, main_path)

def execute(self):
role = self.molecule._args['<role>']

role_path = os.getcwd()
if not role:
role = os.getcwd().split(os.sep)[-1]
role_path = os.getcwd()
util.print_info("Initializing molecule in current directory...")

role_path = os.path.abspath(os.path.join(os.getcwd(), os.pardir))
self._init_existing_role(role, role_path)
else:

if os.path.isdir(role):
msg = 'The directory {} already exists. Cannot create new role.'
LOG.error(msg.format(role))
util.sysexit()
self._init_new_role(role, role_path)

role_path = os.path.join(os.curdir, role)

util.print_info("Initializing role {}...".format(role))

try:
if self.molecule._args['--offline']:
sh.ansible_galaxy('init', '--offline', role)
else:
sh.ansible_galaxy('init', role)
except (subprocess.CalledProcessError, sh.ErrorReturnCode_1) as e:
LOG.error('ERROR: {}'.format(e))
util.sysexit(e.returncode)

self.clean_meta_main(role_path)

env = jinja2.Environment(
loader=jinja2.PackageLoader('molecule', 'template'),
keep_trailing_newline=True)

t_molecule = env.get_template(self.molecule.config.config['molecule'][
'init']['templates']['molecule'])
t_playbook = env.get_template(self.molecule.config.config['molecule'][
'init']['templates']['playbook'])
t_test_default = env.get_template(self.molecule.config.config[
'molecule']['init']['templates']['test_default'])

if (self.molecule._args['--docker']):
t_molecule = env.get_template(self.molecule.config.config[
'molecule']['init']['templates']['molecule_docker'])
if (self.molecule._args['--openstack']):
t_molecule = env.get_template(self.molecule.config.config[
'molecule']['init']['templates']['molecule_openstack'])

sanitized_role = re.sub('[._]', '-', role)
with open(
os.path.join(role_path, self.molecule.config.molecule_file),
'w') as f:
f.write(t_molecule.render(config=self.molecule.config.config,
role=sanitized_role))

with open(
os.path.join(
role_path,
self.molecule.config.config['ansible']['playbook']),
'w') as f:
f.write(t_playbook.render(role=role))

testinfra_path = os.path.join(
role_path,
self.molecule.config.config['molecule']['testinfra_dir'])

if not os.path.isdir(testinfra_path):
os.mkdir(testinfra_path)

with open(os.path.join(testinfra_path, 'test_default.py'), 'w') as f:
f.write(t_test_default.render())

msg = 'Successfully initialized new role in {}'
util.print_success(msg.format(role_path))
msg = 'Successfully initialized new role in {}...'
util.print_success(msg.format(os.path.join(role_path, role)))
util.sysexit(0)

def _init_existing_role(self, role, role_path):
driver = self._get_driver()
extra_context = self._get_cookiecutter_context(role, driver)

util.print_info("Initializing molecule in current directory...")
for template in ['playbook', 'driver/{}'.format(driver)]:
self._create_template(template, extra_context, role_path)

def _init_new_role(self, role, role_path):
driver = self._get_driver()
extra_context = self._get_cookiecutter_context(role, driver)

util.print_info("Initializing role {}...".format(role))
for template in ['galaxy_init', 'playbook', 'driver/{}'.format(driver),
'verifier/testinfra', 'verifier/serverspec']:
self._create_template(template, extra_context, role_path)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've probably implemented cookiecutter somewhat oddly here. Wanted modular templates, since we need to write an entire role tree with modular test suites. Other times we only want a couple files from the templates.

I figure we can iterate on this, and make it better as we learn more about cookiecutter. 🍪


def _create_template(self,
template,
extra_context,
output_dir,
no_input=True,
overwrite=True):
t = self._get_cookiecutter_template_dir(template)

cookiecutter.main.cookiecutter(t,
extra_context=extra_context,
output_dir=output_dir,
no_input=no_input,
overwrite_if_exists=overwrite)

def _get_cookiecutter_context(self, role, driver):
md = self.molecule.config.config['molecule']['init']
platform = md.get('platform')
provider = md.get('provider')

d = {
'repo_name': role,
'role_name': role,
'driver': driver,
}
if driver == 'vagrant':
d.update({'platform_name': platform.get('name'),
'platform_box': platform.get('box'),
'platform_box_url': platform.get('box_url'),
'provider_name': provider.get('name'),
'provider_type': provider.get('type')})

return d

def _get_cookiecutter_template_dir(self, template):
return os.path.join(
os.path.dirname(__file__), '..', 'cookiecutter', template)

def _get_driver(self):
if self.molecule._args['--docker']:
return 'docker'
elif self.molecule._args['--openstack']:
return 'openstack'
else:
return 'vagrant'
11 changes: 4 additions & 7 deletions molecule/conf/defaults.yml
Original file line number Diff line number Diff line change
Expand Up @@ -58,13 +58,10 @@ molecule:
box: trusty64
box_url: https://vagrantcloud.com/ubuntu/boxes/trusty64/versions/14.04/providers/virtualbox.box
box_version: 0.1.0
# templates to use when creating files during `molecule init`
templates:
molecule: molecule.yml.j2
molecule_docker: molecule_docker.yml.j2
molecule_openstack: molecule_openstack.yml.j2
playbook: playbook.yml.j2
test_default: test_default.py.j2
# default provider to populate when doing `molecule init`
provider:
name: virtualbox
type: virtualbox

# defaults passed to ansible-playbook
ansible:
Expand Down
5 changes: 5 additions & 0 deletions molecule/cookiecutter/driver/docker/cookiecutter.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"repo_name": "OVERRIDEN",
"role_name": "OVERRIDEN",
"instances": 2
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't get how the variable in the path name works for this file?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't get how the variable in the path name works for this file?

It's a cookiecutter thing. Creates a directory named what we call the repo. In our case the repo is the name of the role we are creating.

docker:
containers:
{%- for n in range(cookiecutter.instances|int) %}

- name: {{ cookiecutter.role_name }}-{{ '%02d' % loop.index }}
image: ubuntu
image_version: latest
ansible_groups:
- group1
{%- endfor -%}
4 changes: 4 additions & 0 deletions molecule/cookiecutter/driver/openstack/cookiecutter.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"repo_name": "OVERRIDEN",
"role_name": "OVERRIDEN"
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
---
ansible:
playbook: playbook.yml

openstack:
keyfile: ~/.ssh/id_rsa
keypair: NameKey
Expand All @@ -11,4 +8,4 @@ openstack:
flavor: m1.xlarge
sshuser: centos
ansible_groups:
- ansib1
- group1
12 changes: 12 additions & 0 deletions molecule/cookiecutter/driver/vagrant/cookiecutter.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"repo_name": "OVERRIDEN",
"role_name": "OVERRIDEN",
"instances": 2,
"platform_name": "OVERRIDEN",
"platform_box": "OVERRIDEN",
"platform_box_url": "OVERRIDEN",
"provider_name": "virtualbox",
"provider_type": "virtualbox",
"provider_options_memory": 512,
"provider_options_cpu": 2
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
---
vagrant:
platforms:
- name: {{ cookiecutter.platform_name }}
box: {{ cookiecutter.platform_box }}
box_url: {{ cookiecutter.platform_box_url }}

providers:
- name: {{ cookiecutter.provider_name }}
type: {{ cookiecutter.provider_type }}
options:
memory: {{ cookiecutter.provider_options_memory }}
cpus: {{ cookiecutter.provider_options_cpu }}

instances:
{%- for n in range(cookiecutter.instances|int) %}

- name: {{ cookiecutter.role_name }}-{{ '%02d' % loop.index }}
ansible_groups:
- group1
{%- endfor -%}
4 changes: 4 additions & 0 deletions molecule/cookiecutter/galaxy_init/cookiecutter.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"repo_name": "OVERRIDEN",
"role_name": "OVERRIDEN"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
Role Name
=========

A brief description of the role goes here.

Requirements
------------

Any pre-requisites that may not be covered by Ansible itself or the role should be mentioned here. For instance, if the role uses the EC2 module, it may be a good idea to mention in this section that the boto package is required.

Role Variables
--------------

A description of the settable variables for this role should go here, including any variables that are in defaults/main.yml, vars/main.yml, and any variables that can/should be set via parameters to the role. Any variables that are read from other roles and/or the global scope (ie. hostvars, group vars, etc.) should be mentioned here as well.

Dependencies
------------

A list of other roles hosted on Galaxy should go here, plus any details in regards to parameters that may need to be set for other roles, or variables that are used from other roles.

Example Playbook
----------------

Including an example of how to use your role (for instance, with variables passed in as parameters) is always nice for users too:

- hosts: servers
roles:
- { role: username.rolename, x: 42 }

License
-------

BSD

Author Information
------------------

An optional section for the role authors to include contact information, or a website (HTML is not allowed).
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
---
# defaults file for {{ cookiecutter.role_name }}
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
---
# handlers file for {{ cookiecutter.role_name }}
Loading