Initial commit
diff --git a/CHANGELOG.rst b/CHANGELOG.rst
new file mode 100644
index 0000000..a3a1729
--- /dev/null
+++ b/CHANGELOG.rst
@@ -0,0 +1,9 @@
+
+===============
+jenkins-formula
+===============
+
+0.1
+---
+
+- Initial commit to Community formula form
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..cc41a65
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,15 @@
+
+   Copyright (c) 2013 Salt Stack Formulas
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..f5acedc
--- /dev/null
+++ b/README.md
@@ -0,0 +1,130 @@
+
+# Jenkins continuos integration
+
+Jenkins is an application that monitors executions of repeated jobs, such as building a software project or jobs run by cron.
+
+## Sample pillars
+
+Jenkins masters
+
+Jenkins master with user
+
+    jenkins:
+      master:
+        enabled: true
+        http:
+          address: 0.0.0.0
+          port: 8080
+          protocol: http
+        plugins:
+        - name: git
+        - name: metadata
+        - name: envinject
+        - name: greenballs
+        - name: ansicolor
+        - name: build-pipeline-plugin
+        user:
+          admin:
+            api_token: api_token
+            password_hash: salt:hashed_pwd_see_usage
+            email: root@dmain.com
+            public_keys:
+            - key: ssh_public_key_of_current_root_user
+        slaves:
+        - name: slave1.domain.com
+          executors: 2
+
+Jenkins master with SSL
+
+    jenkins:
+      master:
+        enabled: true
+        http:
+          address: 0.0.0.0
+          port: 8080
+          protocol: https
+        ssl:
+          enabled: true
+          host: ci.domain.com
+          authority: Org_Service_CA
+        plugins:
+        - name: git
+        - name: metadata
+        - name: envinject
+        - name: greenballs
+        - name: ansicolor
+        - name: build-pipeline-plugin
+        slaves:
+        - name: slave1.domain.com
+          executors: 2
+
+### Jenkins job builder
+
+Jenkins job builder to configure master
+
+    jenkins:
+      job_builder:
+        enabled: true
+        source: git
+        address: https://git.openstack.org/openstack-infra/jenkins-job-builder
+        branch: master
+        config:
+          source: git
+          address: git@repo.domain.com:jenkins/jobs-org.git
+          branch: develop
+        master:
+          host: localhost
+          port: 8080
+          protocol: http
+          user: admin
+          password: fsdfsdf9438r4fessc9sd
+
+### Jenkins slaves
+
+Slave with sudo :o
+
+    jenkins:
+      slave:
+        enabled: true
+        sudo: true
+        master:
+          host: localhost
+          port: 8080
+          protocol: http
+        user:
+          name: admin
+          password: password
+
+Jenkins slave with keystone credentials
+
+    jenkins:
+      slave:
+        enabled: true
+        master:
+          host: localhost
+          port: 8080
+          protocol: http
+        user:
+          name: admin
+          password: password
+        keystone:
+
+## Usage
+
+User password generation. foo is password. bar is salt.
+
+    echo -n 'foo{bar}' | sha256sum
+
+## Read more
+
+* https://wiki.jenkins-ci.org/display/JENKINS/Use+Jenkins
+* https://wiki.jenkins-ci.org/display/JENKINS/Installing+Jenkins+on+Ubuntu
+* https://github.com/jenkinsci/puppet-jenkins
+* https://wiki.jenkins-ci.org/display/JENKINS/Distributed+builds
+* http://ci.openstack.org/jenkins-job-builder/
+* http://ci.openstack.org/jjb.html
+* http://techs.enovance.com/6006/manage-jenkins-jobs-with-yaml
+* http://zeroturnaround.com/rebellabs/top-10-jenkins-featuresplugins/
+* https://wiki.jenkins-ci.org/display/JENKINS/Build+Pipeline+Plugin
+* https://wiki.jenkins-ci.org/display/JENKINS/LTS+Release+Line
+* https://gist.github.com/rowan-m/1026918
diff --git a/README.rst b/README.rst
new file mode 100644
index 0000000..9a006c9
--- /dev/null
+++ b/README.rst
@@ -0,0 +1,139 @@
+=======
+Jenkins
+=======
+
+Install and configure Jenkins master and slave.
+
+Available states
+================
+
+.. contents::
+    :local:
+
+``jenkins.master``
+------------------
+
+Setup jenkins master
+
+``jenkins.slave``
+-----------------
+
+Setup jenkins slave
+
+``jenkins.job_builder``
+-----------------------
+
+Setup jenkins job builder
+
+Available metadata
+==================
+
+.. contents::
+    :local:
+
+``metadata.jenkins.master.single``
+----------------------------------
+
+Setup single-node master
+
+
+``metadata.jenkins.slave.single``
+---------------------------------
+
+Setup Jenkins slave
+
+Configuration parameters
+========================
+
+
+Example reclass
+===============
+
+Master
+------
+
+.. code-block:: yaml
+
+   classes:
+   - service.jenkins.master
+
+  parameters:
+    _param:
+      jenkins_admin_token: xyz
+      jenkins_admin_password_hash: xyz
+      jenkins_admin_password: xyz
+      job_builder_config_address: git@github.com:xyz/myjobs.git
+      job_builder_config_branch: master
+    nginx:
+      server:
+        site:
+          jenkins:
+            enabled: true
+            type: nginx_proxy
+            name: jenkins
+            proxy:
+              host: 127.0.0.1
+              port: 8080
+              protocol: http
+            host:
+              name: jenkins.example.com
+              port: 80
+    jenkins:
+      master:
+        mode: EXCLUSIVE
+        slaves:
+          - name: slave01
+             label: pbuilder
+             executors: 2
+          - name: slave02
+             label: image_builder
+             mode: EXCLUSIVE
+             executors: 2
+        views:
+          - name: "Package builds"
+            regex: "debian-build-.*"
+          - name: "Contrail builds"
+            regex: "contrail-build-.*"
+          - name: "Aptly"
+            regex: "aptly-.*"
+        plugins:
+        - name: slack
+        - name: extended-choice-parameter
+        - name: rebuild
+        - name: test-stability
+
+Slave
+-----
+
+.. code-block:: yaml
+
+     classes:
+     - service.jenkins.slave.single
+     - service.java.environment
+
+     parameters:
+        _param:
+          java_environment_platform: openjdk
+          java_environment_version: 7
+
+        jenkins:
+          slave:
+            master:
+              host: jenkins.example.com
+              port: 80
+            user:
+              name: jenkins_slave
+              password: dexiech6AepohthaiHook2iesh7ol5ook4Ov3leid3yek6daid2ooNg3Ee2oKeYo
+            gpg:
+              keypair_id: A76882D3
+              public_key: |
+                -----BEGIN PGP PUBLIC KEY BLOCK-----
+                ...
+              private_key: |
+                -----BEGIN PGP PRIVATE KEY BLOCK-----
+                ...
+
+Read more
+=========
+
+* https://wiki.jenkins-ci.org/display/JENKINS/Use+Jenkins
diff --git a/VERSION b/VERSION
new file mode 100644
index 0000000..3b04cfb
--- /dev/null
+++ b/VERSION
@@ -0,0 +1 @@
+0.2
diff --git a/debian/changelog b/debian/changelog
new file mode 100644
index 0000000..5d7eafa
--- /dev/null
+++ b/debian/changelog
@@ -0,0 +1,11 @@
+salt-formula-jenkins (0.2) trusty; urgency=medium
+
+  * First public release
+
+ -- Filip Pytloun <filip.pytloun@tcpcloud.eu>  Tue, 06 Oct 2015 16:38:43 +0200
+
+salt-formula-jenkins (0.1) trusty; urgency=medium
+
+  * Initial release
+
+ -- Filip Pytloun <filip.pytloun@tcpcloud.eu>  Thu, 13 Aug 2015 23:23:41 +0200
diff --git a/debian/compat b/debian/compat
new file mode 100644
index 0000000..ec63514
--- /dev/null
+++ b/debian/compat
@@ -0,0 +1 @@
+9
diff --git a/debian/control b/debian/control
new file mode 100644
index 0000000..c3dbb92
--- /dev/null
+++ b/debian/control
@@ -0,0 +1,15 @@
+Source: salt-formula-jenkins
+Maintainer: Filip Pytloun <filip.pytloun@tcpcloud.eu>
+Section: admin
+Priority: optional
+Build-Depends: debhelper (>= 9)
+Standards-Version: 3.9.6
+Homepage: http://www.tcpcloud.eu
+Vcs-Browser: https://github.com/tcpcloud/salt-formula-jenkins
+Vcs-Git: https://github.com/tcpcloud/salt-formula-jenkins.git
+
+Package: salt-formula-jenkins
+Architecture: all
+Depends: ${misc:Depends}, salt-master, reclass
+Description: Jenkins Salt formula
+ Install and configure Jenkins master and slave.
diff --git a/debian/copyright b/debian/copyright
new file mode 100644
index 0000000..310c441
--- /dev/null
+++ b/debian/copyright
@@ -0,0 +1,15 @@
+Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
+Upstream-Name: salt-formula-jenkins
+Upstream-Contact: Filip Pytloun <filip.pytloun@tcpcloud.eu>
+Source: https://github.com/tcpcloud/salt-formula-jenkins
+
+Files: *
+Copyright: 2014-2015 tcp cloud
+License: Apache-2.0
+  Copyright (C) 2014-2015 tcp cloud
+  .
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  .
+  On a Debian system you can find a copy of this license in
+  /usr/share/common-licenses/Apache-2.0.
diff --git a/debian/docs b/debian/docs
new file mode 100644
index 0000000..d585829
--- /dev/null
+++ b/debian/docs
@@ -0,0 +1,3 @@
+README.rst
+CHANGELOG.rst
+VERSION
diff --git a/debian/install b/debian/install
new file mode 100644
index 0000000..7e6fb6f
--- /dev/null
+++ b/debian/install
@@ -0,0 +1,2 @@
+jenkins/*             /usr/share/salt-formulas/env/jenkins/
+metadata/service/*    /usr/share/salt-formulas/reclass/service/jenkins/
diff --git a/debian/rules b/debian/rules
new file mode 100755
index 0000000..abde6ef
--- /dev/null
+++ b/debian/rules
@@ -0,0 +1,5 @@
+#!/usr/bin/make -f
+
+%:
+	dh $@
+
diff --git a/debian/source/format b/debian/source/format
new file mode 100644
index 0000000..89ae9db
--- /dev/null
+++ b/debian/source/format
@@ -0,0 +1 @@
+3.0 (native)
diff --git a/jenkins/files/config.xml b/jenkins/files/config.xml
new file mode 100644
index 0000000..5ac2ef8
--- /dev/null
+++ b/jenkins/files/config.xml
@@ -0,0 +1,98 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<hudson>
+  <disabledAdministrativeMonitors/>
+  <version>1.0</version>
+  <numExecutors>2</numExecutors>
+  <mode>{{ pillar.jenkins.master.mode }}</mode>
+  <useSecurity>true</useSecurity>
+  <authorizationStrategy class="hudson.security.FullControlOnceLoggedInAuthorizationStrategy"/>
+  <securityRealm class="hudson.security.HudsonPrivateSecurityRealm">
+    <disableSignup>true</disableSignup>
+    <enableCaptcha>false</enableCaptcha>
+  </securityRealm>
+  <disableRememberMe>false</disableRememberMe>
+  <projectNamingStrategy class="jenkins.model.ProjectNamingStrategy$DefaultProjectNamingStrategy"/>
+  <workspaceDir>${ITEM_ROOTDIR}/workspace</workspaceDir>
+  <buildsDir>${ITEM_ROOTDIR}/builds</buildsDir>
+  <markupFormatter class="hudson.markup.RawHtmlMarkupFormatter"  plugin="antisamy-markup-formatter@1.1">
+    <disableSyntaxHighlighting>false</disableSyntaxHighlighting>
+  </markupFormatter>
+  <jdks/>
+  <viewsTabBar class="hudson.views.DefaultViewsTabBar"/>
+  <myViewsTabBar class="hudson.views.DefaultMyViewsTabBar"/>
+  <clouds/>
+  <slaves>
+    {%- if pillar.jenkins.master.slaves is defined %}
+    {%- for slave in pillar.jenkins.master.slaves %}
+    <slave>
+      <name>{{ slave.name }}</name>
+      <description></description>
+      <remoteFS>/var/lib/jenkins</remoteFS>
+      <numExecutors>{{ slave.executors }}</numExecutors>
+      <mode>{%- if slave.mode is defined %}{{ slave.mode }}{%- else %}NORMAL{%- endif %}</mode>
+      <retentionStrategy class="hudson.slaves.RetentionStrategy$Always"/>
+      <launcher class="hudson.slaves.JNLPLauncher"/>
+      <label>{%- if slave.label is defined %}{{ slave.label }}{%- endif %}</label>
+      <nodeProperties>
+        <hudson.slaves.EnvironmentVariablesNodeProperty>
+          <envVars serialization="custom">
+            <unserializable-parents/>
+            <tree-map>
+              <default>
+                <comparator class="hudson.util.CaseInsensitiveComparator"/>
+              </default>
+              <int>0</int>
+            </tree-map>
+          </envVars>
+        </hudson.slaves.EnvironmentVariablesNodeProperty>
+        <node-metadata plugin="metadata@1.0b">
+          <values class="linked-list"/>
+        </node-metadata>
+      </nodeProperties>
+      <userId>admin</userId>
+    </slave>
+    {%- endfor %}
+    {%- endif %}
+  </slaves>
+  <scmCheckoutRetryCount>0</scmCheckoutRetryCount>
+  <views>
+    <hudson.model.AllView>
+      <owner class="hudson" reference="../../.."/>
+      <name>All</name>
+      <filterExecutors>false</filterExecutors>
+      <filterQueue>false</filterQueue>
+      <properties class="hudson.model.View$PropertyList"/>
+    </hudson.model.AllView>
+    {%- if pillar.jenkins.master.views is defined %}
+    {%- for view in pillar.jenkins.master.views %}
+    <listView>
+      <owner class="hudson" reference="../../.."/>
+      <name>{{ view.name }}</name>
+      <filterExecutors>false</filterExecutors>
+      <filterQueue>false</filterQueue>
+      <properties class="hudson.model.View$PropertyList"/>
+      <jobNames>
+        <comparator class="hudson.util.CaseInsensitiveComparator"/>
+      </jobNames>
+      <jobFilters/>
+      <columns>
+        <hudson.views.StatusColumn/>
+        <hudson.views.WeatherColumn/>
+        <hudson.views.JobColumn/>
+        <hudson.views.LastSuccessColumn/>
+        <hudson.views.LastFailureColumn/>
+        <hudson.views.LastDurationColumn/>
+        <hudson.views.BuildButtonColumn/>
+      </columns>
+      <includeRegex>{{ view.regex }}</includeRegex>
+      <recurse>false</recurse>
+    </listView>
+    {%- endfor %}
+    {%- endif %}
+  </views>
+  <primaryView>All</primaryView>
+  <slaveAgentPort>0</slaveAgentPort>
+  <label>master</label>
+  <nodeProperties/>
+  <globalNodeProperties/>
+</hudson>
diff --git a/jenkins/files/config.xml.user b/jenkins/files/config.xml.user
new file mode 100644
index 0000000..9d442a4
--- /dev/null
+++ b/jenkins/files/config.xml.user
@@ -0,0 +1,42 @@
+{%- set user = salt['pillar.get']('jenkins:master:user:'+user_name) -%}
+<?xml version='1.0' encoding='UTF-8'?>
+<user>
+  <fullName>admin</fullName>
+  <properties>
+    <hudson.model.PaneStatusProperties>
+      <collapsed/>
+    </hudson.model.PaneStatusProperties>
+    <jenkins.security.ApiTokenProperty>
+      <apiToken>{{ user.api_token }}</apiToken>
+    </jenkins.security.ApiTokenProperty>
+    <com.cloudbees.plugins.credentials.UserCredentialsProvider_-UserCredentialsProperty plugin="credentials@1.9.4">
+      <domainCredentialsMap class="hudson.util.CopyOnWriteMap$Hash"/>
+    </com.cloudbees.plugins.credentials.UserCredentialsProvider_-UserCredentialsProperty>
+    <hudson.model.MyViewsProperty>
+      <views>
+        <hudson.model.AllView>
+          <owner class="hudson.model.MyViewsProperty" reference="../../.."/>
+          <name>All</name>
+          <filterExecutors>false</filterExecutors>
+          <filterQueue>false</filterQueue>
+          <properties class="hudson.model.View$PropertyList"/>
+        </hudson.model.AllView>
+      </views>
+    </hudson.model.MyViewsProperty>
+    <hudson.search.UserSearchProperty>
+      <insensitiveSearch>false</insensitiveSearch>
+    </hudson.search.UserSearchProperty>
+    <hudson.security.HudsonPrivateSecurityRealm_-Details>
+      <passwordHash>{{ user.password_hash }}</passwordHash>
+    </hudson.security.HudsonPrivateSecurityRealm_-Details>
+    {%- if user.public_keys is defined %}
+    <org.jenkinsci.main.modules.cli.auth.ssh.UserPropertyImpl>
+      <authorizedKeys>{% for key in user.public_keys %}{{ key.key }}{% if not loop.last %}
+      {% endif %}{% endfor %}</authorizedKeys>
+    </org.jenkinsci.main.modules.cli.auth.ssh.UserPropertyImpl>
+    {% endif %}
+    <hudson.tasks.Mailer_-UserProperty plugin="mailer@1.8">
+      <emailAddress>{{ user.get("email", "root@local.domain") }}</emailAddress>
+    </hudson.tasks.Mailer_-UserProperty>
+  </properties>
+</user>
\ No newline at end of file
diff --git a/jenkins/files/init.d/jenkins-slave b/jenkins/files/init.d/jenkins-slave
new file mode 100644
index 0000000..9aa98f3
--- /dev/null
+++ b/jenkins/files/init.d/jenkins-slave
@@ -0,0 +1,80 @@
+#!/bin/bash
+
+### BEGIN INIT INFO
+# Provides:		jenkins-slave
+# Required-Start:	$remote_fs $syslog
+# Required-Stop:	$remote_fs $syslog
+# Default-Start:	2 3 4 5
+# Default-Stop:
+# Short-Description:	Jenkins slave
+### END INIT INFO
+
+set -e
+
+# /etc/init.d/jenkins-slave: start and stop Jenkins slave
+
+if test -f /etc/default/jenkins-slave; then
+	. /etc/default/jenkins-slave
+else
+	echo "Configuration for jenkins-slave does not exist" 1>&2
+	exit 1
+fi
+
+. /lib/lsb/init-functions
+
+COMMAND="/usr/bin/java"
+COMMAND_ARGS="-jar slave.jar ${JENKINS_ARGS}"
+
+function check_running() {
+	start-stop-daemon -T --pidfile ${PIDFILE}
+	ret=$?
+	if [ $ret -ne 0 ]; then
+		log_end_msg 1 || true
+		exit $ret
+	fi
+}
+
+function start() {
+	# Get slave.jar from master
+	[ -f ${JENKINS_HOME}/slave.jar ] || wget -O ${JENKINS_HOME}/slave.jar -q ${JENKINS_URL}/jnlpJars/slave.jar
+
+	if start-stop-daemon -c ${JENKINS_USER} -d ${JENKINS_HOME} --background --start --quiet --oknodo -m --pidfile ${PIDFILE} --exec ${COMMAND} -- ${COMMAND_ARGS}; then
+		log_end_msg 0 || true
+	else
+		log_end_msg 1 || true
+	fi
+}
+
+function stop() {
+	if start-stop-daemon --stop --quiet --oknodo --pidfile ${PIDFILE}; then
+		log_end_msg 0 || true
+	else
+		log_end_msg 1 || true
+	fi
+}
+
+case "$1" in
+	start)
+		log_daemon_msg "Starting Jenkins slave" "jenkins-slave" || true
+		start
+		;;
+	stop)
+		log_daemon_msg "Stopping Jenkins slave" "jenkins-slave" || true
+		stop
+		;;
+	status)
+		status_of_proc -p ${PIDFILE} ${COMMAND} jenkins-slave && exit 0 || exit $?
+		;;
+	restart)
+		log_daemon_msg "Restarting Jenkins slave" "jenkins-slave" || true
+		stop
+		start
+		;;
+
+	*)
+		log_action_msg "Usage: /etc/init.d/jenkins-slave {start|stop|restart}" || true
+		exit 1
+		;;
+esac
+
+exit 0
diff --git a/jenkins/files/jenkins b/jenkins/files/jenkins
new file mode 100644
index 0000000..f51ddb6
--- /dev/null
+++ b/jenkins/files/jenkins
@@ -0,0 +1,59 @@
+# defaults for jenkins continuous integration server
+
+# pulled in from the init script; makes things easier.
+NAME=jenkins
+
+# location of java
+JAVA=/usr/bin/java
+
+# arguments to pass to java
+#JAVA_ARGS="-Xmx256m"
+JAVA_ARGS="-Djava.net.preferIPv4Stack=true" # make jenkins listen on IPv4 address
+
+PIDFILE=/var/run/jenkins/jenkins.pid
+
+# user id to be invoked as (otherwise will run as root; not wise!)
+JENKINS_USER=jenkins
+
+# location of the jenkins war file
+JENKINS_WAR=/usr/share/jenkins/jenkins.war
+
+# jenkins home location
+JENKINS_HOME=/var/lib/jenkins
+
+# set this to false if you don't want Hudson to run by itself
+# in this set up, you are expected to provide a servlet container
+# to host jenkins.
+RUN_STANDALONE=true
+
+# log location.  this may be a syslog facility.priority
+JENKINS_LOG=/var/log/jenkins/$NAME.log
+#HUDSON_LOG=daemon.info
+
+# OS LIMITS SETUP
+#   comment this out to observe /etc/security/limits.conf
+#   this is on by default because http://github.com/jenkinsci/jenkins/commit/2fb288474e980d0e7ff9c4a3b768874835a3e92e
+#   reported that Ubuntu's PAM configuration doesn't include pam_limits.so, and as a result the # of file
+#   descriptors are forced to 1024 regardless of /etc/security/limits.conf
+MAXOPENFILES=8192
+
+# port for HTTP connector (default 8080; disable with -1)
+HTTP_PORT={{ pillar.jenkins.master.http.port }}
+
+# port for AJP connector (disabled by default)
+AJP_PORT=-1
+
+# servlet context, important if you want to use apache proxying  
+PREFIX=/jenkins
+
+# arguments to pass to jenkins.
+# --javahome=$JAVA_HOME
+# --httpPort=$HTTP_PORT (default 8080; disable with -1)
+# --httpsPort=$HTTP_PORT
+# --ajp13Port=$AJP_PORT
+# --argumentsRealm.passwd.$ADMIN_USER=[password]
+# --argumentsRealm.roles.$ADMIN_USER=admin
+# --webroot=~/.jenkins/war
+# --prefix=$PREFIX
+
+JENKINS_ARGS="--webroot=/var/cache/jenkins/war --httpPort=$HTTP_PORT --ajp13Port=$AJP_PORT"
diff --git a/jenkins/files/jenkins-slave b/jenkins/files/jenkins-slave
new file mode 100644
index 0000000..396aff3
--- /dev/null
+++ b/jenkins/files/jenkins-slave
@@ -0,0 +1,50 @@
+{%- from "jenkins/map.jinja" import slave with context %}
+# defaults for jenkins-slave component of the jenkins continuous integration 
+# system
+
+# pulled in from the init script; makes things easier.
+NAME=jenkins-slave
+
+# location of java
+JAVA=/usr/bin/java
+
+# arguments to pass to java - optional
+#JAVA_ARGS="-Xmx256m"
+
+# for daemon to use
+PIDFILE=/var/run/$NAME.pid
+
+# user id to be invoked as (otherwise will run as root; not wise!)
+JENKINS_USER=jenkins
+
+# location of jenkins arch indep files
+JENKINS_ROOT=/usr/share/jenkins
+
+# jenkins home location
+JENKINS_HOME=/var/lib/jenkins
+
+# jenkins /run location
+JENKINS_RUN=/var/run/jenkins
+
+# URL of jenkins server to connect to 
+JENKINS_URL="{{ slave.master.protocol }}://{{ slave.master.host }}:{{ slave.master.port }}"
+
+# Name of slave configuration to use at JENKINS_URL
+# Override if it need to be something other than the
+# hostname of the server the slave is running on.
+JENKINS_HOSTNAME="{{ slave.hostname }}"
+
+# Log file location for use in Debian init script
+JENKINS_SLAVE_LOG=/var/log/jenkins/$NAME.log
+
+# OS LIMITS SETUP
+#   comment this out to observe /etc/security/limits.conf
+#   this is on by default because http://github.com/feniix/hudson/commit/d13c08ea8f5a3fa730ba174305e6429b74853927
+#   reported that Ubuntu's PAM configuration doesn't include pam_limits.so, and as a result the # of file
+#   descriptors are forced to 1024 regardless of /etc/security/limits.confa
+# NOTE - Ubuntu Users - this is not used by the upstart configuration - please use an upstart overrides file
+# to change the OS limits setup.
+MAXOPENFILES=8192
+
+# Arguments to pass to jenkins slave on startup
+JENKINS_ARGS="-jnlpUrl $JENKINS_URL/computer/$JENKINS_HOSTNAME/slave-agent.jnlp -jnlpCredentials {{ slave.user.name }}:{{ slave.user.password }}"
diff --git a/jenkins/files/jenkins_jobs.ini b/jenkins/files/jenkins_jobs.ini
new file mode 100644
index 0000000..d71f07d
--- /dev/null
+++ b/jenkins/files/jenkins_jobs.ini
@@ -0,0 +1,7 @@
+[job_builder]
+recursive=True
+
+[jenkins]
+user={{ pillar.jenkins.job_builder.master.user }}
+password={{ pillar.jenkins.job_builder.master.password }}
+url={{ pillar.jenkins.job_builder.master.protocol }}://{{ pillar.jenkins.job_builder.master.host }}:{{ pillar.jenkins.job_builder.master.port }}
diff --git a/jenkins/files/keystonerc b/jenkins/files/keystonerc
new file mode 100644
index 0000000..d9d2e38
--- /dev/null
+++ b/jenkins/files/keystonerc
@@ -0,0 +1,5 @@
+{%- set server = pillar.jenkins.slave.keystone %}
+export OS_USERNAME="{{ server.user }}"
+export OS_PASSWORD="{{ server.password }}"
+export OS_TENANT_NAME="{{ server.tenant }}"
+export OS_AUTH_URL="{{ server.url }}"
diff --git a/jenkins/files/pbuilderrc b/jenkins/files/pbuilderrc
new file mode 100644
index 0000000..1d67250
--- /dev/null
+++ b/jenkins/files/pbuilderrc
@@ -0,0 +1,54 @@
+{%- from "jenkins/map.jinja" import slave with context %}
+{%- if slave.pbuilder is defined %}
+
+{%- if slave.pbuilder.mirrorsite is defined %}
+MIRRORSITE="{{ slave.pbuilder.mirrorsite }}"
+{%- endif %}
+
+{%- if slave.pbuilder.components is defined %}
+COMPONENTS="{{ slave.pbuilder.components }}"
+{%- endif %}
+
+{%- set keyring = slave.pbuilder.get('keyring', '/etc/apt/trusted.gpg') %}
+DEBOOTSTRAPOPTS=(${DEBOOTSTRAPOPTS[@]} "--keyring={{ keyring }}")
+APTKEYRINGS=(${APTKEYRINGS[@]} "{{ keyring }}")
+
+{%- if slave.pbuilder.aptcache is defined %}
+APTCACHE="{{ slave.pbuilder.aptcache }}"
+{%- endif %}
+
+{%- if slave.pbuilder.aptcachehardlink is defined %}
+APTCACHEHARDLINK="{{ 'yes' if slave.pbuilder.aptcachehardlink else 'no' }}"
+{%- endif %}
+
+{%- if slave.pbuilder.buildplace is defined %}
+BUILDPLACE="{{ slave.pbuilder.buildplace }}"
+{%- endif %}
+
+{%- if slave.pbuilder.buildresult is defined %}
+BUILDRESULT="{{ slave.pbuilder.buildresult }}"
+{%- endif %}
+
+{%- if slave.pbuilder.ccachedir is defined %}
+CCACHEDIR="{{ slave.pbuilder.ccachedir }}"
+{%- endif %}
+
+{%- if slave.pbuilder.usenetwork is defined %}
+USENETWORK="{{ 'yes' if slave.pbuilder.usenetwork else 'no' }}"
+{%- endif %}
+
+{%- if slave.pbuilder.parallel is defined %}
+
+{%- if slave.pbuilder.parallel %}
+{# Automatically set jobs to no. of cpus #}
+JOBS=$(grep -c processor /proc/cpuinfo)
+{%- else %}
+JOBS={{ slave.pbuilder.parallel }}
+{%- endif %}
+
+DEB_BUILD_OPTIONS="parallel=${JOBS} $DEB_BUILD_OPTIONS"
+
+{%- endif %}
+
+BINDMOUNTS="/var/cache/pbuilder/build"
+{%- endif %}
diff --git a/jenkins/files/sudoer b/jenkins/files/sudoer
new file mode 100644
index 0000000..a06260b
--- /dev/null
+++ b/jenkins/files/sudoer
@@ -0,0 +1,5 @@
+# jenkins user with passwordless sudo functionality.
+
+jenkins ALL=(ALL) NOPASSWD:ALL
+
+Defaults:jenkins !requiretty
diff --git a/jenkins/files/sudoer_debian_glue b/jenkins/files/sudoer_debian_glue
new file mode 100644
index 0000000..988682d
--- /dev/null
+++ b/jenkins/files/sudoer_debian_glue
@@ -0,0 +1,4 @@
+# allow jenkins to use cowbuilder, preserve important environment variables
+
+jenkins ALL=NOPASSWD: /usr/sbin/cowbuilder, /usr/sbin/chroot
+Defaults env_keep+="DEB_* DIST ARCH ADT"
diff --git a/jenkins/init.sls b/jenkins/init.sls
new file mode 100644
index 0000000..985416e
--- /dev/null
+++ b/jenkins/init.sls
@@ -0,0 +1,11 @@
+
+include:
+{% if pillar.jenkins.master is defined %}
+- jenkins.master
+{% endif %}
+{% if pillar.jenkins.job_builder is defined %}
+- jenkins.job_builder
+{% endif %}
+{% if pillar.jenkins.slave is defined %}
+- jenkins.slave
+{% endif %}
diff --git a/jenkins/job_builder.sls b/jenkins/job_builder.sls
new file mode 100644
index 0000000..d1525ba
--- /dev/null
+++ b/jenkins/job_builder.sls
@@ -0,0 +1,83 @@
+{% from "jenkins/map.jinja" import job_builder with context %}
+{%- if pillar.jenkins.job_builder.enabled %}
+
+include:
+- git
+
+{%- if not job_builder.pkgs %}
+# Install job builder with pip if we don't have package
+
+include:
+- python
+
+jenkins_job_builder_install:
+  pip.installed:
+    - names:
+      - jenkins-job-builder
+    - require:
+      - pkg: jenkins_job_builder_packages
+
+{%- else %}
+
+jenkins_job_builder_install:
+  pkg.installed:
+    - names: {{ job_builder.pkgs }}
+    - require:
+      - pkg: jenkins_job_builder_packages
+
+jenkins_job_builder_packages:
+  pkg.installed:
+  - names:
+    - python-yaml
+    - python-jenkins
+    - python-pip
+    - python-pbr
+  - require:
+    - pkg: python_packages
+
+{%- endif %}
+
+/srv/jenkins:
+  file.directory:
+  - user: root
+  - group: root
+  - mode: 755
+  - makedirs: true
+
+{{ job_builder.conf_dir }}:
+  file.directory:
+  - user: root
+  - group: root
+  - mode: 755
+  - makedirs: true
+
+{{ job_builder.config_file }}:
+  file.managed:
+  - source: salt://jenkins/files/jenkins_jobs.ini
+  - user: root
+  - group: root
+  - mode: 400
+  - template: jinja
+  - require:
+    - file: /etc/jenkins_jobs
+
+{{ pillar.jenkins.job_builder.config.address }}:
+  git.latest:
+  - target: /srv/jenkins/job_builder_config
+  - rev: {{ pillar.jenkins.job_builder.config.branch }}
+  - require:
+    - file: /srv/jenkins
+
+jenkins_job_builder_jobs_update:
+  cmd.run:
+  - name: jenkins-jobs update /srv/jenkins/job_builder_config
+  - require:
+    - git: {{ pillar.jenkins.job_builder.config.address }}
+    - file: {{ job_builder.config_file }}
+    {%- if not job_builder.pkgs %}
+    - pkg: jenkins_job_builder_install
+    {%- else %}
+    - pip: jenkins_job_builder_install
+    {%- endif %}
+
+{%- endif %}
diff --git a/jenkins/map.jinja b/jenkins/map.jinja
new file mode 100644
index 0000000..5d446ca
--- /dev/null
+++ b/jenkins/map.jinja
@@ -0,0 +1,49 @@
+
+{% set master = salt['grains.filter_by']({
+    'Debian': {
+        'pkgs': ['jenkins'],
+        'service': 'jenkins',
+        'config': '/etc/default/jenkins',
+    },
+    'RedHat': {
+        'pkgs': ['jenkins'],
+        'service': 'jenkins',
+        'config': '/etc/sysconfig/jenkins',
+    },
+}, merge=salt['pillar.get']('jenkins:master')) %}
+
+{% set job_builder = salt['grains.filter_by']({
+    'Debian': {
+        'pkgs': ['jenkins-job-builder', 'jenkins-jobs-slack'],
+        'config_file': '/etc/jenkins_jobs/jenkins_jobs.ini',
+        'conf_dir': '/etc/jenkins_jobs',
+    },
+    'RedHat': {
+        'pkgs': ['jenkins-job-builder', 'jenkins-jobs-slack'],
+        'config_file': '/etc/jenkins_jobs/jenkins_jobs.ini',
+        'conf_dir': '/etc/jenkins_jobs',
+    },
+}, merge=salt['pillar.get']('jenkins:job_builder')) %}
+
+{% if pillar.system is defined %}
+{%- set hostname = pillar.system.name %}
+{%- else %}
+{%- set hostname = pillar.linux.system.name+'.'+pillar.linux.system.domain %}
+{%- endif %}
+
+{% set slave = salt['grains.filter_by']({
+    'Debian': {
+        'pkgs': ['jenkins-slave'],
+        'init_script': '/etc/init.d/jenkins-slave',
+        'service': 'jenkins-slave',
+        'hostname': hostname,
+        'config': '/etc/default/jenkins-slave',
+    },
+    'RedHat': {
+        'pkgs': ['jenkins-slave'],
+        'init_script': '/etc/init.d/jenkins-slave',
+        'hostname': hostname,
+        'service': 'jenkins-slave',
+        'config': '/etc/sysconfig/jenkins-slave',
+    },
+}, merge=salt['pillar.get']('jenkins:slave')) %}
diff --git a/jenkins/master/init.sls b/jenkins/master/init.sls
new file mode 100644
index 0000000..8fbf128
--- /dev/null
+++ b/jenkins/master/init.sls
@@ -0,0 +1,58 @@
+{% from "jenkins/map.jinja" import master with context %}
+
+{%- if master.enabled %}
+
+include:
+- java
+- jenkins.master.users
+{%- if master.plugins is defined %}
+- jenkins.master.plugins
+{%- endif %}
+
+jenkins_packages:
+  pkg.installed:
+  - names: {{ master.pkgs }}
+  - require:
+    - pkg: java_packages
+
+jenkins_{{ master.config }}:
+  file.managed:
+  - name: {{ master.config }}
+  - source: salt://jenkins/files/jenkins
+  - user: root
+  - group: root
+  - template: jinja
+  - require:
+    - pkg: jenkins_packages
+
+/var/lib/jenkins/config.xml:
+  file.managed:
+  - source: salt://jenkins/files/config.xml
+  - user: jenkins
+  - group: nogroup
+  - template: jinja
+  - require:
+    - pkg: jenkins_packages
+
+{%- if master.get('sudo', false) %}
+
+/etc/sudoers.d/99-jenkins-user:
+  file.managed:
+  - source: salt://jenkins/files/sudoer
+  - template: jinja
+  - user: root
+  - group: root
+  - mode: 440
+  - require:
+    - service: jenkins_master_service
+
+{%- endif %}
+
+jenkins_master_service:
+  service.running:
+  - name: {{ master.service }}
+  - watch:
+    - file: jenkins_{{ master.config }}
+    - file: /var/lib/jenkins/config.xml
+
+{%- endif %}
diff --git a/jenkins/master/plugins.sls b/jenkins/master/plugins.sls
new file mode 100644
index 0000000..49fb6a0
--- /dev/null
+++ b/jenkins/master/plugins.sls
@@ -0,0 +1,28 @@
+{% from "jenkins/map.jinja" import master with context %}
+
+/var/lib/jenkins/updates:
+  file.directory:
+  - user: jenkins
+  - group: nogroup
+
+setup_jenkins_cli:
+  cmd.run:
+  - names:
+    - sleep 30
+    - wget http://localhost:{{ master.http.port }}/jnlpJars/jenkins-cli.jar
+  - unless: "[ -f /root/jenkins-cli.jar ]"
+  - cwd: /root
+  - require:
+    - service: jenkins_master_service
+
+{%- for plugin in master.plugins %}
+
+install_jenkins_plugin_{{ plugin.name }}:
+  cmd.run:
+  - name: java -jar jenkins-cli.jar -s http://localhost:{{ master.http.port }} install-plugin --username admin --password {{ master.user.admin.password }} {{ plugin.name }}
+  - unless: "[ -d /var/lib/jenkins/plugins/{{ plugin.name }} ]"
+  - cwd: /root
+  - require:
+    - cmd: setup_jenkins_cli
+
+{%- endfor %}
diff --git a/jenkins/master/users.sls b/jenkins/master/users.sls
new file mode 100644
index 0000000..cd29822
--- /dev/null
+++ b/jenkins/master/users.sls
@@ -0,0 +1,20 @@
+{% from "jenkins/map.jinja" import master with context %}
+
+{%- for user_name, user in master.user.iteritems() %}
+
+/var/lib/jenkins/users/{{ user_name }}:
+  file.directory:
+  - makedirs: true
+
+/var/lib/jenkins/users/{{ user_name }}/config.xml:
+  file.managed:
+  - source: salt://jenkins/files/config.xml.user
+  - template: jinja
+  - require:
+    - file: /var/lib/jenkins/users/{{ user_name }}
+  - defaults:
+      user_name: "{{ user_name }}"
+  - watch_in:
+    - service: jenkins_master_service
+
+{%- endfor %}
diff --git a/jenkins/slave/gpg.sls b/jenkins/slave/gpg.sls
new file mode 100644
index 0000000..b23001b
--- /dev/null
+++ b/jenkins/slave/gpg.sls
@@ -0,0 +1,44 @@
+{% from "jenkins/map.jinja" import slave with context %}
+
+jenkins_gpg_key_dir:
+  file.directory:
+  - name: /var/lib/jenkins/.gnupg
+  - user: jenkins
+  - group: jenkins
+  - mode: 700
+
+gpg_priv_key:
+  file.managed:
+  - name: /var/lib/jenkins/.gnupg/secret.gpg
+  - contents_pillar: jenkins:slave:gpg:private_key
+  - user: jenkins
+  - group: jenkins
+  - mode: 600
+  - require:
+    - file: jenkins_gpg_key_dir
+
+gpg_pub_key:
+  file.managed:
+  - name: /var/lib/jenkins/.gnupg/public.gpg
+  - contents_pillar: jenkins:slave:gpg:public_key
+  - user: jenkins
+  - group: jenkins
+  - mode: 644
+  - require:
+    - file: jenkins_gpg_key_dir
+
+import_gpg_pub_key:
+  cmd.run:
+  - name: gpg --no-tty --import /var/lib/jenkins/.gnupg/public.gpg
+  - user: jenkins
+  - unless: gpg --no-tty --list-keys | grep '{{ slave.gpg.keypair_id }}'
+  - require:
+    - file: jenkins_gpg_key_dir
+
+import_gpg_priv_key:
+  cmd.run:
+  - name: gpg --no-tty --allow-secret-key-import --import /var/lib/jenkins/.gnupg/secret.gpg
+  - user: jenkins
+  - unless: gpg --no-tty --list-secret-keys | grep '{{ slave.gpg.keypair_id }}'
+  - require:
+    - file: jenkins_gpg_key_dir
diff --git a/jenkins/slave/init.sls b/jenkins/slave/init.sls
new file mode 100644
index 0000000..e34d0ec
--- /dev/null
+++ b/jenkins/slave/init.sls
@@ -0,0 +1,94 @@
+{% from "jenkins/map.jinja" import slave with context %}
+
+{%- if slave.enabled %}
+
+include:
+- java
+{%- if slave.pbuilder is defined %}
+- jenkins.slave.pbuilder
+{%- endif %}
+{%- if slave.gpg is defined %}
+- jenkins.slave.gpg
+{%- endif %}
+{%- if slave.keystone is defined %}
+- jenkins.slave.keystone
+{%- endif %}
+
+{% if slave.pkgs %}
+
+jenkins_slave_package:
+  pkg.installed:
+  - names: {{ slave.pkgs }}
+  - require:
+    - pkg: java_packages
+
+{% else %}
+
+# No jenkins-slave package, use magic init script instead
+{{ slave.init_script }}:
+  file.managed:
+    - source: salt://jenkins/files/init.d/jenkins-slave
+    - user: root
+    - group: root
+    - mode: 755
+    - template: jinja
+
+{% endif %}
+
+{{ slave.config }}:
+  file.managed:
+  - source: salt://jenkins/files/jenkins-slave
+  - user: root
+  - group: root
+  - template: jinja
+  - require:
+    {% if slave.pkgs %}
+    - pkg: jenkins_slave_package
+    {% else %}
+    - file: {{ slave.init_script }}
+    {% endif %}
+
+jenkins_slave_service:
+  service.running:
+  - name: {{ slave.service }}
+  - watch:
+    - file: {{ slave.config }}
+  - require:
+    {% if slave.pkgs %}
+    - pkg: jenkins_slave_package
+    {% else %}
+    - file: {{ slave.init_script }}
+    {% endif %}
+
+{%- if pillar.linux.system.user.jenkins is not defined %}
+
+jenkins_slave_user:
+  user.present:
+  - name: jenkins
+  - shell: /bin/bash
+  - home: /var/lib/jenkins
+  - require_in:
+    {%- if slave.gpg is defined %}
+    - file: jenkins_gpg_key_dir
+    {%- endif %}
+    {%- if slave.pbuilder is defined %}
+    - file: /var/lib/jenkins/pbuilder
+    {%- endif %}
+
+{%- endif %}
+
+{%- if slave.get('sudo', false) %}
+
+/etc/sudoers.d/99-jenkins-user:
+  file.managed:
+  - source: salt://jenkins/files/sudoer
+  - template: jinja
+  - user: root
+  - group: root
+  - mode: 440
+  - require:
+    - service: jenkins_slave_service
+
+{%- endif %}
+
+{%- endif %}
diff --git a/jenkins/slave/keystone.sls b/jenkins/slave/keystone.sls
new file mode 100644
index 0000000..d308f76
--- /dev/null
+++ b/jenkins/slave/keystone.sls
@@ -0,0 +1,19 @@
+{% from "jenkins/map.jinja" import slave with context %}
+
+/var/lib/jenkins/keystonerc:
+  file.managed:
+  - source: salt://jenkins/files/keystonerc
+  - user: jenkins
+  - group: jenkins
+  - mode: 0400
+  - template: jinja
+  - require:
+    - service: jenkins_slave_service
+
+jenkins_slave_openstack_clients:
+  pkg.installed:
+  - names:
+    - python-keystoneclient
+    - python-novaclient
+    - python-neutronclient
+    - python-glanceclient
diff --git a/jenkins/slave/pbuilder.sls b/jenkins/slave/pbuilder.sls
new file mode 100644
index 0000000..b45947c
--- /dev/null
+++ b/jenkins/slave/pbuilder.sls
@@ -0,0 +1,66 @@
+{% from "jenkins/map.jinja" import slave with context %}
+
+jenkins_debian_glue_packages:
+  pkg.latest:
+  - names:
+    - reprepro
+    - cowbuilder
+    - jenkins-debian-glue
+    - git-buildpackage
+    - debhelper
+
+/srv/repository:
+  file:
+  - directory
+  - user: jenkins
+  - mode: 755
+  - makedirs: true
+  - require:
+    - service: jenkins_slave_service
+
+/etc/sudoers.d/98-jenkins-debian-glue:
+  file.managed:
+  - source: salt://jenkins/files/sudoer_debian_glue
+  - template: jinja
+  - user: root
+  - group: root
+  - mode: 440
+  - require:
+    - service: jenkins_slave_service
+
+/etc/pbuilderrc:
+  file.managed:
+    - source: salt://jenkins/files/pbuilderrc
+    - template: jinja
+    - user: root
+    - group: root
+    - mode: 644
+    - require:
+      - pkg: jenkins_debian_glue_packages
+
+/var/cache/pbuilder:
+  file.symlink:
+  - target: /var/lib/jenkins/pbuilder
+  - require:
+    - file: /var/lib/jenkins/pbuilder
+  - require_in:
+    - pkg: jenkins_debian_glue_packages
+
+/var/lib/jenkins/pbuilder:
+  file:
+  - directory
+  - user: jenkins
+  - group: jenkins
+  - mode: 755
+  - makedirs: true
+
+{%- if slave.pbuilder.ccachedir is defined %}
+{{ slave.pbuilder.ccachedir }}:
+  file.directory:
+  - user: root
+  - group: root
+  - mode: 777
+  - makedirs: true
+  - require:
+    - file: /var/cache/pbuilder
+{%- endif %}
diff --git a/metadata/service/master/single.yml b/metadata/service/master/single.yml
new file mode 100644
index 0000000..549d004
--- /dev/null
+++ b/metadata/service/master/single.yml
@@ -0,0 +1,43 @@
+applications:
+- jenkins
+parameters:
+  jenkins:
+    master:
+      enabled: true
+      sudo: false
+      mode: "NORMAL"
+      http:
+        network: any
+        port: 8080
+        protocol: http
+      plugins:
+      - name: git
+      - name: metadata
+      - name: envinject
+      - name: ansicolor
+      - name: greenballs
+      - name: gitlab-hook
+      - name: build-pipeline-plugin
+      - name: copyartifact
+      - name: ws-cleanup
+      - name: parameterized-trigger
+      user:
+        admin:
+          api_token: ${_param:jenkins_admin_token}
+          password: ${_param:jenkins_admin_password}
+          password_hash: ${_param:jenkins_admin_password_hash}
+          email: root@dmain.com
+    job_builder:
+      enabled: true
+      source: git
+      branch: master
+      config:
+        source: git
+        address: ${_param:job_builder_config_address}
+        branch: ${_param:job_builder_config_branch}
+      master:
+        host: localhost
+        port: 8080
+        protocol: http
+        user: admin
+        password: ${_param:jenkins_admin_password}
diff --git a/metadata/service/slave/single.yml b/metadata/service/slave/single.yml
new file mode 100644
index 0000000..cc9690c
--- /dev/null
+++ b/metadata/service/slave/single.yml
@@ -0,0 +1,16 @@
+applications:
+- jenkins
+parameters:
+  jenkins:
+    slave:
+      enabled: true
+      pkgs: false
+      sudo: false
+      scripts: []
+      master:
+        host: 127.0.0.1
+        port: 8080
+        protocol: http
+      user:
+        name: admin
+        password: ${_secret:jenkins_admin_password}