Add travis ci (#35)

* Add travis CI
* Add ci testing of example models (with python 2.7 as py3 still fails)
diff --git a/.gitignore b/.gitignore
index 5503138..162c415 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,3 +6,4 @@
 /build
 /dist
 /.coverage
+.kitchen
diff --git a/.kitchen-verify.sh b/.kitchen-verify.sh
new file mode 100755
index 0000000..fb3adde
--- /dev/null
+++ b/.kitchen-verify.sh
@@ -0,0 +1,22 @@
+#!/bin/bash
+#set -x
+
+# setup
+source /*.env
+INVENTORY_BASE_URI=/tmp/kitchen/test/model/$MODEL
+RECLASS=/tmp/kitchen
+
+# prereq
+python -m ensurepip --default-pip
+pip install pipenv
+
+# env
+cd $RECLASS
+pipenv --venv || pipenv install --python ${PYVER}
+test -e /etc/reclsss || mkdir /etc/reclass
+cp -avf $INVENTORY_BASE_URI/reclass-config* /etc/reclass
+
+# verify
+for n in $(ls $INVENTORY_BASE_URI/nodes/*|sort); do
+  pipenv run python${PYVER} ./reclass.py --inventory-base-uri=$INVENTORY_BASE_URI --nodeinfo $(basename $n .yml)
+done
diff --git a/.kitchen.yml b/.kitchen.yml
new file mode 100644
index 0000000..45be629
--- /dev/null
+++ b/.kitchen.yml
@@ -0,0 +1,41 @@
+---
+driver:
+  name: docker
+  priviledged: false
+  use_sudo: false
+  volume:
+    - <%= ENV['PWD'] %>:/tmp/kitchen
+
+
+provisioner:
+  name: shell
+  script: .kitchen-verify.sh
+
+
+verifier:
+  name: inspec
+
+<%- pyver = ENV['PYTHON_VERSION'] || '2.7' %>
+
+platforms:
+  <% `find test/model -maxdepth 1 -mindepth 1 -type d |sort -u`.split().each do |model| %>
+  <% model=model.split('/')[2] %>
+  - name: <%= model %>
+    driver_config:
+      image: python:<%= pyver %>
+      platform: ubuntu
+      hostname: reclass
+      provision_command:
+        #FIXME, setup reclass env (prereq, configs, upload models)
+        #- apt-get install -y rsync
+        - echo "
+            export LC_ALL=C.UTF-8;\n
+            export LANG=C.UTF-8;\n
+            export PYVER=<%= pyver %>;\n
+            export MODEL=<%= model %>;\n
+          " > /kitchen.env
+  <% end %>
+
+suites:
+  - name: model
+
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..2a41776
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,116 @@
+sudo: required
+language: python
+dist: trusty
+cache: pip
+python:
+- '2.7'
+- '3.6'
+service:
+- docker
+
+#apt:
+  #update: true
+
+#stages:
+#- name: test
+#- name: coverage
+#- name: models
+#- name: build
+#  if: fork = false
+#- name: publish
+#  if: tag =~ ^v.* and fork = false and branch = 'master'
+
+env:
+  global:
+    - PACKAGENAME="reclass"
+
+install: &pyinst
+- pip install pyparsing
+- pip install PyYAML
+# To test example models with kitchen:
+- |
+  test -e Gemfile || cat <<EOF > Gemfile
+  source 'https://rubygems.org'
+  gem 'rake'
+  gem 'test-kitchen'
+  gem 'kitchen-docker'
+  gem 'kitchen-inspec'
+  gem 'inspec'
+- bundle install
+
+script:
+- python setup.py install
+- find . reclass -name 'test_*.py' | sort | xargs -n1 -i% bash -c "echo %; python %"
+# To test example models with kitchen:
+- export PYTHON_VERSION=$TRAVIS_PYTHON_VERSION
+- kitchen list
+- if [ "$PYTHON_VERSION" = "2.7" ]; then kitchen test; fi
+
+# NOTE: travis stage builds, below saved for future reference
+#jobs:
+#  include:
+#  - stage: test
+#    script: &unittest
+#    - python setup.py install
+#    - find . reclass -name 'test_*.py' | sort | xargs -n1 -i% bash -c "echo %; python %"
+#
+#  - stage: coverage
+#    install: *pyinst
+#    script:
+#    - python3 -m pytest --cov=. --cov-report=term-missing:skip-covered
+#    - coverage xml
+#    #- coveralls
+#    #- |
+#        #[ ! -z "${CODACY_PROJECT_TOKEN}" ] && python-codacy-coverage -r coverage.xml || echo "Codacy coverage NOT exported"
+#
+#  - stage: lint
+#    script:
+#    - python3 -m flake8
+#
+#  - stage: models
+#    install: &kitchen
+#    - pip install PyYAML
+#    - pip install virtualenv
+#    - |
+#      test -e Gemfile || cat <<EOF > Gemfile
+#      source 'https://rubygems.org'
+#      gem 'rake'
+#      gem 'test-kitchen'
+#      gem 'kitchen-docker'
+#      gem 'kitchen-inspec'
+#      gem 'inspec'
+#    - bundle install
+#    script:
+#    - export PYTHON_VERSION=$TRAVIS_PYTHON_VERSION
+#    - kitchen list
+#    #FIXME- kitchen test
+#
+#  - stage: build
+#    install: *pyinst
+#    script: []
+#
+#  - stage: publish
+#    install:
+#    - "/bin/true"
+#    script:
+#    - "/bin/true"
+#    deploy:
+#      provider: pypi
+#      user: epcim
+#      password:
+#        secure: TBD
+#      on:
+#        tags: true
+#        repo: salt-formulas/reclass
+#        branch: master
+#        #FIXME, $TRAVIS_PYTHON_VERSION == '2.7'
+
+notifications:
+  webhooks:
+    on_success: change  # options: [always|never|change] default: always
+    on_failure: never
+    on_start: never
+    on_cancel: never
+    on_error: never
+  email: true
+
diff --git a/Pipfile b/Pipfile
new file mode 100644
index 0000000..525e7cc
--- /dev/null
+++ b/Pipfile
@@ -0,0 +1,17 @@
+[[source]]
+url = "https://pypi.python.org/simple"
+verify_ssl = true
+name = "pypi"
+
+[dev-packages]
+
+[packages]
+pyparsing = "*"
+PyYAML = "*"
+six = "*"
+pyyaml = "*"
+# FIXME, issues with compile phase
+#"pygit2" = "*"
+
+[requires]
+python_version = "2.7"
diff --git a/reclass/cli.py b/reclass/cli.py
index d1b22b8..f0b6069 100644
--- a/reclass/cli.py
+++ b/reclass/cli.py
@@ -38,9 +38,9 @@
         else:
             data = reclass.inventory()
 
-        print output(data, options.output, options.pretty_print, options.no_refs)
+        print(output(data, options.output, options.pretty_print, options.no_refs))
 
-    except ReclassException, e:
+    except ReclassException as e:
         e.exit_with_message(sys.stderr)
 
     sys.exit(posix.EX_OK)
diff --git a/test/model/default/classes/first.yml b/test/model/default/classes/first.yml
new file mode 100644
index 0000000..9b72a26
--- /dev/null
+++ b/test/model/default/classes/first.yml
@@ -0,0 +1,32 @@
+parameters:
+  _param:
+    some: param
+    colour: red
+  lab:
+    name: test
+    label: first
+  colour:
+    escaped: \${_param:colour}
+    doubleescaped: \\${_param:colour}
+    unescaped: ${_param:colour}
+  colours:
+    red:
+      name: red
+    blue:
+      name: blue
+  one:
+    a: 1
+    b: 2
+  two:
+    c: 3
+    d: 4
+  three:
+    e: 5
+  list_to_override:
+  - one
+  - two
+  dict_to_override:
+   one: 1
+   two: 2
+
+
diff --git a/test/model/default/classes/lab/env/dev.yml b/test/model/default/classes/lab/env/dev.yml
new file mode 100644
index 0000000..0cce363
--- /dev/null
+++ b/test/model/default/classes/lab/env/dev.yml
@@ -0,0 +1,4 @@
+
+parameters:
+  lab:
+    name: dev
diff --git a/test/model/default/classes/second.yml b/test/model/default/classes/second.yml
new file mode 100644
index 0000000..dab50c7
--- /dev/null
+++ b/test/model/default/classes/second.yml
@@ -0,0 +1,9 @@
+classes:
+- first
+
+parameters:
+  will:
+    warn:
+      at:
+        second: ${_param:notfound}
+  three: ${one}
diff --git a/test/model/default/classes/third.yml b/test/model/default/classes/third.yml
new file mode 100644
index 0000000..135acd4
--- /dev/null
+++ b/test/model/default/classes/third.yml
@@ -0,0 +1,18 @@
+classes:
+- second
+
+parameters:
+  _param:
+    notfound: exist
+  myparam: ${_param:some}
+  will:
+    not:
+      fail:
+        at:
+          tree: ${_param:notfound}
+  three: ${two}
+  empty:
+    list: []
+    dict: {}
+  ~list_to_override: ${empty:list}
+  ~dict_to_override: ${empty:dict}
diff --git a/test/model/default/nodes/reclass.yml b/test/model/default/nodes/reclass.yml
new file mode 100644
index 0000000..94b7519
--- /dev/null
+++ b/test/model/default/nodes/reclass.yml
@@ -0,0 +1,3 @@
+
+classes:
+- third
diff --git a/test/model/default/reclass-config.yml b/test/model/default/reclass-config.yml
new file mode 100644
index 0000000..9d8f30f
--- /dev/null
+++ b/test/model/default/reclass-config.yml
@@ -0,0 +1 @@
+storage_type: yaml_fs
diff --git a/test/model/extensions/classes/first.yml b/test/model/extensions/classes/first.yml
new file mode 100644
index 0000000..96ece27
--- /dev/null
+++ b/test/model/extensions/classes/first.yml
@@ -0,0 +1,6 @@
+parameters:
+  _param:
+    some: param
+  lab:
+    name: test
+    label: first
diff --git a/test/model/extensions/classes/lab/env/dev.yml b/test/model/extensions/classes/lab/env/dev.yml
new file mode 100644
index 0000000..0cce363
--- /dev/null
+++ b/test/model/extensions/classes/lab/env/dev.yml
@@ -0,0 +1,4 @@
+
+parameters:
+  lab:
+    name: dev
diff --git a/test/model/extensions/classes/second.yml b/test/model/extensions/classes/second.yml
new file mode 100644
index 0000000..a9babd3
--- /dev/null
+++ b/test/model/extensions/classes/second.yml
@@ -0,0 +1,8 @@
+classes:
+- first
+
+parameters:
+  will:
+    warn:
+      at:
+        second: ${_param:notfound}
diff --git a/test/model/extensions/classes/third.yml b/test/model/extensions/classes/third.yml
new file mode 100644
index 0000000..20a937c
--- /dev/null
+++ b/test/model/extensions/classes/third.yml
@@ -0,0 +1,13 @@
+classes:
+- missing.class
+- second
+
+parameters:
+  _param:
+    notfound: exist
+  myparam: ${_param:some}
+  will:
+    not:
+      fail:
+        at:
+          tree: ${_param:notfound}
diff --git a/test/model/extensions/nodes/reclass.yml b/test/model/extensions/nodes/reclass.yml
new file mode 100644
index 0000000..94b7519
--- /dev/null
+++ b/test/model/extensions/nodes/reclass.yml
@@ -0,0 +1,3 @@
+
+classes:
+- third
diff --git a/test/model/extensions/reclass-config.yml b/test/model/extensions/reclass-config.yml
new file mode 100644
index 0000000..6e2f101
--- /dev/null
+++ b/test/model/extensions/reclass-config.yml
@@ -0,0 +1,3 @@
+storage_type: yaml_fs
+ignore_class_notfound: True
+ignore_class_regexp: ['.*']