├── .docker └── nginx │ ├── Dockerfile │ └── nginx.conf ├── .gitignore ├── .rspec ├── .rubocop.yml ├── .ruby-version ├── .travis.yml ├── Dockerfile ├── Gemfile ├── Gemfile.lock ├── LICENSE ├── README.md ├── Rakefile ├── bin ├── deploy └── prepare ├── circle.yml ├── docker-entrypoint.sh └── spec ├── acceptance ├── 000_docker_spec.rb ├── 100_integration_spec.rb ├── 110_integration_mysql57_spec.rb ├── 120_integration_postgres93_spec.rb └── 130_integration_reverse_proxy_spec.rb ├── spec_helper.rb └── support ├── docker_dsl.rb ├── helpers └── docker_helper.rb ├── matchers ├── console_output_matcher.rb ├── exposed_ports_matcher.rb ├── file_content_matcher.rb ├── mapped_ports_matcher.rb ├── volume_matcher.rb ├── wait_for_console_matcher.rb └── workdir_matcher.rb ├── shared_examples ├── a_buildable_docker_image_shared_context.rb ├── bamboo_shared_example.rb ├── clean_console_shared_example.rb ├── clean_logfile_shared_example.rb ├── using_a_mysql_database_shared_example.rb ├── using_a_postgresql_database_shared_example.rb └── using_an_embedded_database_shared_example.rb └── waiting_helper.rb /.docker/nginx/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM nginx:alpine 2 | 3 | COPY nginx.conf /etc/nginx/nginx.conf 4 | -------------------------------------------------------------------------------- /.docker/nginx/nginx.conf: -------------------------------------------------------------------------------- 1 | #user nobody; 2 | worker_processes 1; 3 | 4 | #error_log logs/error.log; 5 | #error_log logs/error.log notice; 6 | #error_log logs/error.log info; 7 | 8 | #pid logs/nginx.pid; 9 | 10 | 11 | events { 12 | worker_connections 1024; 13 | } 14 | 15 | 16 | http { 17 | include mime.types; 18 | default_type application/octet-stream; 19 | 20 | log_format main '$remote_addr - $remote_user [$time_local] "$request" ' 21 | '$status $body_bytes_sent "$http_referer" ' 22 | '"$http_user_agent" "$http_x_forwarded_for"'; 23 | 24 | #access_log logs/access.log main; 25 | 26 | 27 | keepalive_timeout 600; 28 | proxy_connect_timeout 600; 29 | proxy_send_timeout 600; 30 | proxy_read_timeout 600; 31 | 32 | server { 33 | listen 80; 34 | 35 | location /bamboo-path { 36 | proxy_set_header X-Forwarded-Host $host; 37 | proxy_set_header X-Forwarded-Server $host; 38 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 39 | proxy_pass http://container:8085/bamboo-path; 40 | client_max_body_size 100M; 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.gitignore.io 2 | 3 | ### Linux ### 4 | *~ 5 | 6 | # KDE directory preferences 7 | .directory 8 | 9 | # Linux trash folder which might appear on any partition or disk 10 | .Trash-* 11 | 12 | ### Ruby ### 13 | *.gem 14 | *.rbc 15 | /.config 16 | /coverage/ 17 | /InstalledFiles 18 | /pkg/ 19 | /spec/reports/ 20 | /test/tmp/ 21 | /test/version_tmp/ 22 | /tmp/ 23 | 24 | ## Specific to RubyMotion: 25 | .dat* 26 | .repl_history 27 | build/ 28 | 29 | ## Documentation cache and generated files: 30 | /.yardoc/ 31 | /_yardoc/ 32 | /doc/ 33 | /rdoc/ 34 | 35 | ## Environment normalisation: 36 | /.bundle/ 37 | /vendor/bundle 38 | /lib/bundler/man/ 39 | 40 | # for a library or gem, you might want to ignore these files since the code is 41 | # intended to run in multiple environments; otherwise, check them in: 42 | # Gemfile.lock 43 | # .ruby-version 44 | # .ruby-gemset 45 | 46 | # unless supporting rvm < 1.11.0 or doing something fancy, ignore this: 47 | .rvmrc 48 | -------------------------------------------------------------------------------- /.rspec: -------------------------------------------------------------------------------- 1 | --color 2 | --require spec_helper 3 | --format documentation 4 | --fail-fast -------------------------------------------------------------------------------- /.rubocop.yml: -------------------------------------------------------------------------------- 1 | AllCops: 2 | Exclude: 3 | - 'vendor/**/*' 4 | - 'spec/fixtures/**/*' 5 | - 'tmp/**/*' 6 | TargetRubyVersion: 2.2 7 | 8 | Metrics/AbcSize: 9 | Enabled: false 10 | 11 | Metrics/LineLength: 12 | Enabled: false 13 | 14 | Metrics/MethodLength: 15 | Enabled: false 16 | 17 | Style/Documentation: 18 | Enabled: false 19 | 20 | Style/AlignHash: 21 | Enabled: false 22 | 23 | Style/AlignParameters: 24 | Enabled: false 25 | 26 | Style/PredicateName: 27 | Enabled: false 28 | -------------------------------------------------------------------------------- /.ruby-version: -------------------------------------------------------------------------------- 1 | 2.4.2 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | branches: 2 | only: 3 | - master 4 | - eap 5 | 6 | sudo: required 7 | 8 | language: ruby 9 | 10 | services: 11 | - docker 12 | 13 | addons: 14 | apt: 15 | packages: 16 | - docker-ce 17 | 18 | script: 19 | - bundle exec rspec 20 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM openjdk:8-alpine 2 | 3 | # Setup useful environment variables 4 | ENV BAMBOO_HOME /var/atlassian/bamboo 5 | ENV BAMBOO_INSTALL /opt/atlassian/bamboo 6 | ENV BAMBOO_VERSION 6.8.0 7 | 8 | # Install Atlassian Bamboo and helper tools and setup initial home 9 | # directory structure. 10 | RUN set -x \ 11 | && addgroup -S bamboo \ 12 | && adduser -S -h "${BAMBOO_HOME}" bamboo bamboo \ 13 | && apk add --no-cache curl xmlstarlet git openssh bash ttf-dejavu libc6-compat tzdata \ 14 | && mkdir -p "${BAMBOO_HOME}/lib" \ 15 | && chmod -R 700 "${BAMBOO_HOME}" \ 16 | && chown -R bamboo:bamboo "${BAMBOO_HOME}" \ 17 | && mkdir -p "${BAMBOO_INSTALL}" \ 18 | && curl -Ls "https://www.atlassian.com/software/bamboo/downloads/binary/atlassian-bamboo-${BAMBOO_VERSION}.tar.gz" | tar -zx --directory "${BAMBOO_INSTALL}" --strip-components=1 --no-same-owner \ 19 | && curl -Ls "https://dev.mysql.com/get/Downloads/Connector-J/mysql-connector-java-5.1.40.tar.gz" | tar -xz --directory "${BAMBOO_INSTALL}/lib" --strip-components=1 --no-same-owner "mysql-connector-java-5.1.40/mysql-connector-java-5.1.40-bin.jar" \ 20 | && chmod -R 700 "${BAMBOO_INSTALL}" \ 21 | && chown -R bamboo:bamboo "${BAMBOO_INSTALL}" \ 22 | && sed --in-place 's/^# umask 0027$/umask 0027/g' "${BAMBOO_INSTALL}/bin/setenv.sh" \ 23 | && xmlstarlet ed --inplace \ 24 | --delete "Server/Service/Engine/Host/@xmlValidation" \ 25 | --delete "Server/Service/Engine/Host/@xmlNamespaceAware" \ 26 | "${BAMBOO_INSTALL}/conf/server.xml" \ 27 | && touch -d "@0" "${BAMBOO_INSTALL}/conf/server.xml" 28 | 29 | 30 | # Use the default unprivileged account. This could be considered bad practice 31 | # on systems where multiple processes end up being executed by 'daemon' but 32 | # here we only ever run one process anyway. 33 | USER bamboo:bamboo 34 | 35 | # Expose default HTTP and SSH ports. 36 | EXPOSE 8085 54663 37 | 38 | # Set volume mount points for installation and home directory. Changes to the 39 | # home directory needs to be persisted as well as parts of the installation 40 | # directory due to eg. logs. 41 | VOLUME ["/var/atlassian/bamboo","/opt/atlassian/bamboo/logs"] 42 | 43 | # Set the default working directory as the Bamboo home directory. 44 | WORKDIR /var/atlassian/bamboo 45 | 46 | COPY "docker-entrypoint.sh" "/" 47 | ENTRYPOINT ["/docker-entrypoint.sh"] 48 | 49 | # Run Atlassian Bamboo as a foreground process by default. 50 | CMD ["/opt/atlassian/bamboo/bin/start-bamboo.sh", "-fg"] 51 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | gem 'rake' 4 | gem 'rspec' 5 | 6 | gem 'capybara' 7 | gem 'poltergeist' 8 | gem 'poltergeist-suppressor' 9 | 10 | gem 'docker-api', require: 'docker' 11 | gem 'minitar' 12 | gem 'rspec_junit_formatter' 13 | 14 | gem 'rb-readline' 15 | 16 | gem 'pry' 17 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | specs: 4 | addressable (2.5.2) 5 | public_suffix (>= 2.0.2, < 4.0) 6 | capybara (2.17.0) 7 | addressable 8 | mini_mime (>= 0.1.3) 9 | nokogiri (>= 1.3.3) 10 | rack (>= 1.0.0) 11 | rack-test (>= 0.5.4) 12 | xpath (>= 2.0, < 4.0) 13 | cliver (0.3.2) 14 | coderay (1.1.2) 15 | diff-lcs (1.3) 16 | docker-api (1.34.0) 17 | excon (>= 0.47.0) 18 | multi_json 19 | excon (0.60.0) 20 | method_source (0.9.0) 21 | mini_mime (1.0.0) 22 | mini_portile2 (2.3.0) 23 | minitar (0.6.1) 24 | multi_json (1.13.1) 25 | nokogiri (1.8.2) 26 | mini_portile2 (~> 2.3.0) 27 | poltergeist (1.17.0) 28 | capybara (~> 2.1) 29 | cliver (~> 0.3.1) 30 | websocket-driver (>= 0.2.0) 31 | poltergeist-suppressor (0.0.1) 32 | capybara 33 | poltergeist 34 | pry (0.11.3) 35 | coderay (~> 1.1.0) 36 | method_source (~> 0.9.0) 37 | public_suffix (3.0.1) 38 | rack (2.0.4) 39 | rack-test (0.8.2) 40 | rack (>= 1.0, < 3) 41 | rake (12.3.0) 42 | rb-readline (0.5.5) 43 | rspec (3.7.0) 44 | rspec-core (~> 3.7.0) 45 | rspec-expectations (~> 3.7.0) 46 | rspec-mocks (~> 3.7.0) 47 | rspec-core (3.7.1) 48 | rspec-support (~> 3.7.0) 49 | rspec-expectations (3.7.0) 50 | diff-lcs (>= 1.2.0, < 2.0) 51 | rspec-support (~> 3.7.0) 52 | rspec-mocks (3.7.0) 53 | diff-lcs (>= 1.2.0, < 2.0) 54 | rspec-support (~> 3.7.0) 55 | rspec-support (3.7.1) 56 | rspec_junit_formatter (0.3.0) 57 | rspec-core (>= 2, < 4, != 2.12.0) 58 | websocket-driver (0.7.0) 59 | websocket-extensions (>= 0.1.0) 60 | websocket-extensions (0.1.3) 61 | xpath (3.0.0) 62 | nokogiri (~> 1.8) 63 | 64 | PLATFORMS 65 | ruby 66 | 67 | DEPENDENCIES 68 | capybara 69 | docker-api 70 | minitar 71 | poltergeist 72 | poltergeist-suppressor 73 | pry 74 | rake 75 | rb-readline 76 | rspec 77 | rspec_junit_formatter 78 | 79 | BUNDLED WITH 80 | 1.16.0 81 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Martin Aksel Jensen, maage@dotmaage.dk 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://img.shields.io/circleci/project/cptactionhank/docker-atlassian-bamboo.svg)](https://circleci.com/gh/cptactionhank/docker-atlassian-bamboo) [![Open Issues](https://img.shields.io/github/issues/cptactionhank/docker-atlassian-bamboo.svg)](https://github.com/cptactionhank/docker-atlassian-bamboo/issues) [![Stars on GitHub](https://img.shields.io/github/stars/cptactionhank/docker-atlassian-bamboo.svg)](https://github.com/cptactionhank/docker-atlassian-bamboo/stargazers) [![Forks on GitHub](https://img.shields.io/github/forks/cptactionhank/docker-atlassian-bamboo.svg)](https://github.com/cptactionhank/docker-atlassian-bamboo/network) [![Stars on Docker Hub](https://img.shields.io/docker/stars/cptactionhank/atlassian-bamboo.svg)](https://hub.docker.com/r/cptactionhank/atlassian-bamboo/) [![Pulls on Docker Hub](https://img.shields.io/docker/pulls/cptactionhank/atlassian-bamboo.svg)](https://hub.docker.com/r/cptactionhank/atlassian-bamboo/) [![Sponsor by PayPal](https://img.shields.io/badge/sponsor-PayPal-blue.svg)](https://paypal.me/cptactionhank/5) 2 | 3 | # Atlassian Bamboo in a Docker container 4 | 5 | This is a containerized installation of Atlassian Bamboo with Docker, and it's a match made in heaven for us all to enjoy. The aim of this image is to keep the image as vanilla as possible, only with a few Docker related twists. You can get started by clicking the appropriate link below and reading the documentation. 6 | 7 | * [Atlassian JIRA Core](https://cptactionhank.github.io/docker-atlassian-jira) 8 | * [Atlassian JIRA Software](https://cptactionhank.github.io/docker-atlassian-jira-software) 9 | * [Atlassian JIRA Service Desk](https://cptactionhank.github.io/docker-atlassian-service-desk) 10 | * [Atlassian Confluence](https://cptactionhank.github.io/docker-atlassian-confluence) 11 | * [Atlassian Bamboo](https://github.com/cptactionhank/docker-atlassian-bamboo) 12 | * [Atlassian Bitbucket Server](https://cptactionhank.github.io/docker-atlassian-bitbucket) 13 | 14 | If you want to help out, you can check out the contribution section further down. 15 | 16 | ## I'm in the fast lane! Get me started 17 | 18 | To quickly get started running a Bamboo instance, use the following command: 19 | ```bash 20 | docker run --detach --publish 8085:8085 cptactionhank/atlassian-bamboo:latest 21 | ``` 22 | 23 | Then simply navigate to [`http://localhost:8085`](http://localhost:8085) and finish the installation. 24 | 25 | ## Configuration 26 | 27 | You can configure a small set of things by supplying the following environment variables 28 | 29 | | Environment Variable | Description | 30 | | ---------------------- | ----------- | 31 | | X_PROXY_NAME | Sets the Tomcat Connectors `ProxyName` attribute | 32 | | X_PROXY_PORT | Sets the Tomcat Connectors `ProxyPort` attribute | 33 | | X_PROXY_SCHEME | If set to `https` the Tomcat Connectors `secure=true` and `redirectPort` equal to `X_PROXY_PORT` | 34 | | X_PATH | Sets the Tomcat connectors `path` attribute | 35 | | TZ | Sets the timezone used by the Bamboo server. E.g. `Europe/Berlin` | 36 | 37 | ## Contributions 38 | 39 | This image has been created with the best intentions and an expert understanding of Docker, but it should not be expected to be flawless. Should you be in the position to do so, I request that you help support this repository with best-practices and other additions. 40 | 41 | CircleCI has been configured to build the `Dockerfile` and run acceptance tests on the Atlassian Bamboo image to ensure it is working. Additionally it been configured to automatically deploy new version branches upon successfully building a new version of Atlassian Bamboo in the `master` branch and serves as the base. 42 | 43 | If you see out of date documentation, lack of tests, etc., you can help out by either 44 | - creating an issue and opening a discussion, or 45 | - sending a pull request with modifications (Remember to read [contributing guide](CONTRIBUTING.md) before) 46 | 47 | Continuous Integration and Continuous Delivery is made possible with the great services from [GitHub](https://github.com) and [CircleCI](https://circleci.com/) written in [Ruby](https://www.ruby-lang.org/), using [RSpec](http://rspec.info/), [Capybara](https://github.com/teamcapybara/capybara/), and [PhantomJS](http://phantomjs.org/) frameworks. 48 | 49 | ## Donations 50 | 51 | Thank you for wanting to help support this repository by supporting me and my supply of hair cuts, tea and coffee, among others. 52 | 53 | __Bitcoin:__ `1CT2J3kT1kmj9Z6f4SEvvL3oAkNFQwD5kQ` 54 | 55 | __Ethereum:__ `0x82305dcE146b2aCaDA0d63235b84c187A5A23c36` 56 | 57 | __Doge:__ `DDKU3SHDu7BcR1P7n5qXGwb8SviiCG5gFX` 58 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require 'rspec/core/rake_task' 2 | 3 | RSpec::Core::RakeTask.new :spec 4 | task default: :spec 5 | -------------------------------------------------------------------------------- /bin/deploy: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Prepare Git committer information 4 | git config user.email "maage@dotmaage.dk" 5 | git config user.name "Circle CI Automated Builder" 6 | 7 | # Obtain the currently latest version of Atlassian Bamboo version defined by the 8 | # Dockerfile in this repository. 9 | VERSION=$(sed -nr 's/ENV BAMBOO_VERSION[[:space:]]*(.+)/\1/p' Dockerfile) 10 | 11 | echo "Checking if there has been made any modifications" 12 | 13 | git diff --exit-code . 14 | 15 | if [[ $? == 0 ]]; then 16 | 17 | # The specific branch already exists, consider committing a patch if there 18 | # was actual changes to the repository. 19 | echo "There was no changes. Skipping deployment..." 20 | 21 | else 22 | 23 | echo "Committing changes" 24 | 25 | # add changes made to current branch 26 | git add --all 27 | git commit --message "Updated Atlassian Bamboo master branch to latest version ${VERSION}" 28 | 29 | # push changes made to current branch 30 | git push origin master 31 | 32 | fi 33 | 34 | echo "Checking if branch is already available?" 35 | 36 | git fetch --all &> /dev/null 37 | git show-branch "origin/${VERSION}" &> /dev/null 38 | 39 | if [[ $? == 0 ]]; then 40 | 41 | # The specific branch already exists, consider committing a patch if there 42 | # was actual changes to the repository. 43 | echo "Branch '${VERSION}' already exist. Skipping..." 44 | 45 | else 46 | 47 | # Create a new orphaned version branch to the repository with the current 48 | # changes performed in the master repository and thereby keeps the `master` 49 | # and latest version branch in sync. 50 | echo "Creating new branch '${VERSION}'" 51 | 52 | git checkout --orphan "${VERSION}" 53 | 54 | # We do not want to include README.md in version branches since the latest 55 | # built branch from the Docker Hub will use that README.md which could be 56 | # misleading. Contributing is also taken out to ensure a single place for 57 | # the most up-to-date information. 58 | rm --recursive --force "README.md" "CONTRIBUTING.md" 59 | 60 | # Perform initial commit with the current directory as is (ie. prepared for 61 | # the new version branch). 62 | echo "Committing changes" 63 | 64 | git add --all 65 | git commit --message "Created new Atlassian Bamboo branch for version ${VERSION}" 66 | 67 | # Acutally push the new branch to the origin repository (GitHub) such that 68 | # the changes are published for the Docker Hub repository and everyone else. 69 | echo "Pushing new branch '${VERSION}'" 70 | 71 | git push "origin" "${VERSION}" || exit 255 72 | 73 | # Notify the new branch has been deployed. 74 | echo "Deployed new version to new branch '${VERSION}'" 75 | 76 | fi 77 | -------------------------------------------------------------------------------- /bin/prepare: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Usage: bin/prepare [VERSION] 4 | # 5 | # Prepares the current directory according to the given Atlassian Bamboo 6 | # VERSION. If no VERSION information is specified the latest version is 7 | # retrieved from the Atlassian Bamboo download feed. 8 | 9 | if [[ "x${1}" == "x" && "$(git rev-parse --abbrev-ref HEAD)" != "master" ]]; then 10 | echo "Not master branch and therefore nothing to prepare" 11 | exit 0 12 | fi 13 | 14 | echo "Obtaining Altassian Bamboo version information..." 15 | 16 | # Obtain the currently latest version of Atlassian Bamboo version defined 17 | # by the Dockerfile in this repository. 18 | CURRENT_VERSION=$(sed -nr 's/ENV BAMBOO_VERSION[[:space:]]*(.+)/\1/p' Dockerfile) 19 | 20 | # Obtain the latest Atlassian Bamboo version by going to the JSON version 21 | # feed information to get a JSONP formatted response, strip the output to 22 | # retrieve the actual content and then get the version number from the first 23 | # entry. 24 | VERSION=${1:-"$(curl -Ls 'https://my.atlassian.com/download/feeds/current/bamboo.json' | sed 's/downloads(\(.*\))/\1/g' | jq -r '.[0].version')"} 25 | 26 | # Outputs the version information 27 | echo "Found versions:" 28 | echo " Current: ${CURRENT_VERSION}" 29 | echo " New: ${VERSION}" 30 | echo "" 31 | 32 | echo "Preparing branch for version new Atlassian Bamboo version" 33 | 34 | # Edit the Dockerfile by changing the current version to the new obtained 35 | # version from the Atlassian Bamboo version feed. 36 | sed --in-place \ 37 | --expression "s/ENV BAMBOO_VERSION ${CURRENT_VERSION}/ENV BAMBOO_VERSION ${VERSION}/g" \ 38 | Dockerfile 39 | 40 | echo "Ready for acceptance testing" 41 | -------------------------------------------------------------------------------- /circle.yml: -------------------------------------------------------------------------------- 1 | machine: 2 | pre: 3 | - sudo apt-get install jq 4 | services: 5 | - docker 6 | 7 | dependencies: 8 | pre: 9 | - docker pull nginx:alpine 10 | - docker pull postgres:9.3 11 | - docker pull mysql:5.6 12 | - docker pull openjdk:8-alpine 13 | - docker build . 14 | 15 | test: 16 | pre: 17 | - bin/prepare 18 | 19 | deployment: 20 | newest-version: 21 | branch: master 22 | commands: 23 | - bin/deploy 24 | -------------------------------------------------------------------------------- /docker-entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # check if the `server.xml` file has been changed since the creation of this 4 | # Docker image. If the file has been changed the entrypoint script will not 5 | # perform modifications to the configuration file. 6 | if [ "$(stat -c "%Y" "${BAMBOO_INSTALL}/conf/server.xml")" -eq "0" ]; then 7 | if [ -n "${X_PROXY_NAME}" ]; then 8 | xmlstarlet ed --inplace --pf --ps --insert '//Connector[@port="8085"]' --type "attr" --name "proxyName" --value "${X_PROXY_NAME}" "${BAMBOO_INSTALL}/conf/server.xml" 9 | fi 10 | if [ -n "${X_PROXY_PORT}" ]; then 11 | xmlstarlet ed --inplace --pf --ps --insert '//Connector[@port="8085"]' --type "attr" --name "proxyPort" --value "${X_PROXY_PORT}" "${BAMBOO_INSTALL}/conf/server.xml" 12 | fi 13 | if [ -n "${X_PROXY_SCHEME}" ]; then 14 | xmlstarlet ed --inplace --pf --ps --insert '//Connector[@port="8085"]' --type "attr" --name "scheme" --value "${X_PROXY_SCHEME}" "${BAMBOO_INSTALL}/conf/server.xml" 15 | fi 16 | if [ "${X_PROXY_SCHEME}" = "https" ]; then 17 | xmlstarlet ed --inplace --pf --ps --insert '//Connector[@port="8085"]' --type "attr" --name "secure" --value "true" "${BAMBOO_INSTALL}/conf/server.xml" 18 | xmlstarlet ed --inplace --pf --ps --update '//Connector[@port="8085"]/@redirectPort' --value "${X_PROXY_PORT}" "${BAMBOO_INSTALL}/conf/server.xml" 19 | fi 20 | if [ -n "${X_PATH}" ]; then 21 | xmlstarlet ed --inplace --pf --ps --update '//Context/@path' --value "${X_PATH}" "${BAMBOO_INSTALL}/conf/server.xml" 22 | fi 23 | fi 24 | 25 | exec "$@" 26 | -------------------------------------------------------------------------------- /spec/acceptance/000_docker_spec.rb: -------------------------------------------------------------------------------- 1 | describe 'Docker image building' do 2 | context 'when validating host software' do 3 | it 'should supported version' do 4 | expect { Docker.validate_version! }.to_not raise_error 5 | end 6 | end 7 | 8 | context 'when building image' do 9 | subject { Docker::Image.build_from_dir '.' } 10 | 11 | it { is_expected.to_not be_nil } 12 | it { is_expected.to have_exposed_port tcp: 8085 } 13 | it { is_expected.to_not have_exposed_port udp: 8085 } 14 | it { is_expected.to have_volume '/var/atlassian/bamboo' } 15 | it { is_expected.to have_volume '/opt/atlassian/bamboo/logs' } 16 | it { is_expected.to have_working_directory '/var/atlassian/bamboo' } 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /spec/acceptance/100_integration_spec.rb: -------------------------------------------------------------------------------- 1 | describe 'Atlassian Bamboo with Embedded Database' do 2 | include_examples 'a buildable Docker image', '.', env: ["CATALINA_OPTS=-Xms1024m -Xmx2048m -Djava.security.egd=file:/dev/./urandom -Datlassian.plugins.enable.wait=#{Docker::DSL.timeout}"] 3 | 4 | include_examples 'an acceptable Atlassian Bamboo instance', 'using an embedded database' 5 | end 6 | -------------------------------------------------------------------------------- /spec/acceptance/110_integration_mysql57_spec.rb: -------------------------------------------------------------------------------- 1 | describe 'Atlassian Bamboo with MySQL 5.7 Database' do 2 | include_examples 'a buildable Docker image', '.', env: ["CATALINA_OPTS=-Xms1024m -Xmx2048m -Djava.security.egd=file:/dev/./urandom -Datlassian.plugins.enable.wait=#{Docker::DSL.timeout}"] 3 | 4 | include_examples 'an acceptable Atlassian Bamboo instance', 'using a MySQL database' do 5 | before :all do 6 | Docker::Image.create fromImage: 'mysql:5.7' 7 | # Create and run a MySQL 5.7 container instance 8 | @container_db = Docker::Container.create image: 'mysql:5.7', env: ['MYSQL_ROOT_PASSWORD=mysecretpassword'] 9 | @container_db.start! 10 | # Wait for the MySQL instance to start 11 | @container_db.wait_for_output %r{socket:\ '/var/run/mysqld/mysqld\.sock'\ \ port:\ 3306\ \ MySQL\ Community\ Server\ \(GPL\)} 12 | # Create JIRA database 13 | if ENV['CIRCLECI'] 14 | `docker run --link "#{@container_db.id}:db" mysql:5.7 mysql --host "db" --user=root --password=mysecretpassword --execute 'CREATE DATABASE bamboodb CHARACTER SET utf8 COLLATE utf8_bin;'` 15 | else 16 | @container_db.exec ['mysql', '--user=root', '--password=mysecretpassword', '--execute', 'CREATE DATABASE bamboodb CHARACTER SET utf8 COLLATE utf8_bin;'] 17 | end 18 | end 19 | after :all do 20 | if ENV['CIRCLECI'] 21 | @container_db.kill signal: 'SIGKILL' unless @container_db.nil? 22 | else 23 | @container_db.kill signal: 'SIGKILL' unless @container_db.nil? 24 | @container_db.remove force: true, v: true unless @container_db.nil? 25 | end 26 | end 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /spec/acceptance/120_integration_postgres93_spec.rb: -------------------------------------------------------------------------------- 1 | describe 'Atlassian Bamboo with PostgreSQL 9.3 Database' do 2 | include_examples 'a buildable Docker image', '.', env: ["CATALINA_OPTS=-Xms1024m -Xmx2048m -Djava.security.egd=file:/dev/./urandom -Datlassian.plugins.enable.wait=#{Docker::DSL.timeout}"] 3 | 4 | include_examples 'an acceptable Atlassian Bamboo instance', 'using a PostgreSQL database' do 5 | before :all do 6 | Docker::Image.create fromImage: 'postgres:9.3' 7 | # Create and run a PostgreSQL 9.3 container instance 8 | @container_db = Docker::Container.create image: 'postgres:9.3' 9 | @container_db.start! 10 | # Wait for the PostgreSQL instance to start 11 | @container_db.wait_for_output(/PostgreSQL\ init\ process\ complete;\ ready\ for\ start\ up\./) 12 | # Create JIRA database 13 | if ENV['CIRCLECI'] 14 | `docker run --rm --link "#{@container_db.id}:db" postgres:9.3 psql --host "db" --user "postgres" --command "create database bamboodb owner postgres encoding 'utf8';"` 15 | else 16 | @container_db.exec ['psql', '--username', 'postgres', '--command', "create database bamboodb owner postgres encoding 'utf8';"] 17 | end 18 | end 19 | after :all do 20 | @container_db.kill signal: 'SIGKILL' unless @container_db.nil? 21 | @container_db.remove force: true, v: true unless @container_db.nil? || ENV['CIRCLECI'] 22 | end 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /spec/acceptance/130_integration_reverse_proxy_spec.rb: -------------------------------------------------------------------------------- 1 | describe 'Atlassian Bamboo behind reverse proxy' do 2 | include_examples 'a buildable Docker image', '.', 3 | env: [ 4 | "CATALINA_OPTS=-Xms1024m -Xmx2048m -Djava.security.egd=file:/dev/./urandom -Datlassian.plugins.enable.wait=#{Docker::DSL.timeout}", 5 | "X_PROXY_NAME=localhost", 6 | 'X_PROXY_PORT=1234', 7 | 'X_PROXY_SCHEME=http', 8 | 'X_PATH=/bamboo-path' 9 | ] 10 | 11 | include_examples 'an acceptable Atlassian Bamboo instance', 'using an embedded database' do 12 | before :all do 13 | image = Docker::Image.build_from_dir '.docker/nginx' 14 | # Create and run a nginx reverse proxy container instance 15 | @container_proxy = Docker::Container.create image: image.id, 16 | portBindings: { '80/tcp' => [{ 'HostPort' => '1234' }] }, 17 | links: ["#{@container.id}:container"] 18 | @container_proxy.start! 19 | @container_proxy.setup_capybara_url({ tcp: 80 }, '/bamboo-path') 20 | end 21 | 22 | after :all do 23 | if ENV['CIRCLECI'] 24 | @container_proxy.kill signal: 'SIGKILL' unless @container_proxy.nil? 25 | else 26 | @container_proxy.kill signal: 'SIGKILL' unless @container_proxy.nil? 27 | @container_proxy.remove force: true, v: true unless @container_proxy.nil? 28 | end 29 | end 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | require 'docker' 2 | require 'capybara' 3 | require 'capybara/dsl' 4 | require 'capybara/poltergeist' 5 | require 'poltergeist/suppressor' 6 | 7 | REGEX_WARN = /WARNING|WARN/ 8 | REGEX_ERROR = /ERROR|ERR/ 9 | REGEX_SEVERE = /SEVERE|FATAL/ 10 | REGEX_STARTUP = /Server startup in (\d+ ms)/ 11 | REGEX_FILTER = Regexp.compile Regexp.union [ 12 | /org\.apache\.catalina\.loader\.WebappClassLoaderBase\.checkThreadLocalMapForLeaks\ The\ web\ application\ \[.+\]\ created\ a\ ThreadLocal\ with\ key\ of\ type\ \[.*\]\ \(value\ \[.*\]\)\ and\ a\ value\ of\ type\ \[.*\]\ \(value\ \[.*\]\)\ but\ failed\ to\ remove\ it\ when\ the\ web\ application\ was\ stopped\.\ Threads\ are\ going\ to\ be\ renewed\ over\ time\ to\ try\ and\ avoid\ a\ probable\ memory\ leak\./ 13 | ] 14 | 15 | Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each { |file| require file } 16 | 17 | RSpec.configure do |config| 18 | config.include Capybara::DSL 19 | config.include Docker::DSL 20 | config.include WaitingHelper 21 | 22 | # set the default timeout to 10 minutes. 23 | timeout = 600 24 | 25 | # rspec-expectations config goes here. You can use an alternate 26 | # assertion/expectation library such as wrong or the stdlib/minitest 27 | # assertions if you prefer. 28 | 29 | config.expect_with :rspec do |expectations| 30 | # This option will default to `true` in RSpec 4. It makes the `description` 31 | # and `failure_message` of custom matchers include text for helper methods 32 | # defined using `chain`, e.g.: 33 | # be_bigger_than(2).and_smaller_than(4).description 34 | # # => "be bigger than 2 and smaller than 4" 35 | # ...rather than: 36 | # # => "be bigger than 2" 37 | expectations.include_chain_clauses_in_custom_matcher_descriptions = true 38 | 39 | # Configure the Rspec to only accept the new syntax on new projects, to 40 | # avoid having the 2 syntax all over the place. 41 | expectations.syntax = :expect 42 | end 43 | 44 | # rspec-mocks config goes here. You can use an alternate test double 45 | # library (such as bogus or mocha) by changing the `mock_with` option here. 46 | config.mock_with :rspec do |mocks| 47 | # Prevents you from mocking or stubbing a method that does not exist on 48 | # a real object. This is generally recommended, and will default to 49 | # `true` in RSpec 4. 50 | mocks.verify_partial_doubles = true 51 | end 52 | 53 | Excon.defaults[:write_timeout] = timeout 54 | Excon.defaults[:read_timeout] = timeout 55 | 56 | Capybara.configure do |conf| 57 | conf.register_driver :poltergeist_debug do |app| 58 | Capybara::Poltergeist::Driver.new app, timeout: timeout, 59 | # we should't care about javascript errors since we did not make any 60 | # implementation, but only deliver the software packages as best 61 | # effort and this is more an Atlassian problem. 62 | js_errors: false, 63 | phantomjs_logger: Capybara::Poltergeist::Suppressor.new 64 | end 65 | 66 | # Since we're connecting to a running Docker container, Capybara should 67 | # not startup a Rails server. 68 | conf.run_server = false 69 | conf.default_driver = :poltergeist_debug 70 | conf.default_max_wait_time = timeout 71 | 72 | # conf.ignore_hidden_elements = false 73 | # conf.visible_text_only = false 74 | end 75 | 76 | Docker::DSL.configure do |conf| 77 | conf.timeout = timeout 78 | end 79 | end 80 | -------------------------------------------------------------------------------- /spec/support/docker_dsl.rb: -------------------------------------------------------------------------------- 1 | module Docker 2 | module DSL 3 | extend RSpec::Matchers::DSL 4 | 5 | class << self 6 | attr_accessor :timeout 7 | 8 | def configure 9 | yield self 10 | end 11 | end 12 | 13 | Docker::DSL.configure do |config| 14 | config.timeout = nil 15 | end 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /spec/support/helpers/docker_helper.rb: -------------------------------------------------------------------------------- 1 | require 'docker' 2 | require 'uri' 3 | require 'rspec' 4 | require 'rspec/expectations' 5 | 6 | module Docker 7 | class Container 8 | def mapped_port(port) 9 | port_string = port.first.reverse.join '/' 10 | json['NetworkSettings']['Ports'][port_string].first['HostPort'] 11 | end 12 | 13 | def host 14 | json['NetworkSettings']['IPAddress'] 15 | end 16 | 17 | def setup_capybara_url(port, path = '') 18 | docker_url = URI.parse Docker.url 19 | docker_url.host = 'localhost' || Docker.info['Name'] 20 | docker_url.scheme = 'http' 21 | docker_url.path = path 22 | docker_url.port = mapped_port port 23 | Capybara.app_host = docker_url.to_s 24 | end 25 | 26 | def kill_and_wait(options = {}) 27 | options = { timeout: Docker::DSL.timeout }.merge(options) 28 | kill options 29 | wait options[:timeout] 30 | end 31 | 32 | def wait_for_output(regex) 33 | thread = Thread.new do 34 | Timeout.timeout Docker::DSL.timeout do 35 | Thread.handle_interrupt(TimeoutError => :on_blocking) do 36 | streaming_logs stdout: true, stderr: true, follow: true do |_, chunk| 37 | if chunk =~ regex 38 | Thread.current[:chunk] = chunk 39 | Thread.exit 40 | end 41 | end 42 | end 43 | end 44 | end 45 | thread.join 46 | thread[:chunk].to_s.match(regex).to_a.drop 1 47 | end 48 | end 49 | end 50 | -------------------------------------------------------------------------------- /spec/support/matchers/console_output_matcher.rb: -------------------------------------------------------------------------------- 1 | require 'docker' 2 | require 'rspec' 3 | require 'rspec/expectations' 4 | 5 | module Docker 6 | module DSL 7 | extend RSpec::Matchers::DSL 8 | 9 | class ConsoleOutputMatcher < RSpec::Matchers::BuiltIn::BaseMatcher 10 | def initialize(expected, options = {}) 11 | @expected = expected 12 | @options = options 13 | end 14 | 15 | def matches?(actual) 16 | @actual = [] 17 | exception_filter = @options[:filter] 18 | actual.streaming_logs stdout: true, stderr: true, tail: 'all' do |_, chunk| 19 | @actual << chunk if (chunk =~ @expected) && (chunk !~ exception_filter) 20 | end 21 | !@actual.empty? 22 | end 23 | 24 | def description 25 | "match #{@expected.inspect} in console output" 26 | end 27 | end 28 | 29 | def contain_console_output(regex, options = {}) 30 | ConsoleOutputMatcher.new regex, options 31 | end 32 | end 33 | end 34 | -------------------------------------------------------------------------------- /spec/support/matchers/exposed_ports_matcher.rb: -------------------------------------------------------------------------------- 1 | require 'docker' 2 | require 'rspec' 3 | require 'rspec/expectations' 4 | 5 | module Docker 6 | module DSL 7 | extend RSpec::Matchers::DSL 8 | include RSpec::Matchers 9 | 10 | class ExposePort < RSpec::Matchers::BuiltIn::BaseMatcher 11 | def initialize(expected) 12 | @expected = expected.first.reverse.join '/' 13 | end 14 | 15 | def matches?(actual) 16 | @actual = actual.json['Config']['ExposedPorts'] 17 | @actual.key? @expected 18 | end 19 | 20 | def failure_message 21 | "expected image to expose #{description}" 22 | end 23 | 24 | def failure_message_when_negated 25 | "expected image not to expose #{description}" 26 | end 27 | end 28 | 29 | def have_exposed_port(ports) 30 | ExposePort.new ports 31 | end 32 | end 33 | end 34 | -------------------------------------------------------------------------------- /spec/support/matchers/file_content_matcher.rb: -------------------------------------------------------------------------------- 1 | require 'stringio' 2 | require 'docker' 3 | require 'rspec' 4 | require 'rspec/expectations' 5 | require 'archive/tar/minitar' 6 | 7 | module Docker 8 | module DSL 9 | extend RSpec::Matchers::DSL 10 | 11 | class FileContentMatcher < RSpec::Matchers::BuiltIn::BaseMatcher 12 | def initialize(file, expected, options = {}) 13 | @file = file 14 | @expected = expected 15 | @options = options 16 | end 17 | 18 | def matches?(actual) 19 | @actual = [] 20 | exception_filter = @options[:filter] 21 | read_lines_from_files actual do |file, chunk| 22 | @actual << "[#{file}] #{chunk}" if (chunk =~ @expected) && (chunk !~ exception_filter) 23 | end 24 | !@actual.empty? 25 | end 26 | 27 | def description 28 | "match #{@expected.inspect} in file content" 29 | end 30 | 31 | private 32 | 33 | def read_lines_from_files(actual) 34 | stringio = StringIO.new 35 | actual.copy(@file) { |chunk| stringio.write chunk } 36 | stringio.rewind 37 | input = Archive::Tar::Minitar::Input.new(stringio) 38 | input.each do |entry| 39 | (String entry.read).each_line do |chunk| 40 | yield entry.name, chunk if entry.file? 41 | end 42 | end 43 | end 44 | end 45 | 46 | def have_file_contain(file, regex, options = {}) 47 | FileContentMatcher.new file, regex, options 48 | end 49 | end 50 | end 51 | -------------------------------------------------------------------------------- /spec/support/matchers/mapped_ports_matcher.rb: -------------------------------------------------------------------------------- 1 | require 'docker' 2 | require 'rspec' 3 | require 'rspec/expectations' 4 | 5 | module Docker 6 | module DSL 7 | extend RSpec::Matchers::DSL 8 | 9 | class MapPort < RSpec::Matchers::BuiltIn::BaseMatcher 10 | def initialize(expected) 11 | @expected = (expected.first.reverse.join '/') 12 | end 13 | 14 | def matches?(actual) 15 | @actual = actual.json['NetworkSettings']['Ports'].select { |_, v| !v.nil? } 16 | @actual.key? @expected 17 | end 18 | 19 | def failure_message 20 | "expected container to map #{description}" 21 | end 22 | 23 | def failure_message_when_negated 24 | "expected container not to map #{description}" 25 | end 26 | end 27 | 28 | def have_mapped_ports(port) 29 | MapPort.new port 30 | end 31 | end 32 | end 33 | -------------------------------------------------------------------------------- /spec/support/matchers/volume_matcher.rb: -------------------------------------------------------------------------------- 1 | require 'docker' 2 | require 'rspec' 3 | require 'rspec/expectations' 4 | 5 | module Docker 6 | module DSL 7 | extend RSpec::Matchers::DSL 8 | 9 | class VolumesMatcher < RSpec::Matchers::BuiltIn::BaseMatcher 10 | def initialize(path) 11 | @expected = path 12 | end 13 | 14 | def matches?(actual) 15 | @actual = actual.json['Config']['Volumes'] 16 | @actual.key? @expected 17 | end 18 | 19 | def description 20 | "have volume \"#{@expected}\"" 21 | end 22 | 23 | def failure_message 24 | "expected to have volume \"#{@expected}\" defined" 25 | end 26 | 27 | def failure_message_when_negated 28 | "expected not to have volume \"#{@expected}\" defined" 29 | end 30 | end 31 | 32 | def have_volume(path) 33 | VolumesMatcher.new path 34 | end 35 | end 36 | end 37 | -------------------------------------------------------------------------------- /spec/support/matchers/wait_for_console_matcher.rb: -------------------------------------------------------------------------------- 1 | require 'docker' 2 | require 'timeout' 3 | require 'rspec' 4 | require 'rspec/expectations' 5 | 6 | module Docker 7 | module DSL 8 | extend RSpec::Matchers::DSL 9 | 10 | class WaitConsoleMatcher < RSpec::Matchers::BuiltIn::BaseMatcher 11 | def initialize(expected, options = {}) 12 | @expected = expected 13 | @options = { timeout: Docker::DSL.timeout }.merge(options) 14 | end 15 | 16 | def matches?(actual) 17 | @value = actual.wait_for_output @expected 18 | end 19 | 20 | def description 21 | "wait for match #{@expected.inspect} in console output #{@value}" 22 | end 23 | end 24 | 25 | def wait_until_output_matches(regex, options = {}) 26 | WaitConsoleMatcher.new regex, options 27 | end 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /spec/support/matchers/workdir_matcher.rb: -------------------------------------------------------------------------------- 1 | require 'docker' 2 | require 'rspec' 3 | require 'rspec/expectations' 4 | 5 | module Docker 6 | module DSL 7 | extend RSpec::Matchers::DSL 8 | 9 | class WorkingDirectory < RSpec::Matchers::BuiltIn::Eq 10 | alias parent_matches? matches? 11 | 12 | def matches?(actual) 13 | parent_matches? actual.json['Config']['WorkingDir'] 14 | end 15 | 16 | def description 17 | "define working directory \"#{@expected}\"" 18 | end 19 | end 20 | 21 | def have_working_directory(path) 22 | WorkingDirectory.new path 23 | end 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /spec/support/shared_examples/a_buildable_docker_image_shared_context.rb: -------------------------------------------------------------------------------- 1 | shared_examples 'a buildable Docker image' do |path, options = {}| 2 | before :all do 3 | image = Docker::Image.build_from_dir(path) 4 | container_options = { Image: image.id }.merge options 5 | @container = Docker::Container.create container_options 6 | @container.start! PublishAllPorts: true 7 | @container.setup_capybara_url tcp: 8085 8 | # Apply hack for Travis 9 | if ENV['TRAVIS'] 10 | Thread.new do 11 | @container.streaming_logs stdout: true, stderr: true, follow: true do |stream, chunk| 12 | puts "[#{stream}] #{chunk}" 13 | end 14 | end 15 | end 16 | end 17 | 18 | describe 'when starting a Bamboo container' do 19 | subject { @container } 20 | 21 | it { is_expected.to_not be_nil } 22 | it { is_expected.to wait_until_output_matches REGEX_STARTUP } 23 | end 24 | 25 | after :all do 26 | if ENV['CIRCLECI'] 27 | @container.kill signal: 'SIGKILL' unless @container.nil? 28 | else 29 | @container.kill signal: 'SIGKILL' unless @container.nil? 30 | @container.remove force: true, v: true unless @container.nil? 31 | end 32 | end 33 | end 34 | -------------------------------------------------------------------------------- /spec/support/shared_examples/bamboo_shared_example.rb: -------------------------------------------------------------------------------- 1 | shared_examples 'an acceptable Atlassian Bamboo instance' do |database_examples| 2 | describe 'Going through the setup process' do 3 | before :all do 4 | until current_path =~ %r{/setup/setupLicense.action} 5 | visit '/' 6 | sleep 1 7 | end 8 | end 9 | 10 | subject { page } 11 | 12 | context 'when visiting the root page' do 13 | it { is_expected.to have_current_path %r{/setup/setupLicense.action} } 14 | it { is_expected.to have_css 'form#validateLicense' } 15 | it { is_expected.to have_field 'licenseString' } 16 | it { is_expected.to have_button 'Custom installation' } 17 | end 18 | 19 | context 'when processing license setup' do 20 | before :all do 21 | within 'form#validateLicense' do 22 | fill_in 'licenseString', with: 'AAACLg0ODAoPeNqNVEtv4jAQvudXRNpbpUSEx6FIOQBxW3ZZiCB0V1WllXEG8DbYke3A8u/XdUgVQyg9ZvLN+HuM/e1BUHdGlNvuuEHQ73X73Y4bR4nbbgU9ZwFiD2IchcPH+8T7vXzuej9eXp68YSv45UwoASYhOeYwxTsIE7RIxtNHhwh+SP3a33D0XnntuxHsIeM5CIdwtvYxUXQPoRIF6KaC0FUGVlEB3v0hOAOWYiH9abFbgZith3i34nwOO65gsAGmZBhUbNC/nIpjhBWEcefJWelzqIDPWz/OtjmXRYv2XyqwnwueFkT57x8e4cLmbCD1QnX0UoKQoRc4EUgiaK4oZ2ECUrlZeay75sLNs2JDmZtWR8oPCfWZGwHAtjzXgIo0SqmZiKYJmsfz8QI5aI+zApuq6fqJKVPAMCPnNpk4LPW6kBWgkZb+kQAzzzS2g6Dnte69Tqvsr4SOskIqEFOeggz1v4zrHbr0yLJR8rU64FpQpVtBy1mZxM4CnHC9Faf8tKMnTF1AiXORFixyQaWto3RZ+ncWLXtMg6EnKZZRpmQNb2R8tnJXFulCfXmXLry7TrHBWn2HNVyH8WYxj9AzmsxiNL/R88Xg6rA1lVs4QpO5titxhplJcCY2mFFZLutAZVhKipm15/VhJx36YVqyN8YP7IaGC1+lwnJ7Q5pJpNmxk5hP3qovutY8Pi4E2WIJ59esnr1p+T6eD67teBVCHf+ga+ho4/4D9YItZDAsAhQ5qQ6pASJ+SA7YG9zthbLxRoBBEwIURQr5Zy1B8PonepyLz3UhL7kMVEs=X02q6' 23 | click_button 'Custom installation' 24 | end 25 | end 26 | 27 | it { is_expected.to have_current_path %r{/setup/setupGeneralConfiguration.action} } 28 | it { is_expected.to have_css 'form#setupGeneralConfiguration' } 29 | it { is_expected.to have_css 'input#setupGeneralConfiguration_save' } 30 | end 31 | 32 | context 'when processing general setup' do 33 | before :all do 34 | within 'form#setupGeneralConfiguration' do 35 | find('input#setupGeneralConfiguration_save').trigger('click') 36 | end 37 | end 38 | 39 | it { is_expected.to have_current_path %r{/setup/setupDatabase.action} } 40 | it { is_expected.to have_css 'form#chooseDatabaseType' } 41 | it { is_expected.to have_css 'input#chooseDatabaseType_save' } 42 | it { is_expected.to have_button 'Continue' } 43 | end 44 | 45 | context 'when processing database setup' do 46 | include_examples database_examples 47 | end 48 | 49 | context 'when selecting data to import' do 50 | before :all do 51 | within 'form#performImportData' do 52 | choose 'performImportData_dataOptionclean' 53 | find('input#performImportData_save').trigger('click') 54 | end 55 | end 56 | 57 | it { is_expected.to have_current_path %r{/setup/setupAdminUser.action} } 58 | it { is_expected.to have_field 'fullName' } 59 | it { is_expected.to have_field 'email' } 60 | it { is_expected.to have_field 'username' } 61 | it { is_expected.to have_field 'password' } 62 | it { is_expected.to have_field 'confirmPassword' } 63 | it { is_expected.to have_button 'Finish' } 64 | it { is_expected.to have_css 'form#performSetupAdminUser' } 65 | it { is_expected.to have_css 'input#performSetupAdminUser_save' } 66 | end 67 | 68 | context 'when processing administrative account setup' do 69 | before :all do 70 | within 'form#performSetupAdminUser' do 71 | fill_in 'fullName', with: 'Continuous Integration Administrator' 72 | fill_in 'email', with: 'jira@circleci.com' 73 | fill_in 'username', with: 'admin' 74 | fill_in 'password', with: 'admin' 75 | fill_in 'confirmPassword', with: 'admin' 76 | find('input#performSetupAdminUser_save').trigger('click') 77 | end 78 | end 79 | 80 | it { is_expected.to have_current_path %r{/setup/finishsetup.action} } 81 | end 82 | 83 | context 'when finishing the setup' do 84 | it { is_expected.to have_current_path %r{/start.action} } unless ENV['CIRCLECI'] 85 | 86 | # The acceptance testing comes to an end here since we got to the 87 | # Bamboo without any trouble through the setup. 88 | end 89 | end 90 | 91 | describe 'Stopping the Atlassian Bamboo instance' do 92 | before(:all) { @container.kill_and_wait signal: 'SIGTERM' } 93 | 94 | subject { @container } 95 | 96 | it 'should shut down successful' do 97 | # give the container up to 5 minutes to successfully shutdown 98 | # exit code: 128+n Fatal error signal "n", ie. 143 = fatal error signal 99 | # SIGTERM. 100 | # 101 | # The following state check has been left out 'ExitCode' => 143 to 102 | # suppor CircleCI as CI builder. For some reason whether you send SIGTERM 103 | # or SIGKILL, the exit code is always 0, perhaps it's the container 104 | # driver 105 | # is_expected.to include_state 'Running' => false 106 | end 107 | 108 | include_examples 'a clean console' unless ENV['CIRCLECI'] 109 | include_examples 'a clean logfile', '/var/atlassian/bamboo/logs/atlassian-bamboo.log' unless ENV['CIRCLECI'] 110 | include_examples 'a clean logfile', '/var/atlassian/bamboo/logs/emergency-atlassian-bamboo.log' unless ENV['CIRCLECI'] 111 | end 112 | end 113 | -------------------------------------------------------------------------------- /spec/support/shared_examples/clean_console_shared_example.rb: -------------------------------------------------------------------------------- 1 | shared_examples 'a clean console' do 2 | context 'validating console output' do 3 | it { is_expected.to_not contain_console_output REGEX_SEVERE, filter: REGEX_FILTER } 4 | # it { is_expected.to_not contain_console_output REGEX_ERROR, filter: REGEX_FILTER } 5 | # There happens a lot of warnings by default so disable this check for now 6 | # it { is_expected.to_not contain_console_output REGEX_WARN, filter: REGEX_FILTER } 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /spec/support/shared_examples/clean_logfile_shared_example.rb: -------------------------------------------------------------------------------- 1 | shared_examples 'a clean logfile' do |log_file| 2 | context "validating logfile \"#{log_file}\"" do 3 | it { is_expected.to_not have_file_contain log_file, REGEX_SEVERE, filter: REGEX_FILTER } unless ENV['CIRCLECI'] 4 | # it { is_expected.to_not have_file_contain log_file, REGEX_ERROR, filter: REGEX_FILTER } unless ENV['CIRCLECI'] 5 | # There happens a lot of warnings by default so disable this check for now 6 | # it { is_expected.to_not have_file_contain log_file, REGEX_WARN, filter: REGEX_FILTER } 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /spec/support/shared_examples/using_a_mysql_database_shared_example.rb: -------------------------------------------------------------------------------- 1 | shared_examples 'using a MySQL database' do 2 | describe 'choosing MySQL as external database' do 3 | before :all do 4 | choose 'chooseDatabaseType_dbChoicestandardDb' 5 | select 'MySQL', from: 'selectedDatabase' 6 | find('input#chooseDatabaseType_save').trigger('click') 7 | end 8 | 9 | it { is_expected.to have_current_path %r{/setupDatabaseConnection.action} } 10 | it { is_expected.to have_css 'form#performSetupDatabaseConnection' } 11 | it { is_expected.to have_css 'input#performSetupDatabaseConnection_save' } 12 | it { is_expected.to have_field 'dbConfigInfo.databaseUrl' } 13 | it { is_expected.to have_field 'dbConfigInfo.userName' } 14 | it { is_expected.to have_field 'dbConfigInfo.password' } 15 | it { is_expected.to have_button 'Continue' } 16 | end 17 | 18 | describe 'setting up JDBC Configuration' do 19 | before :all do 20 | within 'form#performSetupDatabaseConnection' do 21 | fill_in 'dbConfigInfo.databaseUrl', with: "jdbc:mysql://#{@container_db.host}/bamboodb?autoReconnect=true&useUnicode=true&characterEncoding=utf8" 22 | fill_in 'dbConfigInfo.userName', with: 'root' 23 | fill_in 'dbConfigInfo.password', with: 'mysecretpassword' 24 | find('input#performSetupDatabaseConnection_save').trigger('click') 25 | end 26 | end 27 | 28 | it { is_expected.to have_current_path %r{/setup/setupSelectImport.action} } 29 | it { is_expected.to have_css 'form#performImportData' } 30 | it { is_expected.to have_css 'input#performImportData_save' } 31 | it { is_expected.to have_button 'Continue' } 32 | it { is_expected.to have_selector :radio_button, 'performImportData_dataOptionclean' } 33 | end 34 | end 35 | -------------------------------------------------------------------------------- /spec/support/shared_examples/using_a_postgresql_database_shared_example.rb: -------------------------------------------------------------------------------- 1 | shared_examples 'using a PostgreSQL database' do 2 | describe 'choosing PostgreSQL as external database' do 3 | before :all do 4 | choose 'chooseDatabaseType_dbChoicestandardDb' 5 | select 'PostgreSQL', from: 'selectedDatabase' 6 | find('input#chooseDatabaseType_save').trigger('click') 7 | end 8 | 9 | it { is_expected.to have_current_path %r{/setupDatabaseConnection.action} } 10 | it { is_expected.to have_css 'form#performSetupDatabaseConnection' } 11 | it { is_expected.to have_css 'input#performSetupDatabaseConnection_save' } 12 | it { is_expected.to have_field 'dbConfigInfo.databaseUrl' } 13 | it { is_expected.to have_field 'dbConfigInfo.userName' } 14 | it { is_expected.to have_field 'dbConfigInfo.password' } 15 | it { is_expected.to have_button 'Continue' } 16 | end 17 | 18 | describe 'setting up JDBC Configuration' do 19 | before :all do 20 | within 'form#performSetupDatabaseConnection' do 21 | fill_in 'dbConfigInfo.databaseUrl', with: "jdbc:postgresql://#{@container_db.host}:5432/bamboodb" 22 | # puts current_url 23 | # puts "jdbc:postgresql://#{@container_db.host}:5432/bamboodb" 24 | # require 'pry' 25 | # binding.pry 26 | fill_in 'dbConfigInfo.userName', with: 'postgres' 27 | fill_in 'dbConfigInfo.password', with: 'mysecretpassword' 28 | find('input#performSetupDatabaseConnection_save').trigger('click') 29 | end 30 | end 31 | 32 | it { is_expected.to have_current_path %r{/setup/setupSelectImport.action} } 33 | it { is_expected.to have_css 'form#performImportData' } 34 | it { is_expected.to have_css 'input#performImportData_save' } 35 | it { is_expected.to have_button 'Continue' } 36 | it { is_expected.to have_selector :radio_button, 'performImportData_dataOptionclean' } 37 | end 38 | end 39 | -------------------------------------------------------------------------------- /spec/support/shared_examples/using_an_embedded_database_shared_example.rb: -------------------------------------------------------------------------------- 1 | shared_examples 'using an embedded database' do 2 | before :all do 3 | choose 'chooseDatabaseType_dbChoiceembeddedDb' 4 | find('input#chooseDatabaseType_save').trigger('click') 5 | end 6 | 7 | it { is_expected.to have_current_path %r{/setup/setupEmbeddedDatabase.action} } 8 | 9 | it { is_expected.to have_current_path %r{/setup/setupSelectImport.action} } 10 | it { is_expected.to have_css 'form#performImportData' } 11 | it { is_expected.to have_css 'input#performImportData_save' } 12 | it { is_expected.to have_selector :radio_button, 'performImportData_dataOptionclean' } 13 | it { is_expected.to have_button 'Continue' } 14 | end 15 | -------------------------------------------------------------------------------- /spec/support/waiting_helper.rb: -------------------------------------------------------------------------------- 1 | module WaitingHelper 2 | def wait_for_ajax 3 | Timeout.timeout(Capybara.default_max_wait_time) do 4 | loop until finished_all_ajax_requests? 5 | end 6 | end 7 | 8 | def wait_for_document 9 | Timeout.timeout(Capybara.default_max_wait_time) do 10 | loop until document_ready? 11 | end 12 | end 13 | 14 | def wait_for_page 15 | Timeout.timeout(Capybara.default_max_wait_time) do 16 | loop until finished_all_ajax_requests? && document_ready? 17 | end 18 | end 19 | 20 | def wait_for_location_change 21 | original = current_url 22 | Timeout.timeout(Capybara.default_max_wait_time) do 23 | loop until location_changed? original 24 | end 25 | end 26 | 27 | private 28 | 29 | def finished_all_ajax_requests? 30 | page.evaluate_script('AJS.$.active').zero? 31 | end 32 | 33 | def document_ready? 34 | page.evaluate_script('document.readyState === "complete"') 35 | end 36 | 37 | def location_changed?(url) 38 | !url.eql? current_url 39 | end 40 | end 41 | --------------------------------------------------------------------------------