├── .gitignore ├── Dockerfile ├── Gemfile ├── Gemfile.lock ├── Puppetfile ├── Puppetfile.lock ├── README.md ├── Rakefile ├── Vagrantfile ├── cucumber.yml ├── docker └── tomcat │ ├── Dockerfile │ ├── dump.sql │ ├── jdbc.properties │ └── run.sh ├── features ├── smoke_tests.feature ├── step_definitions │ ├── common_steps.rb │ └── tcp_ip_steps.rb └── support │ ├── env.rb │ └── hooks.rb ├── manifests ├── nodes │ ├── db.pp │ ├── qa.pp │ ├── tomcat.pp │ └── www.pp └── site.pp ├── modules └── .PLACEHOLDER ├── mymodules └── acme │ ├── files │ ├── dump.sql │ └── properties.aug │ └── manifests │ ├── db_node.pp │ ├── init.pp │ ├── tomcat_node.pp │ └── www_node.pp └── spec ├── acceptance ├── nodesets │ ├── centos-65-x64-docker.yml │ ├── centos-65-x64.yml │ └── default.yml └── system_spec.rb ├── hosts ├── db_spec.rb ├── tomcat_spec.rb └── www_spec.rb ├── spec_helper.rb └── spec_helper_acceptance.rb /.gitignore: -------------------------------------------------------------------------------- 1 | /.tmp/ 2 | /modules/* 3 | !/modules/.PLACEHOLDER 4 | /.vagrant 5 | /.librarian/ 6 | /spec/fixtures/ 7 | /target/ 8 | /.yum 9 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM devopsil/puppet:3.5.1 2 | 3 | COPY modules/ /etc/puppet/modules/ 4 | COPY manifests/ /etc/puppet/manifests/ 5 | 6 | RUN puppet apply /etc/puppet/manifests/ --verbose --detailed-exitcodes --certname tomcat1.acme.com || [ $? -eq 2 ] 7 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | gem 'librarian-puppet', '>=1.0.0' 4 | 5 | group :rake do 6 | gem 'puppet', '>=3.4.0', :require => false 7 | gem 'rake', '>=1.0.0', :require => false 8 | gem 'puppet-lint', '>=0.1.13', :require => false 9 | gem 'puppetlabs_spec_helper', '>=0.8.2', :require => false 10 | gem 'beaker', '>=1.20.1', :require => false 11 | gem 'beaker-rspec', '>=2.1.0', :require => false 12 | gem 'minitest', '<5.0.0', :require => false # avoid conflict with Beaker 13 | 14 | gem 'cucumber', :require => false 15 | gem 'capybara', :require => false 16 | gem 'poltergeist', :require => false 17 | end 18 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | specs: 4 | CFPropertyList (2.2.8) 5 | activemodel (4.0.10) 6 | activesupport (= 4.0.10) 7 | builder (~> 3.1.0) 8 | activesupport (4.0.10) 9 | i18n (~> 0.6, >= 0.6.9) 10 | minitest (~> 4.2) 11 | multi_json (~> 1.3) 12 | thread_safe (~> 0.1) 13 | tzinfo (~> 0.3.37) 14 | addressable (2.3.6) 15 | archive-tar-minitar (0.5.2) 16 | autoparse (0.3.3) 17 | addressable (>= 2.3.1) 18 | extlib (>= 0.9.15) 19 | multi_json (>= 1.0.0) 20 | aws-sdk (1.42.0) 21 | json (~> 1.4) 22 | nokogiri (>= 1.4.4) 23 | beaker (1.20.1) 24 | aws-sdk (= 1.42.0) 25 | blimpy (~> 0.6) 26 | docker-api 27 | fission (~> 0.4) 28 | fog (~> 1.22.1) 29 | google-api-client (~> 0.7.1) 30 | hocon (~> 0.0.4) 31 | inifile (~> 2.0) 32 | json (~> 1.8) 33 | mime-types (~> 1.25) 34 | net-scp (~> 1.1) 35 | net-ssh (~> 2.6) 36 | nokogiri (~> 1.5.10) 37 | rbvmomi (= 1.8.1) 38 | unf (~> 0.1) 39 | beaker-rspec (3.0.0) 40 | beaker (~> 1.10) 41 | rspec 42 | serverspec (~> 1.0) 43 | specinfra (~> 1.0) 44 | blimpy (0.6.7) 45 | fog 46 | minitar 47 | thor 48 | builder (3.1.4) 49 | capybara (2.4.4) 50 | mime-types (>= 1.16) 51 | nokogiri (>= 1.3.3) 52 | rack (>= 1.0.0) 53 | rack-test (>= 0.5.4) 54 | xpath (~> 2.0) 55 | cliver (0.3.2) 56 | cucumber (1.3.17) 57 | builder (>= 2.1.2) 58 | diff-lcs (>= 1.1.3) 59 | gherkin (~> 2.12) 60 | multi_json (>= 1.7.5, < 2.0) 61 | multi_test (>= 0.1.1) 62 | diff-lcs (1.2.5) 63 | docker-api (1.14.0) 64 | archive-tar-minitar 65 | excon (>= 0.38.0) 66 | json 67 | excon (0.41.0) 68 | extlib (0.9.16) 69 | facter (2.2.0) 70 | CFPropertyList (~> 2.2.6) 71 | faraday (0.9.0) 72 | multipart-post (>= 1.2, < 3) 73 | fission (0.5.0) 74 | CFPropertyList (~> 2.2) 75 | fog (1.22.1) 76 | fog-brightbox 77 | fog-core (~> 1.22) 78 | fog-json 79 | ipaddress (~> 0.5) 80 | nokogiri (~> 1.5, >= 1.5.11) 81 | fog-brightbox (0.6.1) 82 | fog-core (~> 1.22) 83 | fog-json 84 | inflecto 85 | fog-core (1.24.0) 86 | builder 87 | excon (~> 0.38) 88 | formatador (~> 0.2) 89 | mime-types 90 | net-scp (~> 1.1) 91 | net-ssh (>= 2.1.3) 92 | fog-json (1.0.0) 93 | multi_json (~> 1.0) 94 | formatador (0.2.5) 95 | gherkin (2.12.2) 96 | multi_json (~> 1.3) 97 | google-api-client (0.7.1) 98 | addressable (>= 2.3.2) 99 | autoparse (>= 0.3.3) 100 | extlib (>= 0.9.15) 101 | faraday (>= 0.9.0) 102 | jwt (>= 0.1.5) 103 | launchy (>= 2.1.1) 104 | multi_json (>= 1.0.0) 105 | retriable (>= 1.4) 106 | signet (>= 0.5.0) 107 | uuidtools (>= 2.1.0) 108 | her (0.7.2) 109 | activemodel (>= 3.0.0, < 4.2) 110 | activesupport (>= 3.0.0, < 4.2) 111 | faraday (>= 0.8, < 1.0) 112 | multi_json (~> 1.7) 113 | hiera (1.3.4) 114 | json_pure 115 | highline (1.6.21) 116 | hocon (0.0.6) 117 | i18n (0.6.11) 118 | inflecto (0.0.2) 119 | inifile (2.0.2) 120 | ipaddress (0.8.0) 121 | json (1.8.1) 122 | json_pure (1.8.1) 123 | jwt (1.0.0) 124 | launchy (2.4.3) 125 | addressable (~> 2.3) 126 | librarian (0.1.2) 127 | highline 128 | thor (~> 0.15) 129 | librarian-puppet (2.0.0) 130 | librarian (>= 0.1.2) 131 | puppet_forge 132 | rsync 133 | metaclass (0.0.4) 134 | mime-types (1.25.1) 135 | minitar (0.5.4) 136 | minitest (4.7.5) 137 | mocha (1.1.0) 138 | metaclass (~> 0.0.1) 139 | multi_json (1.10.1) 140 | multi_test (0.1.1) 141 | multipart-post (2.0.0) 142 | net-scp (1.2.1) 143 | net-ssh (>= 2.6.5) 144 | net-ssh (2.9.1) 145 | nokogiri (1.5.11) 146 | poltergeist (1.5.1) 147 | capybara (~> 2.1) 148 | cliver (~> 0.3.1) 149 | multi_json (~> 1.0) 150 | websocket-driver (>= 0.2.0) 151 | puppet (3.7.2) 152 | facter (> 1.6, < 3) 153 | hiera (~> 1.0) 154 | json_pure 155 | puppet-lint (1.1.0) 156 | puppet-syntax (1.3.0) 157 | rake 158 | puppet_forge (1.0.3) 159 | her (~> 0.6) 160 | puppetlabs_spec_helper (0.8.2) 161 | mocha 162 | puppet-lint 163 | puppet-syntax 164 | rake 165 | rspec 166 | rspec-puppet 167 | rack (1.5.2) 168 | rack-test (0.6.2) 169 | rack (>= 1.0) 170 | rake (10.3.2) 171 | rbvmomi (1.8.1) 172 | builder 173 | nokogiri (>= 1.4.1) 174 | trollop 175 | retriable (1.4.1) 176 | rspec (2.99.0) 177 | rspec-core (~> 2.99.0) 178 | rspec-expectations (~> 2.99.0) 179 | rspec-mocks (~> 2.99.0) 180 | rspec-core (2.99.2) 181 | rspec-expectations (2.99.2) 182 | diff-lcs (>= 1.1.3, < 2.0) 183 | rspec-its (1.0.1) 184 | rspec-core (>= 2.99.0.beta1) 185 | rspec-expectations (>= 2.99.0.beta1) 186 | rspec-mocks (2.99.2) 187 | rspec-puppet (1.0.1) 188 | rspec 189 | rsync (1.0.9) 190 | serverspec (1.16.0) 191 | highline 192 | net-ssh 193 | rspec (~> 2.99) 194 | rspec-its 195 | specinfra (~> 1.27) 196 | signet (0.5.1) 197 | addressable (>= 2.2.3) 198 | faraday (>= 0.9.0.rc5) 199 | jwt (>= 0.1.5) 200 | multi_json (>= 1.0.0) 201 | specinfra (1.27.5) 202 | thor (0.19.1) 203 | thread_safe (0.3.4) 204 | trollop (2.0) 205 | tzinfo (0.3.42) 206 | unf (0.1.4) 207 | unf_ext 208 | unf_ext (0.0.6) 209 | uuidtools (2.1.5) 210 | websocket-driver (0.3.5) 211 | xpath (2.0.0) 212 | nokogiri (~> 1.3) 213 | 214 | PLATFORMS 215 | ruby 216 | 217 | DEPENDENCIES 218 | beaker (>= 1.20.1) 219 | beaker-rspec (>= 2.1.0) 220 | capybara 221 | cucumber 222 | librarian-puppet (>= 1.0.0) 223 | minitest (< 5.0.0) 224 | poltergeist 225 | puppet (>= 3.4.0) 226 | puppet-lint (>= 0.1.13) 227 | puppetlabs_spec_helper (>= 0.8.2) 228 | rake (>= 1.0.0) 229 | -------------------------------------------------------------------------------- /Puppetfile: -------------------------------------------------------------------------------- 1 | forge 'http://forge.puppetlabs.com' 2 | 3 | mod 'puppetlabs/java', '>=1.0.0' 4 | mod 'puppetlabs/apache', '>=0.9.0' 5 | mod 'puppetlabs/postgresql', '>=3.0.0' 6 | mod 'puppetlabs/firewall' 7 | mod 'camptocamp/tomcat', '>=0.6.0' 8 | mod 'maestrodev/maven', '>=1.0.0' 9 | mod 'stahnma/epel', '>=0.0.2' 10 | mod 'maestrodev/avahi', '>=1.0.0' 11 | mod 'csanchez/acme', :path => 'mymodules/acme' 12 | -------------------------------------------------------------------------------- /Puppetfile.lock: -------------------------------------------------------------------------------- 1 | FORGE 2 | remote: https://forgeapi.puppetlabs.com 3 | specs: 4 | camptocamp-archive (0.0.1) 5 | camptocamp-tomcat (0.7.1) 6 | camptocamp-archive (>= 0.0.0) 7 | theforeman-concat_native (>= 0.0.0) 8 | maestrodev-avahi (1.0.0) 9 | maestrodev-maven (1.1.7) 10 | maestrodev-wget (>= 1.0.0) 11 | maestrodev-wget (1.2.2) 12 | puppetlabs-apache (0.9.0) 13 | puppetlabs-concat (>= 1.0.0) 14 | puppetlabs-stdlib (>= 2.4.0) 15 | puppetlabs-apt (1.4.0) 16 | puppetlabs-stdlib (>= 2.2.1) 17 | puppetlabs-concat (1.0.0) 18 | puppetlabs-firewall (0.4.2) 19 | puppetlabs-java (1.0.1) 20 | puppetlabs-stdlib (>= 0.1.6) 21 | puppetlabs-postgresql (3.0.0) 22 | puppetlabs-apt (< 2.0.0, >= 1.1.0) 23 | puppetlabs-concat (< 2.0.0, >= 1.0.0) 24 | puppetlabs-firewall (>= 0.0.4) 25 | puppetlabs-stdlib (< 5.0.0, >= 3.2.0) 26 | puppetlabs-stdlib (4.1.0) 27 | stahnma-epel (0.0.5) 28 | theforeman-concat_native (1.3.1) 29 | 30 | PATH 31 | remote: mymodules/acme 32 | specs: 33 | csanchez-acme (0.0.1) 34 | 35 | DEPENDENCIES 36 | camptocamp-tomcat (>= 0.6.0) 37 | csanchez-acme (>= 0) 38 | maestrodev-avahi (>= 1.0.0) 39 | maestrodev-maven (>= 1.0.0) 40 | puppetlabs-apache (>= 0.9.0) 41 | puppetlabs-firewall (>= 0) 42 | puppetlabs-java (>= 1.0.0) 43 | puppetlabs-postgresql (>= 3.0.0) 44 | stahnma-epel (>= 0.0.2) 45 | 46 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Continuous Delivery with Maven, Puppet and Tomcat 2 | 3 | Examples of setting up a continuous delivery process using Maven, Tomcat, Docker, Puppet, Vagrant, Jenkins, 4 | Cucumber, Beaker and other helpful tools for my talks. 5 | Evolved through the different talks, unsing latest tools. 6 | 7 | * Agile Testing Days 2014: [Continuous Delivery, the Next Frontier](http://www.agiletestingdays.com/session/continuous-delivery-the-next-frontier/) (git tag `agiletestingdays2014`). 8 | * Agile Testing Days 2013: [Infrastructure testing with Jenkins, Puppet and Vagrant](http://blog.csanchez.org/2013/10/29/infrastructure-testing-with-jenkins-puppet-and-vagrant-at-agile-testing-days/) (git tag `agiletestingdays2013`). 9 | * ApacheCon 2013: [Continuous Delivery with Maven, Puppet and Tomcat](http://blog.csanchez.org/2013/11/12/continuous-delivery-with-maven-puppet-and-tomcat-video-from-apachecon-na-2013/) (git tag `apachecon2013`) 10 | * JavaZone 2012: [Puppet for Java Developers](https://github.com/carlossg/puppet-for-java-devs) (forked repo) 11 | 12 | > Continuous Integration, with Apache Continuum or Jenkins, can be extended to fully manage deployments and production environments, running in Tomcat for instance, in a full Continuous Delivery cycle using infrastructure-as-code tools like Puppet, allowing to manage multiple servers and their configurations. 13 | 14 | 15 | ## Tools 16 | 17 | * [Docker](http://docker.io) for containerized deployments and tests 18 | * [Vagrant](http://vagrantup.com) for VM creation and provisioning 19 | * [librarian-puppet](http://librarian-puppet.com/) for automatic module installation 20 | * [puppet-rspec](http://rspec-puppet.com) for puppet manifest testing 21 | * [Capybara](https://github.com/jnicklas/capybara) and [Cucumber](http://cukes.info/) for acceptance tests 22 | * [Beaker](https://github.com/puppetlabs/beaker) for deployment tests 23 | 24 | 25 | 26 | ## Installation 27 | 28 | [Install Docker](https://docs.docker.com/installation). 29 | 30 | Install all required gems 31 | 32 | bundle install 33 | 34 | Change the $repo var in `mymodules/acme/manifests/tomcat_node.pp` to the location of a repository where you can deploy the Maven builds, accessible from the vagrant vms 35 | 36 | $repo = 'http://carlos-mbook-pro.local:8000/repository/all/' 37 | 38 | Install all Puppet modules with Puppet Librarian 39 | 40 | librarian-puppet install 41 | 42 | Run the specs with puppet-rspec 43 | 44 | bundle exec rake 45 | 46 | Build the Docker Tomcat image, change the REPO environment in `docker/tomcat/Dockerfile` with the location of your repo. 47 | 48 | docker build -t csanchez/appfuse-tomcat docker/tomcat 49 | 50 | Run the system specs with beaker 51 | 52 | bundle exec rake beaker 53 | 54 | Start the stack containers. We assume they are up all the time 55 | Create a host entry `docker.local` pointing to your Docker host. 56 | 57 | docker run -d --name db -p 5432:5432 postgres:8.4.22 58 | docker run -d --name nginx -p 80:80 -v /var/run/docker.sock:/tmp/docker.sock jwilder/nginx-proxy 59 | sleep 10 # wait for postgres to be up 60 | docker run -d --name tomcat -p 8081:8080 --link db:db -e TOMCAT_PASS=admin -e VIRTUAL_HOST=docker.local -e VIRTUAL_PORT=8080 csanchez/appfuse-tomcat 61 | 62 | The app should be available at [http://docker.local](http://docker.local). 63 | 64 | ## Integration tests 65 | 66 | To run the integration tests you can start the containers and run the cucumber tests with rake 67 | 68 | rake integration 69 | 70 | To run the cucumber step that accesses the login page you will need to have [phantomjs](http://phantomjs.org/) installed. To install in OS X with homebrew 71 | 72 | brew install phantomjs 73 | 74 | 75 | ## Continuous integration jobs 76 | 77 | Building these jobs in jenkins. Each job triggers the next. The production updates can run in parallel. 78 | 79 | ### [appfuse](https://github.com/carlossg/appfuse) 80 | 81 | Maven job building `continuous-delivery` branch of `https://github.com/carlossg/appfuse`. 82 | Use your repository url instead of localhost. 83 | 84 | clean deploy -DaltDeploymentRepository=maestro-archiva::default::http://localhost:8000/repository/snapshots -DskipTests=true -P h2 85 | 86 | ### [appfuse QA](https://github.com/carlossg/continuous-delivery) 87 | 88 | Shell job building `https://github.com/carlossg/continuous-delivery`. 89 | 90 | bundle exec rake spec 91 | docker build -t csanchez/appfuse-tomcat docker/tomcat 92 | bundle exec rake beaker 93 | docker rm --force db tomcat nginx || true 94 | docker run -d --name db -p 5432:5432 postgres:8.4.22 95 | docker run -d --name nginx -p 80:80 -v /var/run/docker.sock:/tmp/docker.sock jwilder/nginx-proxy 96 | sleep 10 # wait for postgres to be up 97 | docker run -d --name tomcat -e TOMCAT_PASS=admin -p 8081:8080 --link db:db -e VIRTUAL_HOST=docker.local -e VIRTUAL_PORT=8080 csanchez/appfuse-tomcat 98 | bundle exec rake qa 99 | docker rm --force db tomcat nginx || true 100 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require 'rake/clean' 2 | require 'cucumber' 3 | require 'cucumber/rake/task' 4 | 5 | require 'puppetlabs_spec_helper/rake_tasks' 6 | 7 | CLEAN.include('modules/*', 'spec/fixtures/', 'doc') 8 | CLOBBER.include('.tmp', '.librarian') 9 | 10 | task :librarian_spec_prep do 11 | sh "librarian-puppet install" 12 | end 13 | task :spec_prep => :librarian_spec_prep 14 | 15 | Cucumber::Rake::Task.new(:qa) do |t| 16 | FileUtils.mkdir_p("./target") 17 | desc "Run cucumber tests" 18 | t.profile = "qa" 19 | t.cucumber_opts = "-f pretty -f html --out ./target/cucumber.html -f junit --out ./target/test-reports/" 20 | end 21 | 22 | task :integration => [:spec, :beaker, :qa] 23 | 24 | task :default => [:spec] 25 | -------------------------------------------------------------------------------- /Vagrantfile: -------------------------------------------------------------------------------- 1 | # -*- mode: ruby -*- 2 | # vi: set ft=ruby : 3 | 4 | Vagrant.configure("2") do |config| 5 | 6 | # Every Vagrant virtual environment requires a box to build off of. 7 | config.vm.box = "centos-65-x64-virtualbox-puppet" 8 | config.vm.box_url = "http://puppet-vagrant-boxes.puppetlabs.com/centos-65-x64-virtualbox-puppet.box" 9 | 10 | # redhat-lsb-core needed for lsb* facts in tomcat module 11 | config.vm.provision :shell, :inline => "yum install -y redhat-lsb-core" 12 | 13 | # ssh may take some extra time, avoid network dns resolution 14 | config.vm.provider "virtualbox" do |v| 15 | v.customize ["modifyvm", :id, "--natdnshostresolver1", "on"] 16 | end 17 | 18 | # qa server 19 | config.vm.define :qa do |config| 20 | config.vm.hostname = "qa.acme.local" 21 | config.vm.network "forwarded_port", guest: 5432, host: 9432 22 | config.vm.network "forwarded_port", guest: 8080, host: 9081 23 | config.vm.network "forwarded_port", guest: 80, host: 9080 24 | config.vm.network "private_network", ip: "192.168.33.13" 25 | end 26 | 27 | 28 | # db server 29 | config.vm.define :db do |config| 30 | config.vm.hostname = "db.acme.local" 31 | config.vm.network "forwarded_port", guest: 5432, host: 15432 32 | config.vm.network "private_network", ip: "192.168.33.10" 33 | end 34 | 35 | # tomcat server 36 | config.vm.define :tomcat1 do |config| 37 | config.vm.hostname = "tomcat1.acme.local" 38 | config.vm.network "forwarded_port", guest: 8080, host: 18080 39 | config.vm.network "private_network", ip: "192.168.33.11" 40 | end 41 | 42 | # web server 43 | config.vm.define :www do |config| 44 | config.vm.hostname = "www.acme.local" 45 | config.vm.network "forwarded_port", guest: 80, host: 10080 46 | config.vm.network "private_network", ip: "192.168.33.12" 47 | end 48 | 49 | config.vm.provision :puppet do |puppet| 50 | puppet.module_path = "modules" 51 | puppet.manifest_file = "site.pp" 52 | end 53 | 54 | end 55 | -------------------------------------------------------------------------------- /cucumber.yml: -------------------------------------------------------------------------------- 1 | --- 2 | qa: URL=http://docker.local/ 3 | -------------------------------------------------------------------------------- /docker/tomcat/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM tutum/tomcat:8.0 2 | ENV WEBAPP_HOME $CATALINA_HOME/webapps/ROOT 3 | RUN apt-get update && apt-get install -y unzip curl postgresql 4 | RUN rm -rf $CATALINA_HOME/webapps/ROOT 5 | 6 | ENV REPO http://localhost:8000/repository/snapshots/ 7 | ENV VERSION 2.2.2-SNAPSHOT 8 | 9 | # Get the war 10 | RUN curl -sSL -o /tomcat/webapps/ROOT.war $REPO/org/appfuse/appfuse-spring/$VERSION/appfuse-spring-$VERSION.war \ 11 | && mkdir -p $CATALINA_HOME/webapps/ROOT \ 12 | && cd $CATALINA_HOME/webapps/ROOT \ 13 | && unzip ../ROOT.war \ 14 | && rm ../ROOT.war 15 | 16 | # get the postgresql jdbc jar 17 | RUN curl -sSL -o $WEBAPP_HOME/WEB-INF/lib/postgresql-9.1-901.jdbc4.jar http://repo1.maven.org/maven2/postgresql/postgresql/9.1-901.jdbc4/postgresql-9.1-901.jdbc4.jar 18 | 19 | # configure the db connection and copy sql init file 20 | COPY jdbc.properties $WEBAPP_HOME/WEB-INF/classes/ 21 | COPY dump.sql / 22 | COPY run.sh / 23 | -------------------------------------------------------------------------------- /docker/tomcat/dump.sql: -------------------------------------------------------------------------------- 1 | -- 2 | -- PostgreSQL database dump 3 | -- 4 | 5 | SET statement_timeout = 0; 6 | SET client_encoding = 'UTF8'; 7 | SET standard_conforming_strings = on; 8 | SET check_function_bodies = false; 9 | SET client_min_messages = warning; 10 | 11 | 12 | SET search_path = public, pg_catalog; 13 | 14 | SET default_tablespace = ''; 15 | 16 | SET default_with_oids = false; 17 | 18 | CREATE ROLE appfuse WITH PASSWORD 'appfuse' LOGIN; 19 | CREATE DATABASE appfuse WITH OWNER = appfuse TEMPLATE = template0 ENCODING = 'UTF8' LC_COLLATE = 'en_US.UTF-8' LC_CTYPE = 'en_US.UTF-8'; 20 | 21 | \connect appfuse 22 | 23 | 24 | -- 25 | -- Name: app_user; Type: TABLE; Schema: public; Owner: appfuse; Tablespace: 26 | -- 27 | 28 | CREATE TABLE app_user ( 29 | id bigint NOT NULL, 30 | account_expired boolean NOT NULL, 31 | account_locked boolean NOT NULL, 32 | address character varying(150), 33 | city character varying(50), 34 | country character varying(100), 35 | postal_code character varying(15), 36 | province character varying(100), 37 | credentials_expired boolean NOT NULL, 38 | email character varying(255) NOT NULL, 39 | account_enabled boolean, 40 | first_name character varying(50) NOT NULL, 41 | last_name character varying(50) NOT NULL, 42 | password character varying(255) NOT NULL, 43 | password_hint character varying(255), 44 | phone_number character varying(255), 45 | username character varying(50) NOT NULL, 46 | version integer, 47 | website character varying(255) 48 | ); 49 | 50 | 51 | ALTER TABLE public.app_user OWNER TO appfuse; 52 | 53 | -- 54 | -- Name: hibernate_sequence; Type: SEQUENCE; Schema: public; Owner: appfuse 55 | -- 56 | 57 | CREATE SEQUENCE hibernate_sequence 58 | START WITH 1 59 | INCREMENT BY 1 60 | NO MINVALUE 61 | NO MAXVALUE 62 | CACHE 1; 63 | 64 | 65 | ALTER TABLE public.hibernate_sequence OWNER TO appfuse; 66 | 67 | -- 68 | -- Name: hibernate_sequence; Type: SEQUENCE SET; Schema: public; Owner: appfuse 69 | -- 70 | 71 | SELECT pg_catalog.setval('hibernate_sequence', 1, true); 72 | 73 | 74 | -- 75 | -- Name: role; Type: TABLE; Schema: public; Owner: appfuse; Tablespace: 76 | -- 77 | 78 | CREATE TABLE role ( 79 | id bigint NOT NULL, 80 | description character varying(64), 81 | name character varying(20) 82 | ); 83 | 84 | 85 | ALTER TABLE public.role OWNER TO appfuse; 86 | 87 | -- 88 | -- Name: user_role; Type: TABLE; Schema: public; Owner: appfuse; Tablespace: 89 | -- 90 | 91 | CREATE TABLE user_role ( 92 | user_id bigint NOT NULL, 93 | role_id bigint NOT NULL 94 | ); 95 | 96 | 97 | ALTER TABLE public.user_role OWNER TO appfuse; 98 | 99 | -- 100 | -- Data for Name: app_user; Type: TABLE DATA; Schema: public; Owner: appfuse 101 | -- 102 | 103 | COPY app_user (id, account_expired, account_locked, address, city, country, postal_code, province, credentials_expired, email, account_enabled, first_name, last_name, password, password_hint, phone_number, username, version, website) FROM stdin; 104 | -1 f f Denver US 80210 CO f matt_raible@yahoo.com t Tomcat User 12dea96fec20593566ab75692c9949596833adc9 A male kitty. user 1 http://tomcat.apache.org 105 | -2 f f Denver US 80210 CO f matt@raibledesigns.com t Matt Raible d033e22ae348aeb5660fc2140aec35850c4da997 Not a female kitty. admin 1 http://raibledesigns.com 106 | \. 107 | 108 | 109 | -- 110 | -- Data for Name: role; Type: TABLE DATA; Schema: public; Owner: appfuse 111 | -- 112 | 113 | COPY role (id, description, name) FROM stdin; 114 | -1 Administrator role (can edit Users) ROLE_ADMIN 115 | -2 Default role for all Users ROLE_USER 116 | \. 117 | 118 | 119 | -- 120 | -- Data for Name: user_role; Type: TABLE DATA; Schema: public; Owner: appfuse 121 | -- 122 | 123 | COPY user_role (user_id, role_id) FROM stdin; 124 | -1 -2 125 | -2 -1 126 | \. 127 | 128 | 129 | -- 130 | -- Name: app_user_email_key; Type: CONSTRAINT; Schema: public; Owner: appfuse; Tablespace: 131 | -- 132 | 133 | ALTER TABLE ONLY app_user 134 | ADD CONSTRAINT app_user_email_key UNIQUE (email); 135 | 136 | 137 | -- 138 | -- Name: app_user_pkey; Type: CONSTRAINT; Schema: public; Owner: appfuse; Tablespace: 139 | -- 140 | 141 | ALTER TABLE ONLY app_user 142 | ADD CONSTRAINT app_user_pkey PRIMARY KEY (id); 143 | 144 | 145 | -- 146 | -- Name: app_user_username_key; Type: CONSTRAINT; Schema: public; Owner: appfuse; Tablespace: 147 | -- 148 | 149 | ALTER TABLE ONLY app_user 150 | ADD CONSTRAINT app_user_username_key UNIQUE (username); 151 | 152 | 153 | -- 154 | -- Name: role_pkey; Type: CONSTRAINT; Schema: public; Owner: appfuse; Tablespace: 155 | -- 156 | 157 | ALTER TABLE ONLY role 158 | ADD CONSTRAINT role_pkey PRIMARY KEY (id); 159 | 160 | 161 | -- 162 | -- Name: user_role_pkey; Type: CONSTRAINT; Schema: public; Owner: appfuse; Tablespace: 163 | -- 164 | 165 | ALTER TABLE ONLY user_role 166 | ADD CONSTRAINT user_role_pkey PRIMARY KEY (user_id, role_id); 167 | 168 | 169 | -- 170 | -- Name: fk143bf46a4fd90d75; Type: FK CONSTRAINT; Schema: public; Owner: appfuse 171 | -- 172 | 173 | ALTER TABLE ONLY user_role 174 | ADD CONSTRAINT fk143bf46a4fd90d75 FOREIGN KEY (role_id) REFERENCES role(id); 175 | 176 | 177 | -- 178 | -- Name: fk143bf46af503d155; Type: FK CONSTRAINT; Schema: public; Owner: appfuse 179 | -- 180 | 181 | ALTER TABLE ONLY user_role 182 | ADD CONSTRAINT fk143bf46af503d155 FOREIGN KEY (user_id) REFERENCES app_user(id); 183 | 184 | 185 | 186 | -- 187 | -- PostgreSQL database dump complete 188 | -- 189 | 190 | -------------------------------------------------------------------------------- /docker/tomcat/jdbc.properties: -------------------------------------------------------------------------------- 1 | jdbc.driverClassName=org.postgresql.Driver 2 | jdbc.url=jdbc:postgresql://db/appfuse 3 | jdbc.username=appfuse 4 | jdbc.password=appfuse 5 | hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect 6 | -------------------------------------------------------------------------------- /docker/tomcat/run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ ! -f /.tomcat_admin_created ]; then 4 | /create_tomcat_admin_user.sh 5 | fi 6 | 7 | psql -U postgres -h db postgres < /dump.sql 8 | 9 | exec ${CATALINA_HOME}/bin/catalina.sh run 10 | -------------------------------------------------------------------------------- /features/smoke_tests.feature: -------------------------------------------------------------------------------- 1 | @smoke_tests 2 | Feature: Smoke tests 3 | Smoke testing scenarios to make sure all system components are up and running. 4 | 5 | Scenario: Services should be up and listening to their assigned port 6 | * services should be listening on ports: 7 | | 80 | apache | 8 | | 8081 | tomcat | 9 | | 5432 | postgresql | 10 | 11 | Scenario: Appfuse is up and listening on port 80 12 | Given I am at the "/login" page 13 | Then I should see the text "Sign In" 14 | -------------------------------------------------------------------------------- /features/step_definitions/common_steps.rb: -------------------------------------------------------------------------------- 1 | # This file contains general UI testing steps. 2 | 3 | Given /^I am at the "(.*?)" page$/ do |path| 4 | visit path 5 | end 6 | 7 | Then /^I should see the text "([^"]*)"$/ do |text| 8 | page.should have_content text 9 | end 10 | 11 | -------------------------------------------------------------------------------- /features/step_definitions/tcp_ip_steps.rb: -------------------------------------------------------------------------------- 1 | Then /^the "(.*?)" service should be listening on port "(.*?)"$/ do |service, port| 2 | timeout = 60 3 | polling_interval = 2 4 | time_limit = Time.now + timeout 5 | host = URI.parse(Capybara.app_host).host 6 | loop do 7 | begin 8 | s = TCPSocket.new(host, port) 9 | s.close 10 | break 11 | rescue Exception => error 12 | end 13 | raise("#{service} is not listening at #{host} on port #{port}") if Time.now >= time_limit 14 | sleep polling_interval 15 | end 16 | 17 | end 18 | 19 | Then /^services should be listening on ports:$/ do |table| 20 | 21 | table.raw.each do |value| 22 | step %{the "#{value[1]}" service should be listening on port "#{value[0]}"} 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /features/support/env.rb: -------------------------------------------------------------------------------- 1 | require 'capybara' 2 | require 'capybara/cucumber' 3 | require 'capybara/poltergeist' 4 | 5 | Capybara.default_driver = :poltergeist 6 | Capybara.app_host = ENV['URL'] 7 | -------------------------------------------------------------------------------- /features/support/hooks.rb: -------------------------------------------------------------------------------- 1 | def take_screen_capture 2 | path = "target/screenshots/#{Time.now.strftime('%Y-%m-%d-%H-%M-%S')}-screenshot.png" 3 | page.driver.render(path) 4 | end 5 | 6 | # Save a screenshot on for failed scenarios 7 | After('~@api,~@smoke_tests') do |scenario| 8 | if scenario.failed? 9 | take_screen_capture 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /manifests/nodes/db.pp: -------------------------------------------------------------------------------- 1 | # Database node 2 | node /db\..*/ inherits 'parent' { 3 | 4 | file { '/etc/motd': 5 | content => "db server: ${::hostname}\n" 6 | } 7 | 8 | class { 'acme::db_node': } 9 | } 10 | -------------------------------------------------------------------------------- /manifests/nodes/qa.pp: -------------------------------------------------------------------------------- 1 | # QA node with all the stack in one server 2 | node /qa\..*/ inherits 'parent' { 3 | 4 | file { '/etc/motd': 5 | content => "QA server: ${::hostname}\n" 6 | } 7 | 8 | class { 'acme::db_node': } -> 9 | class { 'acme::tomcat_node': 10 | db_host => 'localhost', 11 | } -> 12 | class { 'acme::www_node': 13 | tomcat_host => 'localhost' 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /manifests/nodes/tomcat.pp: -------------------------------------------------------------------------------- 1 | # Tomcat nodes tomcat1, tomcat2, tomcat3,... 2 | node /tomcat\d\..*/ inherits 'parent' { 3 | 4 | file { '/etc/motd': 5 | content => "tomcat server: ${::hostname}\n" 6 | } 7 | 8 | class { 'acme::tomcat_node': } 9 | } 10 | -------------------------------------------------------------------------------- /manifests/nodes/www.pp: -------------------------------------------------------------------------------- 1 | # www node to use Apache as tomcat proxy 2 | node /www\..*/ inherits 'parent' { 3 | 4 | file { '/etc/motd': 5 | content => "web server: ${::hostname}\n" 6 | } 7 | 8 | class { 'acme::www_node': } 9 | } 10 | -------------------------------------------------------------------------------- /manifests/site.pp: -------------------------------------------------------------------------------- 1 | import 'nodes/*.pp' 2 | 3 | node 'parent' { 4 | Exec { 5 | path => ['/bin', '/usr/bin'], 6 | } 7 | 8 | class { 'epel': } -> 9 | class { 'avahi': 10 | firewall => true 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /modules/.PLACEHOLDER: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carlossg/continuous-delivery/6fb0dcb894f52e122ec9db4e36fd1c540c76c7f8/modules/.PLACEHOLDER -------------------------------------------------------------------------------- /mymodules/acme/files/dump.sql: -------------------------------------------------------------------------------- 1 | -- 2 | -- PostgreSQL database dump 3 | -- 4 | 5 | SET statement_timeout = 0; 6 | SET client_encoding = 'UTF8'; 7 | SET standard_conforming_strings = on; 8 | SET check_function_bodies = false; 9 | SET client_min_messages = warning; 10 | 11 | -- 12 | -- Name: plpgsql; Type: EXTENSION; Schema: -; Owner: 13 | -- 14 | 15 | CREATE EXTENSION IF NOT EXISTS plpgsql WITH SCHEMA pg_catalog; 16 | 17 | 18 | -- 19 | -- Name: EXTENSION plpgsql; Type: COMMENT; Schema: -; Owner: 20 | -- 21 | 22 | COMMENT ON EXTENSION plpgsql IS 'PL/pgSQL procedural language'; 23 | 24 | 25 | SET search_path = public, pg_catalog; 26 | 27 | SET default_tablespace = ''; 28 | 29 | SET default_with_oids = false; 30 | 31 | -- 32 | -- Name: app_user; Type: TABLE; Schema: public; Owner: appfuse; Tablespace: 33 | -- 34 | 35 | CREATE TABLE app_user ( 36 | id bigint NOT NULL, 37 | account_expired boolean NOT NULL, 38 | account_locked boolean NOT NULL, 39 | address character varying(150), 40 | city character varying(50), 41 | country character varying(100), 42 | postal_code character varying(15), 43 | province character varying(100), 44 | credentials_expired boolean NOT NULL, 45 | email character varying(255) NOT NULL, 46 | account_enabled boolean, 47 | first_name character varying(50) NOT NULL, 48 | last_name character varying(50) NOT NULL, 49 | password character varying(255) NOT NULL, 50 | password_hint character varying(255), 51 | phone_number character varying(255), 52 | username character varying(50) NOT NULL, 53 | version integer, 54 | website character varying(255) 55 | ); 56 | 57 | 58 | ALTER TABLE public.app_user OWNER TO appfuse; 59 | 60 | -- 61 | -- Name: hibernate_sequence; Type: SEQUENCE; Schema: public; Owner: appfuse 62 | -- 63 | 64 | CREATE SEQUENCE hibernate_sequence 65 | START WITH 1 66 | INCREMENT BY 1 67 | NO MINVALUE 68 | NO MAXVALUE 69 | CACHE 1; 70 | 71 | 72 | ALTER TABLE public.hibernate_sequence OWNER TO appfuse; 73 | 74 | -- 75 | -- Name: hibernate_sequence; Type: SEQUENCE SET; Schema: public; Owner: appfuse 76 | -- 77 | 78 | SELECT pg_catalog.setval('hibernate_sequence', 1, true); 79 | 80 | 81 | -- 82 | -- Name: role; Type: TABLE; Schema: public; Owner: appfuse; Tablespace: 83 | -- 84 | 85 | CREATE TABLE role ( 86 | id bigint NOT NULL, 87 | description character varying(64), 88 | name character varying(20) 89 | ); 90 | 91 | 92 | ALTER TABLE public.role OWNER TO appfuse; 93 | 94 | -- 95 | -- Name: user_role; Type: TABLE; Schema: public; Owner: appfuse; Tablespace: 96 | -- 97 | 98 | CREATE TABLE user_role ( 99 | user_id bigint NOT NULL, 100 | role_id bigint NOT NULL 101 | ); 102 | 103 | 104 | ALTER TABLE public.user_role OWNER TO appfuse; 105 | 106 | -- 107 | -- Data for Name: app_user; Type: TABLE DATA; Schema: public; Owner: appfuse 108 | -- 109 | 110 | COPY app_user (id, account_expired, account_locked, address, city, country, postal_code, province, credentials_expired, email, account_enabled, first_name, last_name, password, password_hint, phone_number, username, version, website) FROM stdin; 111 | -1 f f Denver US 80210 CO f matt_raible@yahoo.com t Tomcat User 12dea96fec20593566ab75692c9949596833adc9 A male kitty. user 1 http://tomcat.apache.org 112 | -2 f f Denver US 80210 CO f matt@raibledesigns.com t Matt Raible d033e22ae348aeb5660fc2140aec35850c4da997 Not a female kitty. admin 1 http://raibledesigns.com 113 | \. 114 | 115 | 116 | -- 117 | -- Data for Name: role; Type: TABLE DATA; Schema: public; Owner: appfuse 118 | -- 119 | 120 | COPY role (id, description, name) FROM stdin; 121 | -1 Administrator role (can edit Users) ROLE_ADMIN 122 | -2 Default role for all Users ROLE_USER 123 | \. 124 | 125 | 126 | -- 127 | -- Data for Name: user_role; Type: TABLE DATA; Schema: public; Owner: appfuse 128 | -- 129 | 130 | COPY user_role (user_id, role_id) FROM stdin; 131 | -1 -2 132 | -2 -1 133 | \. 134 | 135 | 136 | -- 137 | -- Name: app_user_email_key; Type: CONSTRAINT; Schema: public; Owner: appfuse; Tablespace: 138 | -- 139 | 140 | ALTER TABLE ONLY app_user 141 | ADD CONSTRAINT app_user_email_key UNIQUE (email); 142 | 143 | 144 | -- 145 | -- Name: app_user_pkey; Type: CONSTRAINT; Schema: public; Owner: appfuse; Tablespace: 146 | -- 147 | 148 | ALTER TABLE ONLY app_user 149 | ADD CONSTRAINT app_user_pkey PRIMARY KEY (id); 150 | 151 | 152 | -- 153 | -- Name: app_user_username_key; Type: CONSTRAINT; Schema: public; Owner: appfuse; Tablespace: 154 | -- 155 | 156 | ALTER TABLE ONLY app_user 157 | ADD CONSTRAINT app_user_username_key UNIQUE (username); 158 | 159 | 160 | -- 161 | -- Name: role_pkey; Type: CONSTRAINT; Schema: public; Owner: appfuse; Tablespace: 162 | -- 163 | 164 | ALTER TABLE ONLY role 165 | ADD CONSTRAINT role_pkey PRIMARY KEY (id); 166 | 167 | 168 | -- 169 | -- Name: user_role_pkey; Type: CONSTRAINT; Schema: public; Owner: appfuse; Tablespace: 170 | -- 171 | 172 | ALTER TABLE ONLY user_role 173 | ADD CONSTRAINT user_role_pkey PRIMARY KEY (user_id, role_id); 174 | 175 | 176 | -- 177 | -- Name: fk143bf46a4fd90d75; Type: FK CONSTRAINT; Schema: public; Owner: appfuse 178 | -- 179 | 180 | ALTER TABLE ONLY user_role 181 | ADD CONSTRAINT fk143bf46a4fd90d75 FOREIGN KEY (role_id) REFERENCES role(id); 182 | 183 | 184 | -- 185 | -- Name: fk143bf46af503d155; Type: FK CONSTRAINT; Schema: public; Owner: appfuse 186 | -- 187 | 188 | ALTER TABLE ONLY user_role 189 | ADD CONSTRAINT fk143bf46af503d155 FOREIGN KEY (user_id) REFERENCES app_user(id); 190 | 191 | 192 | 193 | -- 194 | -- PostgreSQL database dump complete 195 | -- 196 | 197 | -------------------------------------------------------------------------------- /mymodules/acme/files/properties.aug: -------------------------------------------------------------------------------- 1 | (* Augeas module for editing Java properties files 2 | Author: Craig Dunn 3 | 4 | Limitations: 5 | - doesn't support \ alone on a line 6 | - values are not unescaped 7 | - multi-line properties are broken down by line, and can't be replaced with a single line 8 | 9 | See format info: http://docs.oracle.com/javase/6/docs/api/java/util/Properties.html#load(java.io.Reader) 10 | *) 11 | 12 | 13 | module Properties = 14 | (* Define some basic primitives *) 15 | let empty = Util.empty 16 | let eol = Util.eol 17 | let hard_eol = del "\n" "\n" 18 | let sepch = del /([ \t]*(=|:)|[ \t])/ "=" 19 | let sepspc = del /[ \t]/ " " 20 | let sepch_ns = del /[ \t]*(=|:)/ "=" 21 | let sepch_opt = del /[ \t]*(=|:)?[ \t]*/ "=" 22 | let value_to_eol_ws = store /(:|=)[^\n]*[^ \t\n\\]/ 23 | let value_to_bs_ws = store /(:|=)[^\n]*[^\\\n]/ 24 | let value_to_eol = store /([^ \t\n:=][^\n]*[^ \t\n\\]|[^ \t\n\\:=])/ 25 | let value_to_bs = store /([^ \t\n:=][^\n]*[^\\\n]|[^ \t\n\\:=])/ 26 | let indent = Util.indent 27 | let backslash = del /[\\][ \t]*\n/ "\\\n" 28 | let entry = /([^ \t\n:=\/!#\\]|[\\]:|[\\]=|[\\][\t ]|[\\][^\/\n])+/ 29 | 30 | let multi_line_entry = 31 | [ indent . value_to_bs . backslash ] + . 32 | [ indent . value_to_eol . eol ] . value " < multi > " 33 | 34 | let multi_line_entry_ws = 35 | [ indent . value_to_bs_ws . backslash ] + . 36 | [ indent . value_to_eol . eol ] . value " < multi_ws > " 37 | 38 | (* define comments and properties*) 39 | let bang_comment = [ label "!comment" . del /[ \t]*![ \t]*/ "! " . store /([^ \t\n].*[^ \t\n]|[^ \t\n])/ . eol ] 40 | let comment = ( Util.comment | bang_comment ) 41 | let property = [ indent . key entry . sepch . ( multi_line_entry | indent . value_to_eol . eol ) ] 42 | let property_ws = [ indent . key entry . sepch_ns . ( multi_line_entry_ws | indent . value_to_eol_ws . eol ) ] 43 | let empty_property = [ indent . key entry . sepch_opt . hard_eol ] 44 | let empty_key = [ sepch_ns . ( multi_line_entry | indent . value_to_eol . eol ) ] 45 | 46 | (* setup our lens and filter*) 47 | let lns = ( empty | comment | property_ws | property | empty_property | empty_key ) * 48 | -------------------------------------------------------------------------------- /mymodules/acme/manifests/db_node.pp: -------------------------------------------------------------------------------- 1 | class acme::db_node { 2 | 3 | # install postgres server 4 | class { 'postgresql::server': 5 | ip_mask_allow_all_users => '192.168.0.0/0', 6 | listen_addresses => '*', 7 | postgres_password => 'postgres', 8 | } -> 9 | 10 | # create appfuse database 11 | postgresql::server::db { 'appfuse': 12 | user => 'appfuse', 13 | password => 'appfuse', 14 | grant => 'all', 15 | } -> 16 | 17 | # Create database tables from sql dump 18 | file { '/tmp/dump.sql': 19 | ensure => present, 20 | source => 'puppet:///modules/acme/dump.sql', 21 | } -> 22 | exec { 'appfuse-db': 23 | user => 'postgres', 24 | command => 'psql appfuse -f /tmp/dump.sql && touch /tmp/dump.sql.done', 25 | creates => '/tmp/dump.sql.done', 26 | } 27 | 28 | firewall { '100 allow postgres': 29 | proto => 'tcp', 30 | port => '5432', 31 | action => 'accept', 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /mymodules/acme/manifests/init.pp: -------------------------------------------------------------------------------- 1 | class acme { 2 | # any custom puppet declarations 3 | } 4 | -------------------------------------------------------------------------------- /mymodules/acme/manifests/tomcat_node.pp: -------------------------------------------------------------------------------- 1 | class acme::tomcat_node( 2 | $db_host = 'db.local', 3 | $repo = 'https://repo1.maven.org/maven2', 4 | $appfuse_version = '2.2.2-SNAPSHOT', 5 | $service = 'tomcat-appfuse', 6 | $app_name = 'appfuse', 7 | $tomcat_root = '/srv/tomcat') { 8 | 9 | # install java 10 | class { 'java': } 11 | 12 | # install tomcat 13 | class { 'tomcat': } -> 14 | 15 | # create a tomcat server instance for appfuse 16 | # It allows having multiple independent tomcat servers in different ports 17 | tomcat::instance { 'appfuse': 18 | ensure => present, 19 | http_port => 8080, 20 | } 21 | 22 | # where the war needs to be installed 23 | $webapp = "${tomcat_root}/${app_name}/webapps/ROOT" 24 | 25 | # install maven and download appfuse war file from our archiva instance 26 | class { 'wget': } -> 27 | class { 'maven::maven' : 28 | version => '3.0.4', 29 | } -> 30 | # clean up to deploy the next snapshot 31 | exec { "rm -rf ${webapp}*": } -> 32 | maven { "${webapp}.war": 33 | id => "org.appfuse:appfuse-spring:${appfuse_version}:war", 34 | repos => [$repo], 35 | require => File["${tomcat_root}/${app_name}/webapps"], 36 | notify => Service[$service], 37 | } -> 38 | 39 | # unzip the war file, needed to customize the database 40 | file { $webapp: 41 | ensure => directory, 42 | } 43 | 44 | package { 'unzip': 45 | ensure => present, 46 | } -> 47 | exec { 'unzip-war': 48 | command => "unzip ${webapp}.war", 49 | cwd => $webapp, 50 | creates => "${webapp}/WEB-INF", 51 | require => [Maven["${webapp}.war"], File[$webapp]], 52 | } -> 53 | 54 | # configure appfuse to use postgres database. Download jdbc driver and change jdbc properties 55 | maven { "${webapp}/WEB-INF/lib/postgresql-9.1-901.jdbc4.jar": 56 | id => 'postgresql:postgresql:9.1-901.jdbc4', 57 | repos => [$repo], 58 | } -> 59 | file { "${webapp}/WEB-INF/classes/jdbc.properties": 60 | ensure => present, 61 | content => "jdbc.driverClassName=org.postgresql.Driver 62 | jdbc.url=jdbc:postgresql://${db_host}/appfuse 63 | jdbc.username=appfuse 64 | jdbc.password=appfuse 65 | hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect", 66 | notify => Service[$service], 67 | } -> 68 | 69 | # change appfuse index directory to /tmp/index, otherwise will try to write to tomcat dirs 70 | # use augeas to change that line only 71 | 72 | # Until Augeas has the properties files fixes, use a custom version 73 | # Just a basic approach - for more complete management of lenses consider https://github.com/camptocamp/puppet-augeas 74 | file { "/tmp/properties.aug": 75 | source => "puppet:///modules/acme/properties.aug" 76 | } -> 77 | # Adjust hibernate.properties 78 | augeas { 'update-hibernate-properties': 79 | lens => 'Properties.lns', 80 | incl => "${webapp}/WEB-INF/classes/hibernate.properties", 81 | changes => "set app.search.index.basedir /tmp/index", 82 | load_path => '/tmp', 83 | notify => Service[$service], 84 | } -> 85 | 86 | # Create a folder needed by appfuse 87 | file { "${tomcat_root}/${app_name}/target": 88 | owner => 'tomcat', 89 | ensure => 'directory', 90 | before => Service[$service], 91 | } 92 | 93 | if $::virtual != 'docker' { 94 | firewall { '100 allow tomcat': 95 | proto => 'tcp', 96 | port => '8080', 97 | action => 'accept', 98 | } 99 | } 100 | 101 | } 102 | -------------------------------------------------------------------------------- /mymodules/acme/manifests/www_node.pp: -------------------------------------------------------------------------------- 1 | class acme::www_node($tomcat_host = 'tomcat1.local') { 2 | 3 | class { 'apache': } 4 | class { 'apache::mod::proxy_http': } 5 | 6 | # create a virtualhost that will proxy the tomcat server 7 | apache::vhost { "${::hostname}.local": 8 | port => 80, 9 | docroot => '/var/www', 10 | proxy_dest => "http://${tomcat_host}:8080", 11 | } 12 | 13 | firewall { '100 allow apache': 14 | proto => 'tcp', 15 | port => '80', 16 | action => 'accept', 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /spec/acceptance/nodesets/centos-65-x64-docker.yml: -------------------------------------------------------------------------------- 1 | HOSTS: 2 | centos-65-x64: 3 | roles: 4 | - master 5 | platform: el-6-x86_64 6 | image: devopsil/puppet:3.5.1 7 | # ip: localhost 8 | hypervisor : docker 9 | docker_image_commands: 10 | - yum -y install tar 11 | - useradd vagrant 12 | CONFIG: 13 | log_level: debug 14 | type: git 15 | -------------------------------------------------------------------------------- /spec/acceptance/nodesets/centos-65-x64.yml: -------------------------------------------------------------------------------- 1 | HOSTS: 2 | centos-65-x64: 3 | roles: 4 | - master 5 | platform: el-6-x86_64 6 | box : centos-65-x64-virtualbox-nocm 7 | box_url : http://puppet-vagrant-boxes.puppetlabs.com/centos-65-x64-virtualbox-nocm.box 8 | hypervisor : vagrant 9 | CONFIG: 10 | log_level: debug 11 | type: git 12 | -------------------------------------------------------------------------------- /spec/acceptance/nodesets/default.yml: -------------------------------------------------------------------------------- 1 | HOSTS: 2 | # postgres: 3 | # roles: 4 | # - db 5 | # platform: debian-7-x64 6 | # image: postgres:8.4.22 7 | # hypervisor: docker 8 | # docker_cmd: ["/bin/bash", "-c", "/usr/sbin/sshd -D -o 'PermitRootLogin yes' -o 'PasswordAuthentication yes' & /docker-entrypoint.sh postgres"] 9 | tomcat: 10 | roles: 11 | - default 12 | - webapp 13 | platform: ubuntu-14.04-x64 14 | image: csanchez/appfuse-tomcat 15 | hypervisor: docker 16 | docker_cmd: ["/bin/bash", "-c", "/usr/sbin/sshd -D -o 'PermitRootLogin yes' -o 'PasswordAuthentication yes' & /run.sh"] 17 | CONFIG: 18 | log_level: debug 19 | type: git 20 | -------------------------------------------------------------------------------- /spec/acceptance/system_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper_acceptance' 2 | 3 | describe 'webapp' do 4 | 5 | describe port(8080), 6 | :node => only_host_with_role(hosts, :webapp) do 7 | 8 | before do 9 | # wait for tomcat to start 10 | shell('while ! grep "Starting ProtocolHandler" /tomcat/logs/* > /dev/null; do echo Waiting for tomcat; sleep 2; done') 11 | end 12 | 13 | it { should be_listening } 14 | end 15 | 16 | end 17 | -------------------------------------------------------------------------------- /spec/hosts/db_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe 'db.acme.com' do 4 | include_context :centos 5 | 6 | it { should_not contain_class('java') } 7 | it { should contain_class('postgresql::server') } 8 | end 9 | -------------------------------------------------------------------------------- /spec/hosts/tomcat_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe 'tomcat1.acme.com' do 4 | include_context :centos 5 | 6 | it { should contain_package('java').with_name('java-1.7.0-openjdk-devel') } 7 | 8 | it "configure webapp" do 9 | should contain_maven('/srv/tomcat/appfuse/webapps/ROOT.war') 10 | should contain_maven('/srv/tomcat/appfuse/webapps/ROOT/WEB-INF/lib/postgresql-9.1-901.jdbc4.jar') 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /spec/hosts/www_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe 'www.acme.com' do 4 | include_context :centos 5 | 6 | it { should_not contain_class('java') } 7 | it { should contain_package('httpd') } 8 | it { should contain_firewall('100 allow apache').with_action('accept') } 9 | it { should have_firewall_resource_count(2) } # avahi and apache 10 | end 11 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | require 'rspec-puppet' 2 | 3 | RSpec.configure do |c| 4 | c.module_path = 'modules' 5 | c.manifest_dir = 'manifests' 6 | end 7 | 8 | shared_context :centos do 9 | 10 | let(:facts) {{ 11 | :operatingsystem => 'CentOS', 12 | :kernel => 'Linux', 13 | :osfamily => 'RedHat', 14 | :operatingsystemrelease => '6.3', 15 | :lsbmajdistrelease => '6', 16 | :operatingsystemmajrelease => '6', 17 | :postgres_default_version => '8.4', 18 | :concat_basedir => 'tmp/concat' 19 | }} 20 | 21 | end 22 | -------------------------------------------------------------------------------- /spec/spec_helper_acceptance.rb: -------------------------------------------------------------------------------- 1 | # Beaker workaround for Docker https://github.com/swipely/docker-api/issues/202 2 | require 'docker' 3 | require 'beaker/hypervisor' 4 | 5 | module Beaker 6 | class Docker < Beaker::Hypervisor 7 | def initialize(hosts, options) 8 | require 'docker' 9 | @options = options 10 | @logger = options[:logger] 11 | @hosts = hosts 12 | 13 | # increase the http timeouts as provisioning images can be slow 14 | ::Docker.options[:write_timeout] ||= 300 15 | ::Docker.options[:read_timeout] ||= 300 16 | # assert that the docker-api gem can talk to your docker 17 | # enpoint. Will raise if there is a version mismatch 18 | ::Docker.validate_version! 19 | # Pass on all the logging from docker-api to the beaker logger instance 20 | ::Docker.logger = @logger 21 | end 22 | end 23 | end 24 | 25 | ENV['DOCKER_HOST'] = ENV['DOCKER_HOST'].gsub(%r{tcp://}, 'https://') 26 | cert_path = File.expand_path ENV['DOCKER_CERT_PATH'] 27 | Docker.options = { 28 | :client_cert => File.join(cert_path, 'cert.pem'), 29 | :client_key => File.join(cert_path, 'key.pem'), 30 | :ssl_ca_file => File.join(cert_path, 'ca.pem'), 31 | :ssl_verify_peer => false, 32 | :ssl_version => :TLSv1_2 33 | } 34 | 35 | # End workaround 36 | 37 | 38 | require 'beaker-rspec/spec_helper' 39 | require 'beaker-rspec/helpers/serverspec' 40 | --------------------------------------------------------------------------------