diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..98fb146 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +\#* +.*~ diff --git a/README.md b/README.md new file mode 100644 index 0000000..74042e4 --- /dev/null +++ b/README.md @@ -0,0 +1,17 @@ +# Salt states for CI infrastructure + +This states are not generic and this is done on purpose. The goal is to have maintainable configuration management code and prioritize readability over flexibility. + +## States + +### states/jenkins + +Jenkins state for Debian Jessie. + +Install Jenkins, add plugins according to the hardcoded list, run jimmy to setup basic authentication. + +## Secrets + +Passwords and credentials are stored directly in the repository, but encrypted with gpgkeys. + +To encrypt data one may use utils/encrypt.py script. For it to work you need access to the salt-master gpgkeys. diff --git a/pillars/_secret/jenkins/init.sls b/pillars/_secret/jenkins/init.sls new file mode 100644 index 0000000..8d610b9 --- /dev/null +++ b/pillars/_secret/jenkins/init.sls @@ -0,0 +1,69 @@ +#!yaml|gpg + +jenkins: + jjb_password: | + -----BEGIN PGP MESSAGE----- + + hQIMA4rOOq35JDJjAQ//b0lbfmzIRpCA2cq9OP55J6T5a4NUn9SbyNn8H/N0AiOB + yEYVyyHflpqBtlk+nUNFw2VgaxisJVf9drBpWGnDF9/tfNoBcKXYJXwusEYt1+3+ + ZoWgqhEEQomIjxQ8l6oVW65k4EXkBdZoEJrRf0nVUULuCY9c+p2AVh73NEi0WEIs + Cta5IG9TZo/uKgzMElZLC9NcYC1jeG/rmi/zUWlc7Su2l2/U3ptn6jXyOhdhR4lh + BS1GfFpOD1l/SX9XW2XhUkj3WdwUxoO/slmDfNJ1i9kIjRhZB0axLUrN/1dDo1Ap + bzZ+gz/5sm2UmwwRFzoVxncbVUzWApo76CRIv6Zs2cPtNNMI0+jS2/rwx7pqbStN + ObIkN2GVW9MUmrcSMGP1B84Ds705wYMtvXfAUbwQqNPsYaQEqhfdEM+vZBX4j4Vj + LwRIIqiVJpU8TPjfJH42IdrSw5Hx0BjiBSdQ3lkKO95mybGmLprnvd4cYf6f22DS + RwHetsbUNIojlnDrhv2rUCzrJceDbpc7JPapVaIby+HnoYtPiZphwqJzLNKwAHPB + 9Alji/1nMQPNDn7rJJu3PUXZTG2NBXxd + =GM3V + -----END PGP MESSAGE----- + cli_user_password: | + -----BEGIN PGP MESSAGE----- + + hQIMA4rOOq35JDJjAQ/8Djz9+14Q3203wVnBFBM0hmayv6ZbuDHHVoWdVyl4fIpZ + Y3U7eGWt2OeKljxmVw0LxsHtUWb46ISGeepUf2MoBnfal41AHuehNpoZFWXATfuZ + XXXEEi+FbA2lsd3Ys7VwK7dDq5UH6Dy+o/XYP2BqViq5IUhJ1weHimsIPe79gnep + jKyywXY6UF3PQ+Vbmdw+Q/T/xiayZ940rmgbFDXbfhqX/cNZyTUPn21uet+xGn5Z + HuSfMi86BLpYIRNAt03Gegpte/2ZzwsOtt5t5/XutjWgHB5hTO8kOkH4gtQEz2fH + HjkmI0Akw+gSQ/GssbFUqqeOjrISA/hMTqwfIPvR2gC/gkQqn8mMfwMEDkvtCLBJ + 3E5ZOmZAdJ/BVu8r+DRqEyaJwcI3Dumj4TRyaMuhsOUBxVjSrpl6DXkzAJdaBS8Q + bkmtb9uSJR3NjaM+sZsMLV8+d0m53nBqpBx6JgcUqO+7HZZtNBWKnDGGQ/m4jFvS + RwFAUdeZJMl6oFfhSmFLvtEMgGaygfoa+04T8/MjGdxwxBatJAS6kt7oCs4WfVGY + 7ulcD6s4XD0haYhqsEc4aRJWTYRxBVpM + =WtyQ + -----END PGP MESSAGE----- + + private_key: | + -----BEGIN PGP MESSAGE----- + + 7zT7IvlLa9xX/9lGCfB6WXoRYm+fS4lYMPfbd4aoj5I9+LZGKoIPK/n6PW+Zgh2Y + EPyJzZCRWTDnwzuw3oSE+T+DvJ+JqJ+qkgwUgSx+Sc1vtGiiw+LxXVJ9dJofI3Nl + Q7VMt2sj1cnU3wjQdnuqN5+yT8mnmF4PL+rb2EyTGG7AMx+Q5gspFJngMA0h4JhN + IeK0tqNpn5USztalh5b1Ha4JzyPmyFsVGXzTyRY2npHerebXQBt/R+j0ROVDCdal + wsJFjbBb3bFY7e1IZLxAh1rqSe7sswc37dDseh8ja7AbUglqu98NqfNdUuKfSb9C + r+Zr3qS6DnuFhgFMOmaDArdS7eaE0GkuvTh4G8mJFkFAfaks8W46wYOKZVWdtpat + m/ONAy62qXl1tsCHhwE5ZYxNHvZvf+8iWJ9vThLw1RtdTPBIllqjMvLIhBgi10nu + +HjB9zFOHezOvwjfs/O7cEa/HSJ1CNGo5QpMW3SPb1IpZdL1WiUmuK40B2ZFB7g1 + 7Hl2dkpkRuwkYE94FoszI/UXApYjpCndnAv62PP8Aa3Lxi4gwVCJUaSdHdC0kzoK + 3Hrw5euNj34ONTZvpBGNdGwrU5LkwFdu5bP5xvdWIuMOentH4ELmq+bsFSYapnlU + d0QPBECR57Xc8tXiOk9D/SvWFOwc0xeKmrcdNbL1y96ApX6IGNhzx9wbSWkKH7jX + Idb5JlpkB7rgcFw7R557XZPfNSqdG0ovlJTF/zhB0JqCyeZQeh3sNdCgr711Iwmt + zXMOqa8Hfe/zkhuT58t9xrC9PX2HFrsJPCFR0JMXHTwRK8q0fzzrUREeot/g7mqP + ebPgV3MwqUm8kW3RbMovHygxPB0RGWONqJvoqv1LkfAHJRO5v2xvgHuv4yPmF73H + Qu/15cmNpr0Hdqv114o7zMajQwFrjx0hQASjCQQmJA+dl/o7k+FirO6Cu0ZjswGx + j6SeTpxNuoJrsTc+lHwqqcbQUC1GAtqo4uJ+b0UNpUId7EiCZpeH++PMdqC5l8V9 + bVPnVzLtPqc68ajXqvv7hR//mzM+wVlraYJROQh19ZVT7ZG3hZj4sMAFhnbNbeh6 + wpeaT9PC7RqZ8jWDHyMCUOkwiTdZz+kmH1K8+2n3Y/4WLgTR7b+qqiWPWvKksBRT + zojzGs7tUk6AgTFNyHUJqR1zqDy/GHdCFi+XBVHDCUrcKmt4fWTygSHOwibEPeJt + NOtp26cJ8vXYLZ7SA3VcWOPfNc9VRUxpfgFzYMqdVwg+kUdDKl7oXLIiMFUvTUDm + /C94NjFgaEm9ojo3WjMQgovAjeCrPrMV6FOCdOuHkJuSrMQTxDw7L1U3qmb9jfwX + /2gx2yNF6UyiVIutJLu3opvTjFLFD7AOW5FgWPQVC1jrrouoEwv0oiT16bLeYtRZ + 4dtVbbeqroyAXFbqDQ1eEQSj2j/zT7ROrkd5Uyy2dhp+4A6oTNFhWzbXKaqFHE8N + fMnDRjhNXOJIF4jD2/faoCWGH6PDtK1xKi6nnzHKaetuwZxqKePhMnNatC1WRKIu + 63ydQOluukYlPBjDdivUMYu2yRpEHFINpk6eBdD7JRvGoOeCJa+EATZxiLQX0qXb + e+6YyZGrVYmfWXrIJRNZ3aBuxI/lRDPj/V/xSsqZgttik5FPjSL74SX7fojS8ohC + KnX2bczWODpPkpTTxB+vNsYbGzDXv0qA00uFPrR8K3mBBjHKSER4NCkS6GWUsFr+ + lxVIZakLrM42tVM/0bwI/g== + =W3Kv + -----END PGP MESSAGE----- + public_key: | + ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDv7Wnvougo06T55CL95NC7E53pdMtNQnxtghwZZivFRVICq6UzE3dQaJlVPhq0ITT0B9hLEHpi3GWG5YWXm+9QkfZkcB5WvDafrKTBrk6YfwIeMyhOCnH89R+T6GXfSDXef6ua0dIR5mVEP9hnZKESJzGDe0d+C4/jAIUSgoZ45l90HJP0LK1+WVWHhnJuHPRfWdUQw445dtKsJB4489c8hIAxnEbQRrmaiiu4nlRmdW96ErmgEImJHOA/r6eS8EbjV6pz/kCukniVRkStwaubPHMWlfyXWIPT8L jenkins diff --git a/pillars/top.sls b/pillars/top.sls new file mode 100644 index 0000000..45ed67e --- /dev/null +++ b/pillars/top.sls @@ -0,0 +1,6 @@ +base: + + 'G@roles:jenkins': + - _secret.jenkins + + diff --git a/states/jenkins/master/init.sls b/states/jenkins/master/init.sls new file mode 100644 index 0000000..2bdb6cd --- /dev/null +++ b/states/jenkins/master/init.sls @@ -0,0 +1,5 @@ +include: + - ./install + - ./setup + - ./plugins + - ./jimmy diff --git a/states/jenkins/master/install.sls b/states/jenkins/master/install.sls new file mode 100644 index 0000000..449a996 --- /dev/null +++ b/states/jenkins/master/install.sls @@ -0,0 +1,31 @@ +# Install jenkins package from official Jenkins LTS repositories + +{% set jenkins_version = "2.32.1" %} + + +apt-transport-https: + pkg.installed + +jenkins_repo: + pkgrepo.managed: + - name: deb https://pkg.jenkins.io/debian-stable binary/ + - file: /etc/apt/sources.list.d/jenkins.list + - key_url: https://pkg.jenkins.io/debian-stable/jenkins.io.key + - require: + - pkg: apt-transport-https + +prevent_startup_on_install: + file.managed: + - name: /usr/sbin/policy-rc.d + - contents: | + #!/bin/bash + + exit 101 + - mode: 0700 + +jenkins: + pkg.installed: + - version: {{ jenkins_version }} + - require: + - pkgrepo: jenkins_repo + - file: prevent_startup_on_install diff --git a/states/jenkins/master/jimmy.sls b/states/jenkins/master/jimmy.sls new file mode 100644 index 0000000..9ca31f5 --- /dev/null +++ b/states/jenkins/master/jimmy.sls @@ -0,0 +1,49 @@ +# Setup and run Jimmy (https://github.com/ci-team/jimmy.git) - tool to perform Jenkins master configuration + +{% set jenkins = salt['pillar.get']('jenkins') %} + +{% set venv_path = "/opt/jimmy" %} +{% set jimmy_sources = "git+https://github.com/ci-team/jimmy.git" %} +{% set config_prefix = "/var/lib/jenkins/jimmy" %} +{% set jimmy_config = "{0}/jimmy.yaml".format(config_prefix) %} +{% set jenkins_config = "{0}/jenkins.yaml".format(config_prefix) %} + +python-tox: + pkg.installed + +virtualenv: + pkg.installed + +jimmy_venv: + virtualenv.managed: + - name: {{ venv_path }} + - pip_pkgs: + - {{ jimmy_sources }} + +jimmy_config: + file.managed: + - name: {{ jimmy_config }} + - source: salt://jenkins/master/templates/jimmy.yaml.j2 + - template: jinja + - context: + jenkins_config: {{ jenkins_config }} + - makedirs: True + +jenkins_config: + file.managed: + - name: {{ jenkins_config }} + - source: salt://jenkins/master/templates/jenkins.yaml.j2 + - template: jinja + - context: + cli_user_pub_key: {{ jenkins.public_key }} + cli_user_password: {{ jenkins.cli_user_password }} + jjb_password: {{ jenkins.jjb_password }} + - makedirs: True + - replace: True + +run_jimmy: + cmd.run: + - cwd: {{ venv_path }} + - runas: jenkins + - shell: /bin/bash # needed for the source command + - name: "source {{ venv_path }}/bin/activate && jimmy --conf-path {{ jimmy_config }} -e main" diff --git a/states/jenkins/master/pillars.example b/states/jenkins/master/pillars.example new file mode 100644 index 0000000..ee65985 --- /dev/null +++ b/states/jenkins/master/pillars.example @@ -0,0 +1,36 @@ +# Secrets pillar + +jenkins: + jjb_password: admin + cli_user_password: admin + private_key: | + -----BEGIN RSA PRIVATE KEY----- + MIIEpAIBAAKCAQEAv+667OhUMm+y3Ry+CyoNpuP4WMiC/zPRNgtF9CmZx1OuOWFY + hkXq3aAcKRHfLRStUHQEmfOQYAcFdCmzCAJ3DBQAqE2mOEaHIlFXY1M8O6wsqzPY + sO/ltosJwWSWCTuY7SBU6XTsTSpgwl2w0kssYEGN5h1fdW25E3mXuXp8HmBF7fjK + OLnDxwsye/9abwqVxD/rwAyxvRRwR4mLnzdD2tuJI9Klme4cZ/NWBd4ZJNGUavuB + jRK2fUytxVxDgghfsML2eXU72CoRxW+3hxpRHlk+lsLY0yF+KZSszWVZstwISf7s + oqE3WTCemi2d9Me1b0i0MTbxcpKz0tAV7aLnHwIDAQABAoIBAHsuL1/OuALqt0O7 + p6ioo7xhgUkR3ykEy5fA4nOSo3RPG3kOJe9/Pf5hPNGK+84GADmrTCy2KgnHV9O2 + 04Und9oTmFxsAWsKsL3wsII5sGWn2b9C9iaQwMBIOcmZ0cU5L6u4XWa7uNDJivIT + iWFeB6v7RqPSZCqMcgSyvBK5LqGbEEtIbz54bmY9zVQV9nspvcUXFzeMpKK3mpX1 + R8RbL98Xy3OfMFQLx3+1TaclppzBuC6OJM8hYMHG5hijj+FUrZUeIu6Qyv/TAp2U + Ki1+AGTZ/YaKRL1L/b7So1Y5Pcclavz8ZNod8sq+PgqNXqr2qZCkHZYz5g8Cxg/O + jfWOTXkCgYEA9CVQFgma0ZnImCEJo3dUtwwvay7SO9VDGbmfi8Mdn+bXqVMFLyh5 + tJVuKqpZwAnKvcPvwOe0Cm4WDY/JufrpRJ3Ubg5rkCXrvuJEyYnFRL8m2ziDvtAi + PAhfu7BcRyTvYk3GqJsZWyh+H70HSyEV+iSJAnJ9rmL3nYERA7hakQsCgYEAyUBq + p+iLhBaIRs1Kgcit8idl0Dpv6pFC+CUSEWDuFCXs9jYHFTlFQDvC5TouEAq+Wx8O + Nx21fTrP9hKptHlecor2nYV/PPsdp8hwBJDtIT42WR2MoOICrbnn91jDipzBeguJ + dAR+q5Lud1YT7dn/bWcKUwhanetU9yELm+f4tr0CgYEA3Mm9kiBffPuIt2LxN34c + A2mmnFR8VRW4l9QRH7B04v08vOjSQGJnJdMZDMZ/Yba5PUGrWRVw8Rm7IcqN0H3D + ZIUi7eBmj7ZranVvZZ8M4e3H8uo54l6RBx8/jBmiachSH+FcLXANzy1gWc11ghDK + sitGKAtn80OW0D4/J9EZPh8CgYEAkK8q/oQEqXW5i41Tn3Ky+4CvhcA9bZn5V4Tv + NGdHQlzxuyCkfvGpg8BzIXFuZEIVEFWHVsCZFesOT46jSwhmilq1ahHro9+1Nka3 + odC/akLbt72yuIdD/bnFtuGt0x1XS3K34xzIJHmBz3Gm6jzrka+ekItU+muRrgmn + 2vIusNECgYBxaGFHbyP6mctGeiFkjzEAHWu6gx1K7A8tvIplL39tlYR0m+zRsFjG + i9XlZPgWx4Q49IB95MCEeW6JBepMYLNfsRYLEy62iPdzpvV0ruSYxF4wMNcI+6YC + SnkZ1sr6uE7GVD/dF0v5ric2CHe1HIMvnWHT1N0YRaD4qaNcbnmgyg== + -----END RSA PRIVATE KEY----- + public_key: | + ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC/7rrs6FQyb7LdHL4LKg2m4/hYyIL/M9E2C0X0KZnHU645YViGRerdoBwpEd8tFK1QdASZ85BgBwV0KbMIAncMFACoTaY4RociUVdjUzw7rCyrM9iw7+W2iwnBZJYJO5jtIFTpdOxNKmDCXbDSSyxgQY3mHV91bbkTeZe5enweYEXt+Mo4ucPHCzJ7/1pvCpXEP+vADLG9FHBHiYufN0Pa24kj0qWZ7hxn81YF3hkk0ZRq+4GNErZ9TK3FXEOCCF+wwvZ5dTvYKhHFb7eHGlEeWT6WwtjTIX4plKzNZVmy3AhJ/uyioTdZMJ6aLZ30x7VvSLQxNvFykrPS0BXtoucf jenkins + diff --git a/states/jenkins/master/plugins.jinja b/states/jenkins/master/plugins.jinja new file mode 100644 index 0000000..410568a --- /dev/null +++ b/states/jenkins/master/plugins.jinja @@ -0,0 +1,117 @@ +# Contains list of plugins to be installed. +# +# Each plugin is defined by dictionary +# { +# 'name': 'short-name', +# 'version': '0.0.1', +# 'url': 'http://example.com/full/path/to/plugin.hpi', # optional +# } +# +# To obtain list of plugins from working Jenkins installation one can use the following script: +# +################################################################################################### +# #!/usr/bin/env python +# +# import argparse +# import ConfigParser +# import jenkins +# import pprint +# import toposort +# +# parser = argparse.ArgumentParser() +# parser.add_argument("-c", "--conf", required=True, +# help="Path to jenkins-job-builder config file") +# args = parser.parse_args() +# +# config = ConfigParser.RawConfigParser() +# config.read(args.conf) +# +# jenkinsurl = config.get('jenkins', 'url') +# username = config.get('jenkins', 'user') +# password = config.get('jenkins', 'password') +# +# j = jenkins.Jenkins(jenkinsurl, username, password) +# +# data = j.get_plugins() +# +# graph = {} +# for (key, long_key) in data.keys(): +# graph[key] = set([ item['shortName'] for item in data[key]['dependencies']]) +# +# sorted_list = toposort.toposort_flatten(graph) +# +# plugins = [] +# +# for plugin in sorted_list: +# plugins.append( +# { +# 'name': plugin, +# 'version': str(data[plugin]['version']), +# } +# ) +# +# pprint.pprint(plugins) +# +################################################################################################### + +{% set plugins = [ + {'name': 'bouncycastle-api', 'version': '2.16.0'}, + {'name': 'StashBranchParameter', 'version': '0.2.0'}, + {'name': 'ace-editor', 'version': '1.1'}, + {'name': 'credentials', 'version': '2.1.10'}, + {'name': 'external-monitor-job', 'version': '1.6'}, + {'name': 'gradle', 'version': '1.25'}, + {'name': 'icon-shim', 'version': '2.0.3'}, + {'name': 'javadoc', 'version': '1.4'}, + {'name': 'jquery-detached', 'version': '1.2.1'}, + {'name': 'scm-api', 'version': '1.3'}, + {'name': 'script-security', 'version': '1.25'}, + {'name': 'structs', 'version': '1.5'}, + {'name': 'token-macro', 'version': '2.0'}, + {'name': 'windows-slaves', 'version': '1.2'}, + {'name': 'ant', 'version': '1.4'}, + {'name': 'cloudbees-folder', 'version': '5.15'}, + {'name': 'junit', 'version': '1.19'}, + {'name': 'plain-credentials', 'version': '1.3'}, + {'name': 'ssh-credentials', 'version': '1.12'}, + {'name': 'workflow-step-api', 'version': '2.6'}, + {'name': 'antisamy-markup-formatter', 'version': '1.5'}, + {'name': 'branch-api', 'version': '1.11.1'}, + {'name': 'config-file-provider', 'version': '2.15.1'}, + {'name': 'credentials-binding', 'version': '1.10'}, + {'name': 'display-url-api', 'version': '0.5'}, + {'name': 'git-client', 'version': '2.2.0'}, + {'name': 'matrix-auth', 'version': '1.4'}, + {'name': 'matrix-project', 'version': '1.7.1'}, + {'name': 'pam-auth', 'version': '1.3'}, + {'name': 'timestamper', 'version': '1.8.7'}, + {'name': 'workflow-api', 'version': '2.8'}, + {'name': 'workflow-scm-step', 'version': '2.3'}, + {'name': 'description-setter', 'version': '1.10'}, + {'name': 'mailer', 'version': '1.18'}, + {'name': 'mercurial', 'version': '1.57'}, + {'name': 'multi-slave-config-plugin', 'version': '1.2.0'}, + {'name': 'slack', 'version': '2.1'}, + {'name': 'workflow-support', 'version': '2.11'}, + {'name': 'git', 'version': '3.0.1'}, + {'name': 'ivy', 'version': '1.26'}, + {'name': 'ldap', 'version': '1.13'}, + {'name': 'maven-plugin', 'version': '2.14'}, + {'name': 'rebuild', 'version': '1.25'}, + {'name': 'workflow-cps', 'version': '2.23'}, + {'name': 'analysis-core', 'version': '1.82'}, + {'name': 'anything-goes-formatter', 'version': '1.0'}, + {'name': 'artifactory', 'version': '2.8.2'}, + {'name': 'build-timeout', 'version': '1.18'}, + {'name': 'cloudbees-bitbucket-branch-source', 'version': '1.9'}, + {'name': 'greenballs', 'version': '1.15'}, + {'name': 'jobConfigHistory', 'version': '2.15'}, + {'name': 'jquery', 'version': '1.11.2-0'}, + {'name': 'simple-theme-plugin', 'version': '0.3'}, + {'name': 'stash-pullrequest-builder', 'version': '1.7.0'}, + {'name': 'stashNotifier', 'version': '1.11.4'}, + {'name': 'checkstyle', 'version': '3.47'}, + {'name': 'findbugs', 'version': '4.69'}, +] +%} + diff --git a/states/jenkins/master/plugins.sls b/states/jenkins/master/plugins.sls new file mode 100644 index 0000000..7249669 --- /dev/null +++ b/states/jenkins/master/plugins.sls @@ -0,0 +1,25 @@ +# Install set of plugins on local Jenkins instance via Jenkins cli +{% from 'jenkins/master/plugins.jinja' import plugins with context %} + +{% set jenkins_cli = "/var/cache/jenkins/war/WEB-INF/jenkins-cli.jar" %} +{% set default_url = "http://updates.jenkins-ci.org/download/plugins/{plugin_name}/{plugin_version}/{plugin_name}.hpi" %} + +{% for plugin in plugins %} + +{% set plugin_url = plugin.get('url', default_url.format(plugin_name=plugin.name, plugin_version=plugin.version)) %} + +jenkins_plugin {{ plugin.name }}: + cmd.run: + - name: "java -jar {{ jenkins_cli }} -s http://localhost:8080/ install-plugin {{ plugin_url }}" + - unless: | + version_string=`java -jar /var/cache/jenkins/war/WEB-INF/jenkins-cli.jar -s http://localhost:8080/ list-plugins {{ plugin.name }}` && version=`echo ${version_string%(*)} | awk '{print $NF}'` && [ "$version" == "{{ plugin.version }}" ] + - runas: jenkins + - shell: /bin/bash + - watch_in: + - service: jenkins_restart +{% endfor %} + +jenkins_restart: + service.running: + - name: jenkins + - init_delay: 60 # Jenkins start is slow diff --git a/states/jenkins/master/setup.sls b/states/jenkins/master/setup.sls new file mode 100644 index 0000000..5310d02 --- /dev/null +++ b/states/jenkins/master/setup.sls @@ -0,0 +1,48 @@ +# Initial configuration for Jenkins master service +# Provides unsecured Jenkins instance + +{% set jenkins = salt['pillar.get']('jenkins') %} + +# add parameter to skip SetupWizard +{% set java_args = "-Djava.awt.headless=true -Djenkins.install.runSetupWizard=false" %} +jenkins_java_args: + file.replace: + - name: /etc/default/jenkins + - pattern: "^JAVA_ARGS=.*$" + - repl: 'JAVA_ARGS="{{ java_args }}"' + +jenkins_master_ssh_directory: + file.directory: + - name: /var/lib/jenkins/.ssh + - user: jenkins + - mode: 0700 + +# Configure jenkins user SSH key +# yaml_encode helps passing multiline contents variables +jenkins_master_ssh_private_key: + file.managed: + - name: /var/lib/jenkins/.ssh/id_rsa + - contents: {{ jenkins.private_key | yaml_encode }} + - makedirs: True + - replace: True + - user: jenkins + - mode: 600 + - require: + - file: jenkins_master_ssh_directory + +jenkins_master_ssh_public_key: + file.managed: + - name: /var/lib/jenkins/.ssh/id_rsa.pub + - contents: {{ jenkins.public_key | yaml_encode }} + - makedirs: True + - replace: True + - user: jenkins + - mode: 644 + - require: + - file: jenkins_master_ssh_directory + +jenkins_service: + service.running: + - name: jenkins + - enable: True + - init_delay: 60 # Jenkins start is slow diff --git a/states/jenkins/master/templates/jenkins.yaml.j2 b/states/jenkins/master/templates/jenkins.yaml.j2 new file mode 100644 index 0000000..d7f6fbc --- /dev/null +++ b/states/jenkins/master/templates/jenkins.yaml.j2 @@ -0,0 +1,24 @@ +jenkins: + + security: + password: + access: + - name: jjb-updater + email: jjb@example.com + password: {{ jjb_password }} + full_name: JJB Updater + permissions: + - job + + cli_user: + name: cli_admin + public_key: {{ cli_user_pub_key }} + password: {{ cli_user_password }} + + credentials: + password: + - scope: global + id: jjb-updater + username: jjb-updater + password: {{ jjb_password }} + description: Credentials to JJB updater user required for automated JJB update job. diff --git a/states/jenkins/master/templates/jimmy.yaml.j2 b/states/jenkins/master/templates/jimmy.yaml.j2 new file mode 100644 index 0000000..aef48a7 --- /dev/null +++ b/states/jenkins/master/templates/jimmy.yaml.j2 @@ -0,0 +1,52 @@ +defaults: + inject: + jenkins_cli_path: /var/cache/jenkins/war/WEB-INF/jenkins-cli.jar + +setup: + - name: setup +teardown: + - name: teardown + +pipelines: + main: + steps: + + - name: read_source + description: | + Read source file with Jenkins configuration + inject: + path: env.jenkins_config_path + + - name: build_source + description: | + Build and post-process the initial configuration + inject: + source: results.read_source.source + + - name: check_applicable + description: | + Check if modules are applicable for source file + inject: + source: results.read_source.source + + - name: validate_source + description: | + Perform validation of post-processed document against + jsonschemas or amother kind of checks + inject: + source: results.build_source.source + + - name: update_dest + description: | + Update jenkins configuration by calling jenkins cli + and executing groovy scripts + inject: + source: results.build_source.source + jenkins_url: env.jenkins_url + jenkins_cli_path: config.defaults.inject.jenkins_cli_path + + +envs: + main: + jenkins_url: http://localhost:8080 + jenkins_config_path: {{ jenkins_config }} diff --git a/states/top.sls b/states/top.sls new file mode 100644 index 0000000..9e253e0 --- /dev/null +++ b/states/top.sls @@ -0,0 +1,5 @@ +base: + + 'G@roles:jenkins': + - jenkins.master + diff --git a/utils/encrypt.py b/utils/encrypt.py new file mode 100755 index 0000000..b2cfd7a --- /dev/null +++ b/utils/encrypt.py @@ -0,0 +1,49 @@ +#!/usr/bin/env python + +import subprocess +import argparse +import os + +parser = argparse.ArgumentParser() +parser.add_argument("-d", "--dir", + default="/etc/salt/gpgkeys", + help="path to gpgkeys directory") +parser.add_argument("--oneline", + action='store_true', + help="switch to oneline output") +parser.add_argument("-f", "--filename", + help="input file. If not specified, data will be read from cli.") + +args = parser.parse_args() + +if args.filename: + with open(args.filename) as f: + data = f.read() +else: + data = raw_input() + + +if not os.path.isdir(args.dir): + print "homedir %s does not exist" % args.dir + exit(1) + +secret, stderr = subprocess.Popen( + ['gpg', + '--armor', + '--batch', + '--homedir', args.dir, + '--trust-model', 'always', + '--encrypt', + '--default-recipient-self', + ], + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE).communicate(input=data) + +if not secret: + raise ValueError('No ciphertext found: {0}'.format(stderr)) + +if args.oneline: + secret = secret.replace('\n', r'\n') + +print secret