[
  {
    "path": ".editorconfig",
    "content": "root = true\n\n[*]\nend_of_line = lf\ninsert_final_newline = true\ncharset = utf-8\ntrim_trailing_whitespace = true\n\n[*.java]\nindent_style = space\nindent_size = 4\n"
  },
  {
    "path": ".gitignore",
    "content": "minimesosFile\n.minimesos/*\n*.class\n.gradle/\nbuild/\n\n# Vagrant working files\n.vagrant\n\n# Build system\n.gradle/\nbuild/\n\n# IDEA files\n*.i??\nout/\n.idea/\n\n# Maven\n/target\n\n# Mobile Tools for Java (J2ME)\n.mtj.tmp/\n\n# Package Files #\n*.war\n*.ear\n\n# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml\nhs_err_pid*\n\n# private docker registry images\n.registry\n"
  },
  {
    "path": ".pullapprove.yml",
    "content": "approve_by_comment: true\napprove_regex: ^(Approved|LGTM|:\\+1:)\nauthor_approval: ignored\nreject_regex: ^Rejected\nreset_on_push: false\nreviewers:\n  members:\n  - frankscholten\n  - mwl\n  - sadovnikov\n  - philwinder\n  - lguminski\n  - adam-sandor\n  name: default\n  required: 1\n"
  },
  {
    "path": ".travis.yml",
    "content": "language: java\n\njdk:\n  - oraclejdk8\n\nsudo: required\n\ninstall:\n# one liner installation of docker 1.9.1 below did not work (see https://github.com/moul/travis-docker/issues/38).\n#  - curl -sLo - http://j.mp/install-travis-docker | sh -xe\n# Therefore installing it through a script\n  - sudo sh -c 'echo \"deb https://apt.dockerproject.org/repo ubuntu-precise main\" > /etc/apt/sources.list.d/docker.list'\n  - sudo apt-key adv --keyserver hkp://p80.pool.sks-keyservers.net:80 --recv-keys 58118E89F3A912897C070ADBF76221572C52609D\n  - sudo apt-get update\n  - sudo apt-key update\n  - sudo apt-get -qqy install docker-engine=1.9.1-0~precise\n# Has to run this script with sudo because custom installation does not allow $USER to use docker and it's not possible to relogin\n  - sudo make deps\n\n# Has to run the build script with sudo because custom installation does not allow $USER to use docker and it's not possible to relogin\nscript: chmod +x travis.sh && sudo ./travis.sh\n\nnotifications:\n  email: true\n   # see details on https://docs.travis-ci.com/user/notifications\n  slack:\n    secure: RWUEmM8nef6hH9+AmVaBWVxcjUt5hVPdbw02x+iBdTqAxPC2wxq3Ya/vlWDwhyyXdUgMujfWTJxks3A15qHAzPH22/mVsmAoz8Duspj/C3x8dp/7IncnkbX5AI1fEJy+z+D8uL4J6ALM90y8kUm2QKoddOq1+xO65xZyzvXoxFJDZ9eIlSVsDv7q7qqkaHnWH8nW+DqtGFPlhu5K/luaw56gy7lChUX/KvAy+8fzaUFNPKdJTVu+GpdgJZrqKeQS8+gY00k0AaAS6fOHxTeAUmyC6eDTL1FgBueS5auBha321qU84sQTCQSTHxl0J8YSQzzrBEiGn506DMKFjZLQZWmR4DxxGSc8jd4sdbVXBoWEBQvNI8jZoAzagFnNig1NKPtRAXIuip28FJUhsvK3WOs1H/XsnkRxKZ52jRrDg0yYi48HsqIr7af6nSzAkAK5JEL58Yc1nYvALa0vXjVWuyuo8um0sFNvEDRE/eDi5o6iul0I4CPOM0j+6d8ymVuD6oJ8eeGjYSFVk7XgdCBp1Gcl8NHLgiVjnygcT0U07kszDV7q8ab0iAfjMoTJwFTjPGkwFWJnlD5dciliO7ncWORl//A3JOQqRh5kMp/96995Ia9G4pVnEkh6tQI6G84/qMU0blDrOtTWIO6NjDV4UiGAYtaixr8BGKQWji9K+eY=\n"
  },
  {
    "path": "LICENSE",
    "content": "                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"{}\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright {yyyy} {name of copyright owner}\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n\n"
  },
  {
    "path": "Makefile",
    "content": "default: build\n.PHONY: setup deps test build\n\nsetup:\n\tsudo route delete 172.17.0.0/16; sudo route -n add 172.17.0.0/16 $(shell docker-machine ip ${shell DOCKER_MACHINE_NAME})\n\ndeps:\n\tdocker pull containersol/mesos-agent:1.0.0-0.1.0\n\tdocker pull containersol/mesos-master:1.0.0-0.1.0\n\tdocker pull gliderlabs/registrator:v6\n\tdocker pull consul:0.7.1\n\tdocker pull xebia/mesos-dns:0.0.5\n\tdocker pull mesosphere/marathon:v1.3.5\n\tdocker pull jplock/zookeeper:3.4.6\n\tdocker pull containersol/alpine3.3-java8-jre:v1\n\tdocker pull tutum/hello-world:latest\n\nclean:\n\t./gradlew clean\n\t-docker rmi containersol/minimesos-cli:latest\n\nbuild:\n\t./gradlew build --info --stacktrace\n\nbuild-no-tests:\n\t./gradlew build --info --stacktrace -x test\n\ntest:\n\t./gradlew test --info --stacktrace\n\n"
  },
  {
    "path": "README.md",
    "content": "# minimesos [![Build Status](https://travis-ci.org/ContainerSolutions/minimesos.svg?branch=master)](https://travis-ci.org/ContainerSolutions/minimesos)\n\nThe experimentation and testing tool for Apache Mesos\n\nNOTE: NO LONGER MAINTAINED!\n\n<div>\n<img width=\"175\" src=\"https://github.com/ContainerSolutions/minimesos/raw/master/images/minimesos.png\">\n<ul>\n  <li><strong>Website</strong>: https://www.minimesos.org\n  <li><strong>Source</strong>: https://github.com/ContainerSolutions/minimesos\n  <li><strong>Mailing List</strong>: <a href=\"https://groups.google.com/d/forum/minimesos\">minimesos@googlegroups.com</a>\n  <li><strong>Slack</strong>: <a href=\"https://minimesos.slack.com\">minimesos.slack.com</a>\n  <li><strong>Docs</strong>: <a href=\"https://readthedocs.org/projects/minimesos\">readthedocs.org/projects/minimesos</a>\n  <li><strong>Twitter</strong>: <a href=\"https://twitter.com/minimesos\">@minimesos</a>\n</ul>\n<div style=\"clear: both;\"> \n\n## Videos\n\n#### MesosCon 2016 Denver - minimesos - The Experimentation and Testing tool for Apache Mesos by Frank Scholten [@frank_scholten](https://twitter.com/Frank_Scholten)\n\n[![minimesos - The Experimentation and Testing tool for Apache Mesos by Frank](https://github.com/ContainerSolutions/minimesos/raw/master/images/minimesos-talk.jpg)](https://www.youtube.com/watch?v=J14_H4T0JB0)\n\n#### Introduction to Minimesos by Viktor Sadovnikov [@sadovnikov](https://twitter.com/sadovnikov)\n\n[![Introduction to minimesos by Viktor](https://raw.githubusercontent.com/containersolutions/minimesos/master/docs/images/introduction-to-minimesos-screenshot.jpg)](https://www.youtube.com/watch?v=jVGyz8sCZSU)\n"
  },
  {
    "path": "bin/install",
    "content": "#!/bin/sh\n\n# This script is meant for quick & easy install via:\n# `sudo curl -sSL https://raw.githubusercontent.com/ContainerSolutions/minimesos/master/bin/install | bash`\n#\n# Uses the latest release, unless a version identifier is specified as a parameter of this script.\n\n{ # Prevent execution if the script is only partially downloaded\n\ncommand_exists() {\n    command -v \"$@\" > /dev/null 2>&1\n}\n\ninstall_version() {\n    curl -sSL $1 | sh -s $2\n}\n\nif ! command_exists curl; then\n    echo \"Please install curl to fetch the minimesos files\"\n    exit 1\nfi\n\nVERSION=$(echo $@ | xargs)\n\nif [ -z \"$VERSION\" ]; then\n    VERSION=$(curl -s https://api.github.com/repos/containersolutions/minimesos/releases/latest | grep \"tag_name\" | awk '{ print $2 }' | tr -d '\",')\n    if [ ! \"$VERSION\" ]; then\n\techo \"Cannot determine latest release of minimesos. Please check https://github.com/containersolutions/minimesos/releases\"\n\texit 1\n    fi\nfi\n\n# invoking versioned installation script\nSCRIPT_PATH=https://raw.githubusercontent.com/ContainerSolutions/minimesos/$VERSION\nhttpcode=$(curl -s -o /dev/null -I -w '%{http_code}' --max-time 10 --retry-delay 2 --retry 3 $SCRIPT_PATH/bin/install-version || echo \"404\" )\nif [ $httpcode -eq 200 ]; then\n    install_version $SCRIPT_PATH/bin/install-version $VERSION\nelse\n    echo \"Failed to pull installer off github.com (http err: ${httpcode}), please try again.\"\n    exit 1\nfi\n\nexit 0\n} # Prevent execution if the script is only partially downloaded\n\n"
  },
  {
    "path": "bin/install-version",
    "content": "#!/bin/sh\n\n# This script is meant to be invoked with VERSION given as a parameter.\n# Installs the given version of minimesos on the box\n\n{ # Prevent execution if the script is only partially downloaded\n\ncommand_exists() {\n    command -v \"$@\" > /dev/null 2>&1\n}\n\nif ! command_exists curl; then\n\techo \"Please install curl to fetch the minimesos files\"\n\texit 1\nfi\n\nif [ ! \"$#\" -eq 1 ]; then\n\techo \"Version is not given as parameter\"\n\texit 1\nfi\n\nINSTALL_LOCATION=$HOME/.minimesos/bin\nVERSION=$1\necho \"Installing version \" $VERSION\nmkdir -p $INSTALL_LOCATION\ncurl -sSL https://raw.githubusercontent.com/ContainerSolutions/minimesos/$VERSION/bin/minimesos > $INSTALL_LOCATION/minimesos\nchmod +x $INSTALL_LOCATION/minimesos\n\nif [ -f \"/usr/local/bin/minimesos\" ]; then\n    echo \"Found an old version of minimesos, please remove it:\" && echo\n    echo \"rm -f /usr/local/bin/minimesos\" && echo\nfi\n\necho \"minimesos is installed into ${INSTALL_LOCATION}/minimesos\"\necho \"Run the following command to add it to your executables path:\" && echo\necho \"export PATH=\\$PATH:$INSTALL_LOCATION\"\n\nexit 0\n\n} # Prevent execution if the script is only partially downloaded\n"
  },
  {
    "path": "bin/minimesos",
    "content": "#!/usr/bin/env bash\n\nset -e\n\nMINIMESOS_TAG=\"latest\"\nPARAMS=\"$@\"\nMINIMESOS_CLI_IMAGE=\"containersol/minimesos-cli\"\n\ncommand_exists() {\n    command -v \"$@\" > /dev/null 2>&1\n}\n\nDOCKER_VERSION=$(docker version --format \"{{.Server.Version}}\")\nSMALLEST_VERSION=$(printf \"%s\\n1.11.0\\n\" $DOCKER_VERSION | sort -t '.' -k 1,1 -k 2,2 -k 3,3 -k 4,4 -g | head -n 1)\n\nif ! command_exists docker || [ $SMALLEST_VERSION != \"1.11.0\" ]; then\n    echo \"Minimesos requires Docker 1.11.0 or higher\"\n    exit 1\nfi\n\nif [ \"$DOCKER_HOST\" != \"\" ] && [[ $DOCKER_HOST == tcp* ]]; then\n    DOCKER_HOST_IP=$(echo \"$DOCKER_HOST\" | grep -o '[0-9]\\+[.][0-9]\\+[.][0-9]\\+[.][0-9]\\+')\nelif command_exists docker-machine && [ \"$DOCKER_MACHINE_NAME\" != \"\" ]; then\n    DOCKER_HOST_IP=$(docker-machine ip ${DOCKER_MACHINE_NAME})\nelif [ $(uname) != \"Darwin\" ]; then\n    DOCKER_HOST_IP=$(ip addr show dev docker0 | grep inet | sed -r \"s/.*inet\\s([0-9\\.]+)\\/.*/\\1/\" | head -n 1)\nelse\n    DOCKER_HOST_IP=\"\"\nfi\n\npullImage() {\n    if [ \"$(docker images $1 | grep $2 2> /dev/null)\" = \"\" ]; then\n\techo \"Pulling $1:$2\"\n\tdocker pull \"$1:$2\"\n    fi\n}\n\nif [ \"$#\" -gt 0 -a \"$1\" = up ]; then\n    pullImage ${MINIMESOS_CLI_IMAGE} ${MINIMESOS_TAG}\nfi\n\nif [ $(uname) == \"Darwin\" ]; then\n  MINIMESOS_OS=\"Mac OS X\"\nelse\n  MINIMESOS_OS=\"Linux\"\nfi\n\nMINIMESOS_HOST_DIR=\"$(pwd)\"\nMINIMESOS_DIR=\"$(pwd)/.minimesos\"\nif [ ! -d \"${MINIMESOS_DIR}\" ]; then\n    mkdir -p \"${MINIMESOS_DIR}\"\n    echo \"# Created minimesos directory at ${MINIMESOS_DIR}.\"\nfi\n\n\nDOCKER_CONTAINER=$(\n    docker create --rm \\\n       -v \"${MINIMESOS_HOST_DIR}\":\"${MINIMESOS_HOST_DIR}\" \\\n       -v /var/run/docker.sock:/var/run/docker.sock \\\n       -v /sys/fs/cgroup:/sys/fs/cgroup \\\n       -i \\\n       --env DOCKER_HOST_IP=${DOCKER_HOST_IP} \\\n       --env MINIMESOS_OS=\"${MINIMESOS_OS}\" \\\n       --entrypoint java \\\n       ${MINIMESOS_CLI_IMAGE}:${MINIMESOS_TAG} \\\n       -Dminimesos.file=\"/minimesosFile\" \\\n       -Dminimesos.host.dir=\"${MINIMESOS_HOST_DIR}\" \\\n       -jar /usr/local/share/minimesos/minimesos-cli.jar ${PARAMS} \\\n)\n\ndocker cp ${MINIMESOS_HOST_DIR}/minimesosFile $DOCKER_CONTAINER:/minimesosFile\ndocker start -a $DOCKER_CONTAINER\n"
  },
  {
    "path": "build.gradle",
    "content": "buildscript {\n\n    repositories {\n        maven {\n            url \"http://dl.bintray.com/gesellix/gradle-plugins\"\n        }\n        mavenCentral()\n        mavenLocal()\n        jcenter()\n    }\n\n    dependencies {\n        classpath 'com.bmuschko:gradle-docker-plugin:3.0.2'\n        classpath \"de.gesellix:gradle-debian-plugin:16\"\n    }\n}\n\nplugins {\n    id 'idea'\n    id \"org.sonarqube\" version \"1.2\"\n    id 'net.researchgate.release' version '2.3.5'\n    id 'com.bmuschko.docker-remote-api' version '3.0.1'\n}\n\nsubprojects {\n\n    group = \"com.containersol.minimesos\"\n    version = rootProject.version.toString()\n\n    repositories {\n        mavenCentral()\n        mavenLocal()\n    }\n\n    apply plugin: 'java'\n    apply plugin: 'com.bmuschko.docker-remote-api'\n    apply plugin: 'maven'\n    apply plugin: 'jacoco'\n\n    sourceCompatibility = '1.8'\n    targetCompatibility = '1.8'\n\n    [compileJava, compileTestJava]*.options*.encoding = 'UTF-8'\n\n    compileJava {\n        options.compilerArgs << \"-Xlint:unchecked\" << \"-Xlint:deprecation\"\n    }\n\n    task showDeps(type: DependencyReportTask) {}\n\n    test {\n        testLogging {\n            showStandardStreams = true\n        }\n    }\n\n    jacocoTestReport {\n        reports {\n            xml.enabled false\n            csv.enabled true\n        }\n    }\n\n    configurations {\n        //workaround for build problem using docker4mac (https://github.com/bmuschko/gradle-docker-plugin/issues/235#issuecomment-239982250)\n        dockerJava {\n            resolutionStrategy {\n                force 'de.gesellix:unix-socket-factory:2016-04-06T22-21-19'\n            }\n        }\n    }\n\n    docker {\n        url         = 'unix:///var/run/docker.sock'\n        certPath    = null\n        if (System.getenv(\"DOCKER_HOST\")) {\n            url = System.getenv(\"DOCKER_HOST\").replace(\"tcp\", \"https\")\n            if (System.getenv(\"DOCKER_CERT_PATH\")) {\n                certPath = new File(System.getenv(\"DOCKER_CERT_PATH\").toString())\n            }\n        }\n    }\n}\n\next {\n    mesosVer    = \"1.0.0\"\n    repository  = 'containersol'\n}\n\nidea {\n    project {\n        languageLevel = '1.8'\n        vcs = 'Git'\n    }\n}\n\nsonarqube {\n    properties {\n        property \"sonar.sourceEncoding\", \"UTF-8\"\n        property \"sonar.jacoco.reportPath\", \"${buildDir}/jacoco/test.exec\"\n    }\n}\n\ntask checkDockerAuthentication << {\n    // command below return 1 when user is not logged in to Docker Hub\n    def command = \"docker info | grep \\\"Username:\\\"; rc=\\$?; if [[ \\$rc != 0 ]]; then exit 1; fi\"\n    // executing via bash, so we can make use of its interpreter of the parameters. Otherwise pipes do not work\n    exec {\n        commandLine \"bash\", \"-c\", command\n    }\n}\n\ntask setVersionInBashScript << {\n    if (new File(project.rootDir, \"bin/minimesos\").text.contains('MINIMESOS_TAG=\"latest\"')) {\n        // set explicit version in bash script\n        ant.replace(file: \"bin/minimesos\", token: 'MINIMESOS_TAG=\"latest\"', value: \"MINIMESOS_TAG=\\\"${project.version}\\\"\")\n        // commit modified files\n        exec {\n            workingDir \"${project.rootDir}\"\n            commandLine \"git\", \"commit\", \"-a\", \"-m\", \"[Release ${project.version}] pre-tag commit\"\n        }\n        // push update to repository\n        exec {\n            workingDir \"${project.rootDir}\"\n            commandLine \"git\", \"push\"\n        }\n    }\n}\n\ntask resetVersionInBashScript << {\n    // have to use regular expression because the project version has changed and the previous is not kept\n    ant.replaceregexp(file: \"bin/minimesos\", match: \"MINIMESOS_TAG=\\\".*?\\\"\", replace: \"MINIMESOS_TAG=\\\"latest\\\"\")\n}\n\ntask releaseMinimesosCliImage << {\n\n    ['dockerHubUsername', 'dockerHubPassword', 'dockerHubEmail'].each {\n        assert project.hasProperty(it): 'Undefined \"' + it + '\" property'\n    }\n\n    docker {\n        registryCredentials {\n            username = project.property('dockerHubUsername')\n            password = project.property('dockerHubPassword')\n            email = project.property('dockerHubEmail')\n        }\n    }\n\n    def minimesosCliImage = \"containersol/minimesos-cli\"\n\n    def command = \"docker tag \\$(docker images ${minimesosCliImage} | grep 'latest' | awk '{print \\$3}') ${minimesosCliImage}:${project.version}\"\n    // executing via bash, so we can make use of its interpreter of the parameters. Otherwise pipes do not work\n    exec {\n        commandLine \"bash\", \"-c\", command\n    }\n\n    println \"Pushing ${minimesosCliImage}:${project.version} docker image to Docker Hub...\"\n    exec {\n        commandLine \"docker\", \"push\", \"${minimesosCliImage}:${project.version}\"\n    }\n\n    println \"Pushing ${minimesosCliImage}:latest docker image to Docker Hub...\"\n    exec {\n        commandLine \"docker\", \"push\", \"${minimesosCliImage}:latest\"\n    }\n}\n\ntask createGitHubRelease << {\n    // use GitHub API\n    exec {\n        commandLine \"curl\", \"-H\", \"Content-Type: application/json\", \"-H\", \"Authorization: token ${githubAuthToken}\", \"-XPOST\", \"-d\", \"{\\\"tag_name\\\":\\\"${project.version}\\\"}\", \"https://api.github.com/repos/ContainerSolutions/minimesos/releases\"\n    }\n}\n\ntask updateReleaseDocs << {\n    // update online documentation\n    exec {\n        commandLine \"curl\", \"-XPOST\", \"https://readthedocs.org/build/minimesos\"\n    }\n    // trigger update of minimesos.org\n    exec {\n        commandLine \"curl\", \"-H\", \"x-api-key: ${awsMinimesosOrgApiKey}\", \"https://kilt52ydsk.execute-api.us-east-1.amazonaws.com/prod/RunHugoGit\"\n    }\n}\n\n// fail the build early if user is not logged in to docker\ncheckUpdateNeeded.dependsOn checkDockerAuthentication\n// set explicit version in minimesos bash script\npreTagCommit.dependsOn setVersionInBashScript\n// project.version changes after execution of updateVersion. Time to tag and to push container\nupdateVersion.dependsOn releaseMinimesosCliImage, createGitHubRelease\n// project.version changes after execution of updateVersion\ncommitNewVersion.dependsOn resetVersionInBashScript, updateReleaseDocs\n\n// working around https://github.com/researchgate/gradle-release/issues/144\nrelease {\n    buildTasks = ['releaseBuild']\n}\n\ntask releaseBuild {\n    dependsOn(\n        'minimesos:clean',\n        'minimesos:build',\n        'cli:build'\n    )\n}\n"
  },
  {
    "path": "cli/Dockerfile",
    "content": "FROM debian:jessie-backports\nFROM openjdk:8-jre-alpine\nMAINTAINER Container Solutions BV <info@container-solutions.com>\n\nRUN apk add --update curl libstdc++&& \\\n        rm -rf /var/cache/apk/*\n\nRUN curl https://get.docker.com/builds/Linux/x86_64/docker-1.12.0.tgz -o docker-1.12.0.tgz && \\\n    tar xzf docker-1.12.0.tgz && \\\n    mv docker/docker /usr/bin/docker && \\\n    chmod +x /usr/bin/docker\n\nADD minimesos-cli.jar /usr/local/share/minimesos/minimesos-cli.jar\n"
  },
  {
    "path": "cli/build.gradle",
    "content": "apply plugin: 'application'\n\nimport com.bmuschko.gradle.docker.tasks.image.DockerBuildImage\nimport com.bmuschko.gradle.docker.tasks.image.DockerPushImage\nimport com.bmuschko.gradle.docker.tasks.image.DockerTagImage\n\ndependencies {\n    compile 'com.beust:jcommander:1.48'\n    compile 'org.slf4j:slf4j-api:1.7.12'\n\n    compile project(':minimesos')\n\n    testCompile 'junit:junit:4.11'\n    testCompile \"org.mockito:mockito-core:1.+\"\n    testCompile \"guru.nidi:jdepend:2.9.5\"\n}\n\nmainClassName = \"com.containersol.minimesos.main.Main\"\n\next {\n    imageName = repository + '/minimesos-cli'\n}\n\njar {\n    baseName = \"minimesos-cli\"\n    from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } }\n    manifest {\n        attributes(\n                'Main-Class': mainClassName,\n                'Implementation-Version': project.version\n        )\n    }\n    exclude 'META-INF/*.RSA', 'META-INF/*.SF', 'META-INF/*.DSA'\n}\n\nartifacts {\n    archives jar\n}\n\ntask copyFilesForDocker(type: Copy) {\n    dependsOn 'jar'\n    from \"build/libs/minimesos-cli-${project.version}.jar\"\n    into 'build/docker'\n    rename { String fileName ->\n        fileName.replace(\"-${project.version}\", \"\")\n    }\n}\n\ntask copyDockerfile(type: Copy) {\n    dependsOn 'copyFilesForDocker'\n    from \"Dockerfile\"\n    into 'build/docker'\n}\n\ntask buildDockerImage(type: DockerBuildImage, dependsOn: [copyDockerfile], description: 'build Docker image') {\n    inputDir = new File(\"${buildDir}/docker\")\n    setTag(project.imageName)\n}\n\nafterEvaluate { project ->\n    for (tag in ['snapshot', 'version']) {\n        String uppercasedTag = tag.capitalize()\n\n        task \"tagDockerImageWith$uppercasedTag\"(type: DockerTagImage, description: 'tag Docker image') {\n            setImageId(project.imageName)\n            setTag('version' == tag ? project.version : tag)\n            setRepository(project.imageName)\n            setForce(true)\n        }\n\n        task \"publishDockerImageWith$uppercasedTag\"(type: DockerPushImage, dependsOn: [\"tagDockerImageWith$uppercasedTag\"],\n                description: 'publish Docker image') {\n            setImageName(project.imageName)\n            setTag('version' == tag ? project.version : tag)\n        }\n    }\n}\n\nsourceSets {\n    integrationTest {\n        java {\n            compileClasspath += main.output + test.output\n            runtimeClasspath += main.output + test.output\n            srcDir file('src/integration-test/java')\n        }\n        resources.srcDir file('src/integration-test/resources')\n    }\n}\n\nconfigurations {\n    integrationTestCompile.extendsFrom mainCompile\n    integrationTestCompile.extendsFrom testCompile\n    integrationTestRuntime.extendsFrom mainRuntime\n    integrationTestRuntime.extendsFrom testRuntime\n}\n\ntask integrationTest(type: Test) {\n    testClassesDir = sourceSets.integrationTest.output.classesDir\n    classpath = sourceSets.integrationTest.runtimeClasspath\n    testLogging {\n        showStandardStreams = true\n    }\n}\n\nintegrationTest.dependsOn buildDockerImage\n\nproject.build.dependsOn buildDockerImage\n\nassemble.dependsOn jar\n"
  },
  {
    "path": "cli/src/integration-test/java/com/containersol/minimesos/main/CommandInitTest.java",
    "content": "package com.containersol.minimesos.main;\n\nimport com.containersol.minimesos.MinimesosException;\nimport com.containersol.minimesos.cluster.MesosCluster;\nimport com.containersol.minimesos.config.ClusterConfig;\nimport org.apache.commons.io.FileUtils;\nimport org.junit.Before;\nimport org.junit.Test;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.nio.file.Files;\nimport java.nio.file.Paths;\n\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertTrue;\n\npublic class CommandInitTest {\n\n    private CommandInit commandInit;\n\n    @Before\n    public void before() {\n        commandInit = new CommandInit();\n    }\n\n    @Test\n    public void testFileContent() throws IOException {\n        String fileContent = commandInit.getConfigFileContent();\n        assertTrue(\"agent section is not found\", fileContent.contains(\"agent {\"));\n        assertTrue(\"agent resources section is not found\", fileContent.contains(\"resources {\"));\n        assertTrue(\"zookeeper section is not found\", fileContent.contains(\"zookeeper {\"));\n        assertTrue(\"consul section is not found\", fileContent.contains(\"consul {\"));\n        assertTrue(\"registrator section is not found\", fileContent.contains(\"registrator {\"));\n        assertTrue(\"mesosdns section is not found\", fileContent.contains(\"mesosdns {\"));\n    }\n\n    @Test(expected = MinimesosException.class)\n    public void testExecute_existingMiniMesosFile() throws IOException {\n        String oldHostDir = System.getProperty(MesosCluster.MINIMESOS_HOST_DIR_PROPERTY);\n        File dir = File.createTempFile(\"mimimesos-test\", \"dir\");\n        assertTrue(\"Failed to delete temp file\", dir.delete());\n        assertTrue(\"Failed to create temp directory\", dir.mkdir());\n        System.setProperty(MesosCluster.MINIMESOS_HOST_DIR_PROPERTY, dir.getAbsolutePath());\n\n\n        File minimesosFile = new File(dir, ClusterConfig.DEFAULT_CONFIG_FILE);\n        Files.write(Paths.get(minimesosFile.getAbsolutePath()), \"minimesos { }\".getBytes());\n\n        try {\n            commandInit.execute();\n        } finally {\n            if (oldHostDir == null) {\n                System.getProperties().remove(MesosCluster.MINIMESOS_HOST_DIR_PROPERTY);\n            } else {\n                System.setProperty(MesosCluster.MINIMESOS_HOST_DIR_PROPERTY, oldHostDir);\n            }\n            FileUtils.forceDelete(dir);\n        }\n    }\n\n    @Test\n    public void testValidateParameters() {\n        assertTrue(commandInit.validateParameters());\n    }\n\n    @Test\n    public void testName() {\n        assertEquals(\"init\", commandInit.getName());\n    }\n\n}\n"
  },
  {
    "path": "cli/src/integration-test/java/com/containersol/minimesos/main/CommandLogsTest.java",
    "content": "package com.containersol.minimesos.main;\n\nimport com.containersol.minimesos.cluster.ClusterRepository;\nimport com.containersol.minimesos.cluster.MesosCluster;\nimport com.containersol.minimesos.cluster.MesosClusterFactory;\nimport com.containersol.minimesos.mesos.MesosAgentContainer;\nimport com.containersol.minimesos.mesos.MesosMasterContainer;\nimport com.containersol.minimesos.state.*;\nimport com.containersol.minimesos.util.Downloader;\nimport org.junit.Before;\nimport org.junit.Test;\n\nimport java.io.ByteArrayOutputStream;\nimport java.io.PrintStream;\nimport java.io.UnsupportedEncodingException;\nimport java.net.URI;\nimport java.net.URISyntaxException;\nimport java.util.*;\n\nimport static java.util.Collections.singletonList;\nimport static org.junit.Assert.assertEquals;\nimport static org.mockito.Matchers.any;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.when;\n\npublic class CommandLogsTest {\n\n    private final String slaveId = \"de09c926-7be6-47c6-ab9a-6d1a2b32b642-S0\";\n    private final String taskId = \"http-ports-static-assigned-to-31002.0f732069-a1f2-11e7-97e0-0242ac110006\";\n    private final String workDir = \"/var/lib/mesos/agent-3039938407\";\n    private final String frameworkId = \"de09c926-7be6-47c6-ab9a-6d1a2b32b642-0000\";\n    private final String runId = \"fd7f7182-5ee8-47fc-a1c4-6928aeb1f769\";\n    private final String agentServiceURL = \"http://172.17.0.7:5051\";\n\n    private final String PATH_FORMAT = \"%s/slaves/%s/frameworks/%s/executors/%s/runs/%s\";\n    private final String executorDirectory = String.format(PATH_FORMAT, workDir, slaveId, frameworkId, taskId, runId);\n\n    private final String STDOUT_URL = \"http://172.17.0.7:5051\" +\n        \"/files/download?path=\" +\n        \"%2Fvar%2Flib%2Fmesos\" +\n        \"%2Fagent-3039938407\" +\n        \"%2Fslaves%2Fde09c926-7be6-47c6-ab9a-6d1a2b32b642-S0\" +\n        \"%2Fframeworks%2Fde09c926-7be6-47c6-ab9a-6d1a2b32b642-0000\" +\n        \"%2Fexecutors%2Fhttp-ports-static-assigned-to-31002.0f732069-a1f2-11e7-97e0-0242ac110006\" +\n        \"%2Fruns%2Ffd7f7182-5ee8-47fc-a1c4-6928aeb1f769\" +\n        \"%2Fstdout\";\n\n    private final String STDERR_URL = \"http://172.17.0.7:5051\" +\n        \"/files/download?path=\" +\n        \"%2Fvar%2Flib%2Fmesos\" +\n        \"%2Fagent-3039938407\" +\n        \"%2Fslaves%2Fde09c926-7be6-47c6-ab9a-6d1a2b32b642-S0\" +\n        \"%2Fframeworks%2Fde09c926-7be6-47c6-ab9a-6d1a2b32b642-0000\" +\n        \"%2Fexecutors%2Fhttp-ports-static-assigned-to-31002.0f732069-a1f2-11e7-97e0-0242ac110006\" +\n        \"%2Fruns%2Ffd7f7182-5ee8-47fc-a1c4-6928aeb1f769\" +\n        \"%2Fstderr\";\n\n    private ByteArrayOutputStream outputStream;\n\n    private PrintStream ps;\n\n    private ClusterRepository repository;\n\n    private Downloader downloader;\n\n    private State masterState;\n\n    private State agentState;\n\n    @Before\n    public void initTest() throws URISyntaxException {\n        outputStream = new ByteArrayOutputStream();\n        ps = new PrintStream(outputStream, true);\n\n        masterState = generateMasterState();\n        agentState = generateAgentState();\n\n        MesosMasterContainer master = mock(MesosMasterContainer.class);\n        when(master.getState()).thenReturn(masterState);\n\n        MesosAgentContainer agent = mock(MesosAgentContainer.class);\n        when(agent.getState()).thenReturn(agentState);\n        when(agent.getServiceUrl()).thenReturn(new URI(agentServiceURL));\n\n        MesosCluster mesosCluster = mock(MesosCluster.class);\n        when(mesosCluster.getMaster()).thenReturn(master);\n        when(mesosCluster.getAgents()).thenReturn(Collections.singletonList(agent));\n\n        repository = mock(ClusterRepository.class);\n        when(repository.loadCluster(any(MesosClusterFactory.class))).thenReturn(mesosCluster);\n\n        downloader = mock(Downloader.class);\n        when(downloader.getFileContentAsString(STDOUT_URL)).thenReturn(\"stdout file content\");\n        when(downloader.getFileContentAsString(STDERR_URL)).thenReturn(\"stderr file content\");\n    }\n\n    @Test\n    public void TestStdout() throws UnsupportedEncodingException, URISyntaxException {\n        // Given\n        CommandLogs commandLogs = new CommandLogs(ps);\n        commandLogs.setRepository(repository);\n        commandLogs.setDownloader(downloader);\n        commandLogs.taskId = taskId;\n\n        // When\n        commandLogs.execute();\n\n        // Then\n        String result = outputStream.toString(\"UTF-8\");\n        assertEquals(\n            \"[minimesos] Fetching 'stdout' of task 'http-ports-static-assigned-to-31002.0f732069-a1f2-11e7-97e0-0242ac110006'\\n\\n\" +\n            \"stdout file content\\n\",\n            result);\n\n    }\n\n    @Test\n    public void TestUnexistingTask() throws UnsupportedEncodingException, URISyntaxException {\n        // Given\n        CommandLogs commandLogs = new CommandLogs(ps);\n        commandLogs.setRepository(repository);\n        commandLogs.setDownloader(downloader);\n        commandLogs.taskId = \"doesn't exist\";\n\n        // When\n        commandLogs.execute();\n\n        // Then\n        String result = outputStream.toString(\"UTF-8\");\n        assertEquals(\"Cannot find task: 'doesn't exist'\\n\", result);\n\n    }\n\n    @Test\n    public void TestStderr() throws UnsupportedEncodingException, URISyntaxException {\n        // Given\n        CommandLogs commandLogs = new CommandLogs(ps);\n        commandLogs.setRepository(repository);\n        commandLogs.setDownloader(downloader);\n        commandLogs.taskId = taskId;\n        commandLogs.stderr = true;\n\n        // When\n        commandLogs.execute();\n\n        // Then\n        String result = outputStream.toString(\"UTF-8\");\n        assertEquals(\n            \"[minimesos] Fetching 'stderr' of task 'http-ports-static-assigned-to-31002.0f732069-a1f2-11e7-97e0-0242ac110006'\\n\\n\" +\n                \"stderr file content\\n\",\n            result);\n    }\n\n    private State generateAgentState() {\n        State state = new State();\n        state.setId(slaveId);\n\n        Framework marathon = new Framework();\n        marathon.setName(\"marathon\");\n        marathon.setId(frameworkId);\n\n        Executor executor = new Executor();\n        executor.setDirectory(executorDirectory);\n        executor.setId(taskId);\n        ArrayList<Executor> executors = new ArrayList<>();\n        executors.add(executor);\n        marathon.setExecutors(executors);\n\n        ArrayList<Framework> frameworks = new ArrayList<>();\n        frameworks.add(marathon);\n        state.setFrameworks(frameworks);\n\n        return state;\n    }\n\n    private State generateMasterState() {\n        State state = new State();\n        Framework marathon = new Framework();\n        marathon.setName(\"marathon\");\n\n        Task task = new Task();\n        task.setName(\"weave-scope\");\n        task.setState(\"TASK_RUNNING\");\n        task.setId(taskId);\n        task.setSlaveId(slaveId);\n        task.setFrameworkId(frameworkId);\n        task.setExecutorId(\"\");\n\n        Port port = new Port();\n        port.setNumber(4040);\n\n        Ports ports = new Ports();\n        ports.setPorts(singletonList(port));\n\n        Discovery discovery = new Discovery();\n        discovery.setPorts(ports);\n\n        task.setDiscovery(discovery);\n\n        ArrayList<Task> tasks = new ArrayList<>();\n        tasks.add(task);\n\n        marathon.setTasks(tasks);\n\n        ArrayList<Framework> frameworks = new ArrayList<>();\n        frameworks.add(marathon);\n        state.setFrameworks(frameworks);\n\n        return state;\n    }\n}\n"
  },
  {
    "path": "cli/src/integration-test/java/com/containersol/minimesos/main/CommandPsTest.java",
    "content": "package com.containersol.minimesos.main;\n\nimport com.containersol.minimesos.cluster.ClusterRepository;\nimport com.containersol.minimesos.cluster.MesosCluster;\nimport com.containersol.minimesos.cluster.MesosClusterFactory;\nimport com.containersol.minimesos.mesos.MesosMasterContainer;\nimport com.containersol.minimesos.state.Discovery;\nimport com.containersol.minimesos.state.Framework;\nimport com.containersol.minimesos.state.Port;\nimport com.containersol.minimesos.state.Ports;\nimport com.containersol.minimesos.state.State;\nimport com.containersol.minimesos.state.Task;\nimport org.apache.commons.io.output.ByteArrayOutputStream;\nimport org.junit.Before;\nimport org.junit.Test;\n\nimport java.io.PrintStream;\nimport java.io.UnsupportedEncodingException;\nimport java.util.ArrayList;\n\nimport static java.util.Collections.singletonList;\nimport static org.junit.Assert.assertEquals;\nimport static org.mockito.Matchers.any;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.when;\n\npublic class CommandPsTest {\n\n    private static final String FORMAT = \"%-20s %-20s %-20s %-20s\\n\";\n    private static final Object[] COLUMNS = { \"FRAMEWORK\", \"TASK\", \"STATE\", \"PORT\"};\n    private static final Object[] VALUES = {\"marathon\", \"weave-scope\", \"TASK_RUNNING\", \"4040\" };\n\n    private ByteArrayOutputStream outputStream;\n\n    private PrintStream ps;\n\n    @Before\n    public void initTest() {\n        outputStream = new ByteArrayOutputStream();\n        ps = new PrintStream(outputStream, true);\n    }\n\n    @Test\n    public void execute() throws UnsupportedEncodingException {\n        State state = new State();\n        Framework marathon = new Framework();\n        marathon.setName(\"marathon\");\n\n        Task task = new Task();\n        task.setName(\"weave-scope\");\n        task.setState(\"TASK_RUNNING\");\n\n        Port port = new Port();\n        port.setNumber(4040);\n\n        Ports ports = new Ports();\n        ports.setPorts(singletonList(port));\n\n        Discovery discovery = new Discovery();\n        discovery.setPorts(ports);\n\n        task.setDiscovery(discovery);\n\n        ArrayList<Task> tasks = new ArrayList<>();\n        tasks.add(task);\n\n        marathon.setTasks(tasks);\n\n        ArrayList<Framework> frameworks = new ArrayList<>();\n        frameworks.add(marathon);\n        state.setFrameworks(frameworks);\n\n        MesosMasterContainer master = mock(MesosMasterContainer.class);\n        when(master.getState()).thenReturn(state);\n\n        MesosCluster mesosCluster = mock(MesosCluster.class);\n        when(mesosCluster.getMaster()).thenReturn(master);\n\n        ClusterRepository repository = mock(ClusterRepository.class);\n        when(repository.loadCluster(any(MesosClusterFactory.class))).thenReturn(mesosCluster);\n\n        CommandPs commandPs = new CommandPs(ps);\n        commandPs.setRepository(repository);\n\n        commandPs.execute();\n\n        String result = outputStream.toString(\"UTF-8\");\n        assertEquals(String.format(FORMAT, COLUMNS) + String.format(FORMAT, VALUES), result);\n    }\n\n}\n"
  },
  {
    "path": "cli/src/integration-test/java/com/containersol/minimesos/main/CommandTest.java",
    "content": "package com.containersol.minimesos.main;\n\nimport com.containersol.minimesos.MinimesosException;\nimport com.containersol.minimesos.cluster.ClusterRepository;\nimport com.containersol.minimesos.cluster.MesosCluster;\nimport com.containersol.minimesos.mesos.MesosClusterContainersFactory;\nimport org.apache.commons.io.FileUtils;\nimport org.apache.commons.io.output.ByteArrayOutputStream;\nimport org.json.JSONObject;\nimport org.junit.Before;\nimport org.junit.Test;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.io.PrintStream;\nimport java.io.UnsupportedEncodingException;\n\nimport static junit.framework.TestCase.assertEquals;\nimport static org.junit.Assert.assertFalse;\nimport static org.junit.Assert.assertTrue;\n\npublic class CommandTest {\n\n    private ByteArrayOutputStream outputStream;\n\n    private PrintStream ps;\n\n    private ClusterRepository repository = new ClusterRepository();\n\n    @Before\n    public void initTest() {\n        outputStream = new ByteArrayOutputStream();\n        ps = new PrintStream(outputStream, true);\n    }\n\n    @Test\n    public void testUpAndDestroy() {\n        CommandUp commandUp = new CommandUp();\n        commandUp.setClusterConfigPath(\"src/integration-test/resources/configFiles/complete-minimesosFile\");\n        commandUp.execute();\n\n        MesosCluster cluster = commandUp.getCluster();\n\n        File minimesosFile = repository.getMinimesosFile();\n\n        assertTrue(\"Minimesos file at \" + minimesosFile + \" should exist\", minimesosFile.exists());\n\n        assertEquals(7, cluster.getMemberProcesses().size());\n\n        cluster.destroy(new MesosClusterContainersFactory());\n\n        assertFalse(\"Minimesos file at \" + minimesosFile + \" should be removed\", minimesosFile.exists());\n    }\n\n    @Test\n    public void testUp_invalidMinimesosFile() throws IOException {\n        FileUtils.write(repository.getMinimesosFile(), \"invalid\", \"UTF-8\");\n\n        CommandUp commandUp = new CommandUp();\n        commandUp.setClusterConfigPath(\"src/integration-test/resources/configFiles/complete-minimesosFile\");\n        commandUp.execute();\n        MesosCluster cluster = commandUp.getCluster();\n\n        String fileContent = FileUtils.readFileToString(repository.getMinimesosFile(), \"UTF-8\");\n        assertEquals(\"Invalid state file has not been overwritten\", cluster.getClusterId(), fileContent);\n\n        cluster.destroy(new MesosClusterContainersFactory());\n\n        File minimesosFile = repository.getMinimesosFile();\n\n        assertFalse(\"Minimesos file at \" + minimesosFile + \" should be removed\", minimesosFile.exists());\n    }\n\n    @Test\n    public void testUp_alreadyRunning() {\n        CommandUp commandUp = new CommandUp();\n        commandUp.setClusterConfigPath(\"src/integration-test/resources/configFiles/complete-minimesosFile\");\n\n        commandUp.execute();\n        MesosCluster firstCluster = commandUp.getCluster();\n\n        commandUp.execute();\n        MesosCluster secondCluster = commandUp.getCluster();\n\n        assertEquals(firstCluster, secondCluster);\n\n        MesosClusterContainersFactory factory = new MesosClusterContainersFactory();\n\n        firstCluster.destroy(factory);\n        secondCluster.destroy(factory);\n\n        File minimesosFile = repository.getMinimesosFile();\n\n        assertFalse(\"Minimesos file at \" + minimesosFile + \" should be removed\", minimesosFile.exists());\n    }\n\n    @Test\n    public void testInfo_runningCluster() throws IOException {\n        CommandUp commandUp = new CommandUp();\n        commandUp.setClusterConfigPath(\"src/integration-test/resources/configFiles/complete-minimesosFile\");\n        commandUp.execute();\n\n        CommandInfo commandInfo = new CommandInfo(ps);\n        commandInfo.execute();\n\n        String result = outputStream.toString(\"UTF-8\");\n\n        assertTrue(result.contains(\"Minimesos cluster is running\"));\n        assertTrue(result.contains(\"Mesos version\"));\n\n        commandUp.getCluster().destroy(new MesosClusterContainersFactory());\n    }\n\n    @Test\n    public void testInfo_notRunning() throws IOException {\n        CommandInfo commandInfo = new CommandInfo(ps);\n        commandInfo.execute();\n\n        String result = outputStream.toString(\"UTF-8\");\n        assertTrue(\"Expected phrase not found in: \" + result, result.contains(\"Cluster ID is not found\"));\n    }\n\n    @Test\n    public void testState() throws IOException {\n        CommandUp commandUp = new CommandUp();\n        commandUp.setClusterConfigPath(\"src/integration-test/resources/configFiles/complete-minimesosFile\");\n        commandUp.execute();\n        MesosCluster cluster = commandUp.getCluster();\n\n        CommandState commandState = new CommandState(ps);\n        commandState.execute();\n\n        JSONObject state = new JSONObject(outputStream.toString(\"UTF-8\"));\n\n        assertEquals(\"master@\" + cluster.getMaster().getIpAddress() + \":5050\", state.getString(\"leader\"));\n\n        cluster.destroy(new MesosClusterContainersFactory());\n    }\n\n    @Test\n    public void testInstallCommandValidation() {\n        CommandInstall install = new CommandInstall();\n        assertFalse(\"Install command requires one of the parameters\", install.validateParameters());\n    }\n\n    @Test\n    public void testInstall() {\n        CommandUp commandUp = new CommandUp();\n        commandUp.setClusterConfigPath(\"src/integration-test/resources/configFiles/complete-minimesosFile\");\n        commandUp.execute();\n\n        CommandInstall install = new CommandInstall();\n        install.app = \"src/integration-test/resources/app.json\";\n\n        install.execute();\n\n        commandUp.getCluster().destroy(new MesosClusterContainersFactory());\n    }\n\n    @Test(expected = MinimesosException.class)\n    public void testInstall_alreadyRunning() {\n        CommandUp commandUp = new CommandUp();\n        commandUp.execute();\n\n        CommandInstall install = new CommandInstall();\n        install.app = \"src/integration-test/resources/app.json\";\n\n        install.execute();\n        install.execute();\n\n        commandUp.getCluster().destroy(new MesosClusterContainersFactory());\n    }\n\n    @Test\n    public void testState_notRunning() throws IOException {\n        CommandState commandState = new CommandState(ps);\n        commandState.execute();\n\n        String result = outputStream.toString(\"UTF-8\");\n        assertTrue(result.contains(\"Minimesos cluster is not running\"));\n    }\n\n    @Test\n    public void testCompleteInitFile() throws UnsupportedEncodingException {\n        CommandUp commandUp = new CommandUp(ps);\n        commandUp.setClusterConfigPath(\"src/integration-test/resources/configFiles/complete-minimesosFile\");\n        commandUp.execute();\n\n        String result = outputStream.toString(\"UTF-8\");\n\n        assertTrue(\"Command up output does not contain expected line\", result.contains(\"Minimesos cluster is running\"));\n        assertTrue(result.contains(\"Mesos version\"));\n\n        assertTrue(result.contains(\"MINIMESOS_MARATHON\"));\n\n        commandUp.getCluster().destroy(new MesosClusterContainersFactory());\n    }\n\n}\n"
  },
  {
    "path": "cli/src/integration-test/java/com/containersol/minimesos/main/CommandUninstallTest.java",
    "content": "package com.containersol.minimesos.main;\n\nimport com.containersol.minimesos.MinimesosException;\nimport com.containersol.minimesos.cluster.ClusterRepository;\nimport com.containersol.minimesos.cluster.Marathon;\nimport com.containersol.minimesos.cluster.MesosCluster;\nimport com.containersol.minimesos.cluster.MesosClusterFactory;\nimport mesosphere.marathon.client.model.v2.Result;\nimport org.apache.commons.io.output.ByteArrayOutputStream;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.mockito.Matchers;\nimport org.mockito.Mockito;\n\nimport java.io.PrintStream;\nimport java.io.UnsupportedEncodingException;\n\nimport static org.junit.Assert.assertEquals;\nimport static org.mockito.Mockito.when;\n\npublic class CommandUninstallTest {\n\n    private ByteArrayOutputStream outputStream;\n\n    private PrintStream ps;\n    private Marathon marathon;\n    private MesosCluster mesosCluster;\n    private ClusterRepository repository;\n    private CommandUninstall commandUninstall;\n\n    @Before\n    public void initTest() {\n        outputStream = new ByteArrayOutputStream();\n        ps = new PrintStream(outputStream, true);\n\n        marathon = Mockito.mock(Marathon.class);\n\n        mesosCluster = Mockito.mock(MesosCluster.class);\n        when(mesosCluster.getMarathon()).thenReturn(marathon);\n\n        repository = Mockito.mock(ClusterRepository.class);\n        when(repository.loadCluster(Matchers.any(MesosClusterFactory.class))).thenReturn(mesosCluster);\n\n        commandUninstall = new CommandUninstall(ps);\n        commandUninstall.setRepository(repository);\n    }\n\n    @Test\n    public void execute_app() throws UnsupportedEncodingException {\n        // Given\n        commandUninstall.setApp(\"/app\");\n        when(marathon.deleteApp(\"/app\")).thenReturn(new Result());\n\n        // When\n        commandUninstall.execute();\n\n        // Then\n        String string = outputStream.toString(\"UTF-8\");\n        assertEquals(\"Deleted app '/app'\\n\", string);\n    }\n\n    @Test\n    public void execute_group() throws UnsupportedEncodingException {\n        // Given\n        commandUninstall.setGroup(\"/group\");\n        when(marathon.deleteGroup(\"/group\")).thenReturn(new Result());\n\n        // When\n        commandUninstall.execute();\n\n        // Then\n        String string = outputStream.toString(\"UTF-8\");\n        assertEquals(\"Deleted group '/group'\\n\", string);\n    }\n\n    @Test\n    public void execute_appAndGroup() throws UnsupportedEncodingException {\n        // Given\n        commandUninstall.setGroup(\"/group1\");\n        commandUninstall.setApp(\"/app2\");\n\n        // When\n        commandUninstall.execute();\n\n        // Then\n        String string = outputStream.toString(\"UTF-8\");\n        assertEquals(\"Please specify --app or --group to uninstall an app or group\\n\", string);\n    }\n\n    @Test\n    public void execute_appDoesNotExist() throws UnsupportedEncodingException {\n        Mockito.doThrow(new MinimesosException(\"App does not exist\")).when(marathon).deleteApp(\"app\");\n\n        commandUninstall.execute();\n\n        String result = outputStream.toString(\"UTF-8\");\n        assertEquals(\"Please specify --app or --group to uninstall an app or group\\n\", result);\n    }\n}\n"
  },
  {
    "path": "cli/src/integration-test/java/com/containersol/minimesos/main/CommandUpTest.java",
    "content": "package com.containersol.minimesos.main;\n\nimport com.containersol.minimesos.MinimesosException;\nimport com.containersol.minimesos.cluster.MesosCluster;\nimport com.containersol.minimesos.config.ClusterConfig;\nimport com.containersol.minimesos.mesos.MesosClusterContainersFactory;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.mockito.ArgumentCaptor;\n\nimport java.io.IOException;\n\nimport static org.junit.Assert.assertTrue;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.when;\n\npublic class CommandUpTest {\n\n    private CommandUp commandUp;\n\n    private ArgumentCaptor<ClusterConfig> capturedClusterConfig;\n\n    private MesosCluster mesosCluster;\n\n    @Before\n    public void before() {\n        MesosClusterContainersFactory mesosClusterFactory = mock(MesosClusterContainersFactory.class);\n        mesosCluster = mock(MesosCluster.class);\n        when(mesosCluster.getClusterId()).thenReturn(\"123456\");\n\n        capturedClusterConfig = ArgumentCaptor.forClass(ClusterConfig.class);\n        when(mesosClusterFactory.createMesosCluster(capturedClusterConfig.capture())).thenReturn(mesosCluster);\n\n        commandUp = new CommandUp();\n        commandUp.setMesosClusterFactory(mesosClusterFactory);\n    }\n\n    @Test(expected = MinimesosException.class)\n    public void testExecute_missingMinimesosFile() throws IOException {\n        commandUp.execute();\n    }\n\n    @Test(expected = MinimesosException.class)\n    public void testExecute_invalidMinimesosFile() throws IOException {\n        commandUp.setClusterConfigPath(\"src/integration-test/resources/configFiles/invalid-minimesosFile\");\n        commandUp.execute();\n    }\n\n    @Test\n    public void testBasicClusterConfig() throws IOException {\n        commandUp.setClusterConfigPath(\"src/integration-test/resources/clusterconfig/basic.groovy\");\n        commandUp.execute();\n\n        verify(mesosCluster).start();\n    }\n\n    @Test\n    public void testExecute_mapPortsToHost() {\n        commandUp.setClusterConfigPath(\"src/integration-test/resources/configFiles/complete-minimesosFile\");\n        commandUp.setMapPortsToHost(true);\n        commandUp.execute();\n\n        assertTrue(\"Map ports to host from configuration is expected to remain\", capturedClusterConfig.getValue().isMapPortsToHost());\n    }\n\n}\n"
  },
  {
    "path": "cli/src/integration-test/resources/app.json",
    "content": "{\n  \"id\": \"hello\",\n  \"cmd\": \"echo 'hello'\",\n  \"cpus\": 0.1,\n  \"mem\": 16.0,\n  \"instances\": 1\n}"
  },
  {
    "path": "cli/src/integration-test/resources/clusterconfig/basic.groovy",
    "content": "package clusterconfig\n\nminimesos {\n\n    mapPortsToHost = true\n    timeout = 60\n    mesosVersion = \"1.0.0\"\n    clusterName = \"minimesos-test\"\n\n    master {\n    }\n\n    agent {\n\n        resources {\n            cpu {\n                role = \"logstash\"\n                value = 0.2\n            }\n            mem {\n                role = \"logstash\"\n                value = 512\n            }\n            disk {\n                role = \"*\"\n                value = 5120\n            }\n        }\n\n        imageName = \"containersol/mesos-agent\"\n        imageTag = \"1.0.0-0.1.0\"\n\n    }\n\n    zookeeper {\n    }\n\n    marathon {\n    }\n\n}\n"
  },
  {
    "path": "cli/src/integration-test/resources/clusterconfig/two-agents.groovy",
    "content": "package clusterconfig\n\nminimesos {\n    agent {\n        resources {\n            cpu {\n                role = \"*\"\n                value = 2\n            }\n            mem {\n                role = \"*\"\n                value = 1024\n            }\n            disk {\n                role = \"*\"\n                value = 8192\n            }\n        }\n    }\n    agent {\n    }\n}\n"
  },
  {
    "path": "cli/src/integration-test/resources/configFiles/complete-minimesosFile",
    "content": "minimesos {\n    clusterName = \"Change Cluster Name in minimesosFile file\"\n    mapPortsToHost = false\n    loggingLevel = \"INFO\"\n    mapAgentSandboxVolume = false\n    mesosVersion = \"0.28\"\n    timeout = 60\n\n    agent {\n        imageName = \"containersol/mesos-agent\"\n        imageTag = \"1.0.0-0.1.0\"\n        loggingLevel = \"# INHERIT FROM CLUSTER\"\n        portNumber = 5051\n\n        resources {\n\n            cpu {\n                role = \"*\"\n                value = 4\n            }\n\n            disk {\n                role = \"*\"\n                value = 2000\n            }\n\n            mem {\n                role = \"*\"\n                value = 512\n            }\n\n            ports {\n                role = \"*\"\n                value = \"[31000-32000]\"\n            }\n        }\n    }\n\n    consul {\n        imageName = \"consul\"\n        imageTag = \"0.7.1\"\n    }\n\n    marathon {\n        imageName = \"mesosphere/marathon\"\n        imageTag = \"v1.3.5\"\n    }\n\n    master {\n        aclJson = null\n        authenticate = false\n        imageName = \"containersol/mesos-master\"\n        imageTag = \"1.0.0-0.1.0\"\n        loggingLevel = \"# INHERIT FROM CLUSTER\"\n    }\n\n    registrator {\n        imageName = \"gliderlabs/registrator\"\n        imageTag = \"v6\"\n    }\n\n    mesosdns {\n        imageName = \"xebia/mesos-dns\"\n        imageTag = \"0.0.5\"\n    }\n\n    zookeeper {\n        imageName = \"jplock/zookeeper\"\n        imageTag = \"3.4.6\"\n    }\n}\n"
  },
  {
    "path": "cli/src/integration-test/resources/configFiles/invalid-minimesosFile.txt",
    "content": "invalid"
  },
  {
    "path": "cli/src/integration-test/resources/configFiles/marathonAppConfig-minimesosFile",
    "content": "minimesos {\n    clusterName = \"minimesos-test\"\n    mapPortsToHost = false\n    loggingLevel = \"INFO\"\n    mapAgentSandboxVolume = false\n    mesosVersion = \"0.28\"\n    timeout = 60\n\n    agent {\n        imageName = \"containersol/mesos-agent\"\n        imageTag = \"1.0.0-0.1.0\"\n        portNumber = 5051\n\n        resources {\n\n            cpu {\n                role = \"*\"\n                value = 8\n            }\n\n            disk {\n                role = \"*\"\n                value = 10000\n            }\n\n            mem {\n                role = \"*\"\n                value = 1024\n            }\n\n            ports {\n                role = \"*\"\n                value = \"[31000-32000]\"\n            }\n        }\n    }\n\n    consul {\n        imageName = \"consul\"\n        imageTag = \"0.7.1\"\n    }\n\n    marathon {\n        imageName = \"mesosphere/marathon\"\n        imageTag = \"v1.3.5\"\n\n        app {\n          marathonJson = \"src/test/resources/app.json\"\n        }\n\n    }\n\n    master {\n        imageName = \"containersol/mesos-master\"\n        imageTag = \"1.0.0-0.1.0\"\n    }\n\n    registrator {\n        imageName = \"gliderlabs/registrator\"\n        imageTag = \"v6\"\n    }\n\n    mesosdns {\n        imageName = \"xebia/mesos-dns\"\n        imageTag = \"0.0.5\"\n    }\n\n    zookeeper {\n        imageName = \"jplock/zookeeper\"\n        imageTag = \"3.4.6\"\n    }\n}\n"
  },
  {
    "path": "cli/src/integration-test/resources/configFiles/withMarathon-minimesosFile",
    "content": "minimesos {\n    clusterName = \"minimesos-test\"\n    mapPortsToHost = false\n    loggingLevel = \"INFO\"\n    mapAgentSandboxVolume = false\n    mesosVersion = \"0.28\"\n    timeout = 60\n\n    marathon {\n        imageName = \"mesosphere/marathon\"\n        imageTag = \"v1.3.5\"\n    }\n}\n"
  },
  {
    "path": "cli/src/integration-test/resources/logback-test.xml",
    "content": "<configuration debug=\"false\">\n\n\t<appender name=\"console\" class=\"ch.qos.logback.core.ConsoleAppender\">\n\t\t<target>System.out</target>\n\t\t<encoder>\n\t\t\t<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36}: %msg%n</pattern>\n\t\t</encoder>\n\t</appender>\n\n\t<logger name=\"com.containersol.minimesos\" level=\"debug\"/>\n\n\t<root level=\"warn\">\n\t\t<appender-ref ref=\"console\" />\n\t</root>\n\n</configuration>\n"
  },
  {
    "path": "cli/src/main/java/com/containersol/minimesos/main/Command.java",
    "content": "package com.containersol.minimesos.main;\n\npublic interface Command {\n\n    /**\n     * Validates combination of command parameters\n     *\n     * @return true if command parameters are valid\n     */\n    boolean validateParameters();\n\n    /**\n     * @return name of the command\n     */\n    String getName();\n\n    /**\n     * Executes the command\n     */\n    void execute();\n\n}\n"
  },
  {
    "path": "cli/src/main/java/com/containersol/minimesos/main/CommandDestroy.java",
    "content": "package com.containersol.minimesos.main;\n\nimport com.beust.jcommander.Parameters;\nimport com.containersol.minimesos.cluster.ClusterRepository;\nimport com.containersol.minimesos.cluster.MesosCluster;\nimport com.containersol.minimesos.mesos.MesosClusterContainersFactory;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * Parameters for the 'destroy' command.\n */\n@Parameters(separators = \"=\", commandDescription = \"Destroy a minimesos cluster\")\npublic class CommandDestroy implements Command {\n\n    private static final Logger LOGGER = LoggerFactory.getLogger(CommandDestroy.class);\n\n    public static final String CLINAME = \"destroy\";\n\n    private ClusterRepository repository = new ClusterRepository();\n\n    @Override\n    public void execute() {\n\n        MesosClusterContainersFactory clusterFactory = new MesosClusterContainersFactory();\n\n        MesosCluster cluster = repository.loadCluster(clusterFactory);\n        if (cluster != null) {\n            cluster.destroy(clusterFactory);\n            LOGGER.info(\"Destroyed minimesos cluster with ID \" + cluster.getClusterId());\n        } else {\n            LOGGER.info(\"Minimesos cluster is not running\");\n        }\n    }\n\n    @Override\n    public boolean validateParameters() {\n        return true;\n    }\n\n    @Override\n    public String getName() {\n        return CLINAME;\n    }\n\n}\n"
  },
  {
    "path": "cli/src/main/java/com/containersol/minimesos/main/CommandHelp.java",
    "content": "package com.containersol.minimesos.main;\n\nimport com.beust.jcommander.Parameters;\n\n/**\n * Help command\n */\n@Parameters(separators = \"=\", commandDescription = \"Display help\")\npublic class CommandHelp implements Command {\n\n    public static final String CLINAME = \"help\";\n\n    @Override\n    public void execute() {\n        // Usage is being printed from Main\n    }\n\n    @Override\n    public boolean validateParameters() {\n        return true;\n    }\n\n    @Override\n    public String getName() {\n        return CLINAME;\n    }\n\n}\n"
  },
  {
    "path": "cli/src/main/java/com/containersol/minimesos/main/CommandInfo.java",
    "content": "package com.containersol.minimesos.main;\n\nimport java.io.PrintStream;\nimport java.net.URI;\nimport java.util.List;\n\nimport com.beust.jcommander.Parameters;\nimport com.containersol.minimesos.cluster.ClusterProcess;\nimport com.containersol.minimesos.cluster.ClusterRepository;\nimport com.containersol.minimesos.cluster.ClusterUtil;\nimport com.containersol.minimesos.cluster.MesosCluster;\nimport com.containersol.minimesos.cluster.MesosDns;\nimport com.containersol.minimesos.docker.DockerContainersUtil;\nimport com.containersol.minimesos.mesos.MesosClusterContainersFactory;\nimport com.containersol.minimesos.util.Environment;\n\n/**\n * Info command\n */\n@Parameters(separators = \"=\", commandDescription = \"Display cluster information\")\npublic class CommandInfo implements Command {\n\n    public static final String CLINAME = \"info\";\n\n    private PrintStream output = System.out; //NOSONAR\n\n    private ClusterRepository repository = new ClusterRepository();\n\n    public CommandInfo() { //NOSONAR\n    }\n\n    public CommandInfo(PrintStream ps) {\n        this.output = ps;\n    }\n\n    @Override\n    public void execute() {\n        String clusterId = repository.readClusterId();\n        if (clusterId != null) {\n            MesosCluster cluster = repository.loadCluster(new MesosClusterContainersFactory());\n            if (cluster != null) {\n                output.println(\"Minimesos cluster is running: \" + cluster.getClusterId());\n                output.println(\"Mesos version: \" + cluster.getMaster().getState().getVersion());\n                printServiceUrls(cluster);\n\n                MesosDns mesosDns = cluster.getMesosDns();\n                if (mesosDns != null) {\n                    output.println(\"Running dnsmasq? Add 'server=/mm/\" + mesosDns.getIpAddress() + \"#53' to /etc/dnsmasq.d/10-minimesos to resolve master.mm, zookeeper.mm and Marathon apps on app.marathon.mm.\");\n                }\n            } else {\n                output.println(String.format(\"Minimesos cluster %s is not running. %s is removed\", clusterId, repository.getMinimesosFile().getAbsolutePath()));\n            }\n        } else {\n            output.println(\"Cluster ID is not found in \" + repository.getMinimesosFile().getAbsolutePath());\n        }\n    }\n\n    /**\n     * Prints cluster services URLs and IPs\n     *\n     * @param cluster to examine\n     */\n    private void printServiceUrls(MesosCluster cluster) {\n\n        // print independent from roles variables\n        String masterContainer = cluster.getMaster().getContainerId();\n        String gateway = String.format(\"export %s=%s\", MesosCluster.TOKEN_NETWORK_GATEWAY, DockerContainersUtil.getGatewayIpAddress(masterContainer));\n        output.println(gateway);\n\n        List<ClusterProcess> uniqueMembers = ClusterUtil.getDistinctRoleProcesses(cluster.getMemberProcesses());\n        for (ClusterProcess process : uniqueMembers) {\n\n            URI serviceUrl = process.getServiceUrl();\n            if (serviceUrl != null) {\n                String service = String.format(\"export %s%s=%s\", MesosCluster.MINIMESOS_TOKEN_PREFIX, process.getRole().toUpperCase(), serviceUrl.toString());\n                String serviceIp = String.format(\"export %s%s_IP=%s\", MesosCluster.MINIMESOS_TOKEN_PREFIX, process.getRole().toUpperCase(), serviceUrl.getHost());\n\n                output.println(String.format(\"%s; %s\", service, serviceIp));\n            }\n\n        }\n\n        if (Environment.isRunningInDockerOnMac()) {\n            output.println(\"You are running Docker on Mac so use localhost instead of container IPs for Master, Marathon, Zookeepr and Consul\");\n        }\n    }\n\n    @Override\n    public boolean validateParameters() {\n        return true;\n    }\n\n    @Override\n    public String getName() {\n        return CLINAME;\n    }\n\n}\n"
  },
  {
    "path": "cli/src/main/java/com/containersol/minimesos/main/CommandInit.java",
    "content": "package com.containersol.minimesos.main;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.nio.file.FileSystems;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\nimport java.nio.file.attribute.UserPrincipal;\nimport java.nio.file.attribute.UserPrincipalLookupService;\n\nimport com.beust.jcommander.Parameters;\nimport com.containersol.minimesos.MinimesosException;\nimport com.containersol.minimesos.cluster.MesosCluster;\nimport com.containersol.minimesos.config.AppConfig;\nimport com.containersol.minimesos.config.ClusterConfig;\nimport com.containersol.minimesos.config.ConfigParser;\nimport com.containersol.minimesos.config.ConsulConfig;\nimport com.containersol.minimesos.config.MarathonConfig;\nimport com.containersol.minimesos.config.MesosAgentConfig;\nimport com.containersol.minimesos.config.MesosDNSConfig;\nimport com.containersol.minimesos.config.MesosMasterConfig;\nimport com.containersol.minimesos.config.RegistratorConfig;\nimport com.containersol.minimesos.config.ZooKeeperConfig;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport static java.lang.String.format;\n\n/**\n * Initializes a default minimesosFile in the directory where minimesos is run\n */\n@Parameters(separators = \"=\", commandDescription = \"Initialize a minimesosFile\")\npublic class CommandInit implements Command {\n\n    private static final Logger LOGGER = LoggerFactory.getLogger(CommandInit.class);\n\n    public static final String CLINAME = \"init\";\n\n    public static final String DEFAULT_HOST_USERID = \"1000\";\n\n    @Override\n    public boolean validateParameters() {\n        return true;\n    }\n\n    @Override\n    public String getName() {\n        return CLINAME;\n    }\n\n    @Override\n    public void execute() {\n        File minimesosFile = new File(MesosCluster.getClusterHostDir(), ClusterConfig.DEFAULT_CONFIG_FILE);\n\n        if (minimesosFile.exists()) {\n            throw new MinimesosException(\"A minimesosFile already exists in this directory\");\n        }\n\n        String fileContent = getConfigFileContent();\n\n        Path minimesosPath = Paths.get(minimesosFile.getAbsolutePath());\n        try {\n            Files.write(minimesosPath, fileContent.getBytes());\n        } catch (IOException e) {\n            throw new MinimesosException(format(\"Could not initialize minimesosFile: %s\", e.getMessage()), e);\n        }\n\n        LOGGER.info(\"Initialized minimesosFile in this directory\");\n\n        try {\n            UserPrincipalLookupService lookupService = FileSystems.getDefault().getUserPrincipalLookupService();\n            UserPrincipal owner = lookupService.lookupPrincipalByName(DEFAULT_HOST_USERID);\n            Files.setOwner(minimesosPath, owner);\n        } catch (IOException e) {\n            throw new MinimesosException(\"NOTE: minimesosFile remains owned by root instead of user ID \" + DEFAULT_HOST_USERID + \": \" + e.getMessage(), e);\n        }\n\n    }\n\n    public String getConfigFileContent() {\n        ClusterConfig config = new ClusterConfig();\n        config.setClusterName(\"Change Cluster Name in \" + ClusterConfig.DEFAULT_CONFIG_FILE + \" file\");\n\n        config.setMaster(new MesosMasterConfig(ClusterConfig.DEFAULT_MESOS_VERSION));\n        config.setZookeeper(new ZooKeeperConfig());\n        config.getAgents().add(new MesosAgentConfig(ClusterConfig.DEFAULT_MESOS_VERSION));\n        config.setConsul(new ConsulConfig());\n        config.setRegistrator(new RegistratorConfig());\n        config.setMesosdns(new MesosDNSConfig());\n\n        AppConfig weaveConfig = new AppConfig();\n        weaveConfig.setMarathonJson(\"https://raw.githubusercontent.com/ContainerSolutions/minimesos/master/opt/apps/weave-scope.json\");\n\n        MarathonConfig marathonConfig = new MarathonConfig();\n        marathonConfig.getApps().add(weaveConfig);\n        config.setMarathon(marathonConfig);\n\n        ConfigParser parser = new ConfigParser();\n        return parser.toString(config);\n    }\n\n}\n"
  },
  {
    "path": "cli/src/main/java/com/containersol/minimesos/main/CommandInstall.java",
    "content": "package com.containersol.minimesos.main;\n\nimport com.beust.jcommander.Parameter;\nimport com.beust.jcommander.Parameters;\nimport com.containersol.minimesos.MinimesosException;\nimport com.containersol.minimesos.cluster.ClusterRepository;\nimport com.containersol.minimesos.cluster.Marathon;\nimport com.containersol.minimesos.cluster.MesosCluster;\nimport com.containersol.minimesos.mesos.MesosClusterContainersFactory;\nimport org.apache.commons.io.IOUtils;\n\nimport java.io.IOException;\n\nimport static org.apache.commons.lang.StringUtils.*;\n\n/**\n * Installs an Marathon application or application group.\n */\n@Parameters(commandDescription = \"Install a Marathon application or application group\")\npublic class CommandInstall implements Command {\n\n    private static final String CLINAME = \"install\";\n\n    @Deprecated\n    @Parameter(names = \"--marathonFile\", description = \"[Deprecated - Please use --marathonApp] Relative path or URL to a JSON file with a Marathon app definition.\")\n    String marathonFile = null;\n\n    @Parameter(names = \"--app\", description = \"Relative path or URL to a JSON file with a Marathon app definition. See https://mesosphere.github.io/marathon/docs/application-basics.html.\")\n    String app = null;\n\n    @Parameter(names = \"--group\", description = \"Relative path or URL to a JSON file with a group of Marathon apps. See https://mesosphere.github.io/marathon/docs/application-groups.html.\")\n    String group = null;\n\n    @Parameter(names = \"--stdin\", description = \"Read JSON file with Marathon app or group definition from stdin.\")\n    private boolean stdin = false;\n\n    @Parameter(names = \"--update\", description = \"Update a running application instead of attempting to deploy a new application\")\n    private boolean update = false;\n\n    ClusterRepository repository = new ClusterRepository();\n\n    @Override\n    public void execute() {\n        MesosCluster cluster = repository.loadCluster(new MesosClusterContainersFactory());\n        if (cluster != null) {\n            Marathon marathon = cluster.getMarathon();\n            if (marathon == null) {\n                throw new MinimesosException(\"Marathon container is not found in cluster \" + cluster.getClusterId());\n            }\n\n            String marathonJson;\n            try {\n                marathonJson = getMarathonJson();\n            } catch (IOException e) {\n                throw new MinimesosException(\"Failed to read JSON file from path, URL or stdin\", e);\n            }\n\n            if (update) {\n                marathon.updateApp(marathonJson);\n            } else if (isNotBlank(app) || isNotBlank(marathonFile)) {\n                marathon.deployApp(marathonJson);\n            } else if (isNotBlank(group)) {\n                marathon.deployGroup(marathonJson);\n            } else {\n                throw new MinimesosException(\"Neither app, group, --stdinApp or --stdinGroup is provided\");\n            }\n        } else {\n            throw new MinimesosException(\"Running cluster is not found\");\n        }\n    }\n\n    /**\n     * Getting content of the Marathon JSON file if specified or via standard input\n     *\n     * @return content of the file or standard input\n     */\n    private String getMarathonJson() throws IOException {\n        if (stdin) {\n            return IOUtils.toString(System.in, \"UTF-8\");\n        } else {\n            if (isNotBlank(marathonFile)) {\n                return IOUtils.toString(MesosCluster.getInputStream(marathonFile), \"UTF-8\");\n            } else if (isNotBlank(app)) {\n                return IOUtils.toString(MesosCluster.getInputStream(app), \"UTF-8\");\n            } else if (isNotBlank(group)) {\n                return IOUtils.toString(MesosCluster.getInputStream(group), \"UTF-8\");\n            }\n        }\n        throw new IOException(\"Please specify a URL or path to Marathon JSON file or use --stdin\");\n    }\n\n    @Override\n    public boolean validateParameters() {\n        return isNotBlank(app) || isNotBlank(group) || isNotBlank(marathonFile);\n    }\n\n    @Override\n    public String getName() {\n        return CLINAME;\n    }\n\n}\n"
  },
  {
    "path": "cli/src/main/java/com/containersol/minimesos/main/CommandLogs.java",
    "content": "package com.containersol.minimesos.main;\n\nimport com.beust.jcommander.Parameter;\nimport com.beust.jcommander.Parameters;\nimport com.containersol.minimesos.MinimesosException;\nimport com.containersol.minimesos.cluster.ClusterRepository;\nimport com.containersol.minimesos.cluster.MesosAgent;\nimport com.containersol.minimesos.cluster.MesosCluster;\nimport com.containersol.minimesos.mesos.MesosClusterContainersFactory;\nimport com.containersol.minimesos.state.Executor;\nimport com.containersol.minimesos.state.Framework;\nimport com.containersol.minimesos.state.State;\nimport com.containersol.minimesos.state.Task;\nimport com.containersol.minimesos.util.Downloader;\nimport org.apache.http.client.utils.URIBuilder;\n\nimport java.io.PrintStream;\nimport java.net.URI;\nimport java.net.URISyntaxException;\n\nimport static org.apache.commons.lang.StringUtils.isNotBlank;\nimport static org.apache.commons.lang.StringUtils.isBlank;\n\n@Parameters(separators = \"=\", commandDescription = \"Fetches the stdout logs of the specified task\")\npublic class CommandLogs implements Command {\n\n    private PrintStream output = System.out; // NOSONAR\n\n    private ClusterRepository repository = new ClusterRepository();\n\n    private Downloader downloader = new Downloader();\n\n    @Parameter(names = \"--task\", description = \"Substring of a task ID\", required = true)\n    String taskId = null;\n\n    @Parameter(names = \"--stderr\", description = \"Fetch the stderr logs instead of stdout\")\n    Boolean stderr = false;\n\n    public CommandLogs(PrintStream output) {\n        this.output = output;\n    }\n\n    public CommandLogs() {\n        //NOSONAR\n    }\n\n    @Override\n    public boolean validateParameters() {\n        return isNotBlank(taskId);\n    }\n\n    @Override\n    public String getName() {\n        return \"logs\";\n    }\n\n    @Override\n    public void execute() {\n        MesosCluster cluster = repository.loadCluster(new MesosClusterContainersFactory());\n        if (cluster == null) {\n            output.println(\"Minimesos cluster is not running\");\n            return;\n        }\n\n        State masterState = cluster.getMaster().getState();\n        Task task = findTask(masterState, taskId);\n        if (task == null) {\n            output.println(String.format(\"Cannot find task: '%s'\", taskId));\n            return;\n        }\n\n        MesosAgent agent = findAgent(cluster, task.getSlaveId());\n        if (agent == null) {\n            output.println(String.format(\"Cannot find agent: '%s'\", task.getSlaveId()));\n            return;\n        }\n\n\n        String filename = stderr ? \"stderr\" : \"stdout\";\n        output.println(String.format(\"[minimesos] Fetching '%s' of task '%s'\\n\", filename, task.getId()));\n        URI fileUrl = getFileUrl(agent, task, filename);\n        String content = downloader.getFileContentAsString(fileUrl.toString());\n        output.println(content);\n    }\n\n    public void setRepository(ClusterRepository repository) {\n        this.repository = repository;\n    }\n\n    void setDownloader(Downloader downloader) {\n        this.downloader = downloader;\n    }\n\n    private Task findTask(State state, String taskId) {\n        for (Framework framework : state.getFrameworks()) {\n            for (Task task: framework.getTasks()) {\n                if (task.getId().contains(taskId)) {\n                    return task;\n                }\n            }\n        }\n        return null;\n    }\n\n    private MesosAgent findAgent(MesosCluster cluster, String slaveId) {\n        for (MesosAgent agent : cluster.getAgents()) {\n            State agentState = agent.getState();\n            if (agentState.getId().equals(slaveId)) {\n                return agent;\n            }\n        }\n        return null;\n    }\n\n    private URI getFileUrl(MesosAgent agent, Task task, String filename) throws MinimesosException {\n        Executor executor = findExecutor(agent, task);\n        if (executor == null) {\n            throw new MinimesosException(String.format(\"Cannot find executor: '%s'\", taskId));\n        }\n        String path = executor.getDirectory();\n        URIBuilder uriBuilder = new URIBuilder(agent.getServiceUrl())\n            .setPath(\"/files/download\")\n            .addParameter(\"path\", path + \"/\" + filename);\n        URI sandboxUrl = null;\n        try {\n            sandboxUrl = uriBuilder.build();\n        } catch (URISyntaxException e) {\n            throw new MinimesosException(e.getMessage());\n        }\n        return sandboxUrl;\n    }\n\n    private Executor findExecutor(MesosAgent agent, Task task) {\n        String executorId = task.getExecutorId();\n        if (isBlank(executorId)) { // if executorId is empty, try with the taskId\n            executorId = task.getId();\n        }\n        for (Framework framework : agent.getState().getFrameworks()) {\n            if (framework.getId().equals(task.getFrameworkId())) {\n                for (Executor executor : framework.getExecutors()) {\n                    if (executor.getId().equals(executorId)) {\n                        return executor;\n                    }\n                }\n            }\n        }\n        return null;\n    }\n\n}\n"
  },
  {
    "path": "cli/src/main/java/com/containersol/minimesos/main/CommandPs.java",
    "content": "package com.containersol.minimesos.main;\n\nimport com.beust.jcommander.Parameters;\nimport com.containersol.minimesos.cluster.ClusterRepository;\nimport com.containersol.minimesos.cluster.MesosCluster;\nimport com.containersol.minimesos.mesos.MesosClusterContainersFactory;\nimport com.containersol.minimesos.state.Framework;\nimport com.containersol.minimesos.state.State;\nimport com.containersol.minimesos.state.Task;\n\nimport java.io.PrintStream;\n\n/**\n * Lists tasks on the cluster\n */\n@Parameters(separators = \"=\", commandDescription = \"List running tasks\")\npublic class CommandPs implements Command {\n\n    private static final String FORMAT = \"%-20s %-20s %-20s %-20s\\n\";\n\n    private static final Object[] COLUMNS = { \"FRAMEWORK\", \"TASK\", \"STATE\", \"PORT\" };\n\n    private ClusterRepository repository = new ClusterRepository();\n\n    private PrintStream output = System.out; // NOSONAR\n\n    public CommandPs(PrintStream output) {\n        this.output = output;\n    }\n\n    public CommandPs() {\n        // NOSONAR\n    }\n\n    @Override\n    public boolean validateParameters() {\n        return true;\n    }\n\n    @Override\n    public String getName() {\n        return \"ps\";\n    }\n\n    @Override\n    public void execute() {\n        MesosCluster cluster = repository.loadCluster(new MesosClusterContainersFactory());\n\n        if (cluster == null) {\n            output.println(\"Minimesos cluster is not running\");\n            return;\n        }\n\n        output.printf(FORMAT, COLUMNS);\n        State state = cluster.getMaster().getState();\n        for (Framework framework : state.getFrameworks()) {\n            for (Task task : framework.getTasks()) {\n                output.printf(FORMAT, framework.getName(), task.getName(), task.getState(), task.getDiscovery().getPorts().getPorts().get(0).getNumber());\n            }\n        }\n    }\n\n    public void setRepository(ClusterRepository repository) {\n        this.repository = repository;\n    }\n}\n"
  },
  {
    "path": "cli/src/main/java/com/containersol/minimesos/main/CommandState.java",
    "content": "package com.containersol.minimesos.main;\n\nimport java.io.PrintStream;\n\nimport com.beust.jcommander.Parameters;\nimport com.containersol.minimesos.cluster.ClusterRepository;\nimport com.containersol.minimesos.cluster.MesosCluster;\nimport com.containersol.minimesos.mesos.MesosClusterContainersFactory;\n\n/**\n * Parameters for the 'state' command\n */\n@Parameters(separators = \"=\", commandDescription = \"Display the master's state.json file\")\npublic class CommandState implements Command {\n\n    public static final String CLINAME = \"state\";\n\n    private PrintStream output = System.out; //NOSONAR\n\n    private ClusterRepository repository = new ClusterRepository();\n\n    public CommandState() { //NOSONAR\n    }\n\n    public CommandState(PrintStream ps) {\n        this.output = ps;\n    }\n\n    @Override\n    public void execute() {\n        MesosCluster cluster = repository.loadCluster(new MesosClusterContainersFactory());\n        if (cluster != null) {\n            cluster.state(output);\n        } else {\n            output.println(\"Minimesos cluster is not running\");\n        }\n    }\n\n    @Override\n    public boolean validateParameters() {\n        return true;\n    }\n\n    @Override\n    public String getName() {\n        return CLINAME;\n    }\n\n}\n"
  },
  {
    "path": "cli/src/main/java/com/containersol/minimesos/main/CommandUninstall.java",
    "content": "package com.containersol.minimesos.main;\n\nimport com.beust.jcommander.Parameter;\nimport com.beust.jcommander.Parameters;\nimport com.containersol.minimesos.MinimesosException;\nimport com.containersol.minimesos.cluster.ClusterRepository;\nimport com.containersol.minimesos.cluster.Marathon;\nimport com.containersol.minimesos.cluster.MesosCluster;\nimport com.containersol.minimesos.mesos.MesosClusterContainersFactory;\n\nimport java.io.PrintStream;\n\nimport static org.apache.commons.lang.StringUtils.isNotBlank;\n\n/**\n * Uninstalls a Marathon app or framework\n */\n@Parameters(separators = \"=\", commandDescription = \"Uninstall a Marathon app\")\npublic class CommandUninstall implements Command {\n\n    @Parameter(names = \"--app\", description = \"Marathon app to uninstall\")\n    private String app = null;\n\n    @Parameter(names = \"--group\", description = \"Marathon group to uninstall\")\n    private String group = null;\n\n    private ClusterRepository repository = new ClusterRepository();\n\n    private PrintStream output = System.out; // NOSONAR\n\n    CommandUninstall(PrintStream output) {\n        this.output = output;\n    }\n\n    CommandUninstall() {\n        // NOSONAR\n    }\n\n    @Override\n    public boolean validateParameters() {\n        return true;\n    }\n\n    @Override\n    public String getName() {\n        return \"uninstall\";\n    }\n\n    @Override\n    public void execute() {\n        MesosCluster cluster = repository.loadCluster(new MesosClusterContainersFactory());\n\n        if (cluster == null) {\n            output.println(\"Minimesos cluster is not running\");\n            return;\n        }\n\n        Marathon marathon = cluster.getMarathon();\n        if (marathon == null) {\n            throw new MinimesosException(\"Marathon container is not found in cluster \" + cluster.getClusterId());\n        }\n\n        if (isNotBlank(app) && isNotBlank(group)) {\n            output.println(\"Please specify --app or --group to uninstall an app or group\");\n            return;\n        }\n\n        if (isNotBlank(app)) {\n            try {\n                marathon.deleteApp(app);\n                output.println(\"Deleted app '\" + app + \"'\");\n            } catch (MinimesosException e) { // NOSONAR\n                output.println(e.getMessage());\n            }\n        } else if (isNotBlank(group)) {\n            try {\n                marathon.deleteGroup(group);\n                output.println(\"Deleted group '\" + group + \"'\");\n            } catch (MinimesosException e) { // NOSONAR\n                output.println(e.getMessage());\n            }\n        } else {\n            output.println(\"Please specify --app or --group to uninstall an app or group\");\n        }\n    }\n\n    public void setRepository(ClusterRepository repository) {\n        this.repository = repository;\n    }\n\n    void setApp(String app) {\n        this.app = app;\n    }\n\n    void setGroup(String group) {\n        this.group = group;\n    }\n}\n"
  },
  {
    "path": "cli/src/main/java/com/containersol/minimesos/main/CommandUp.java",
    "content": "package com.containersol.minimesos.main;\n\nimport java.io.InputStream;\nimport java.io.PrintStream;\n\nimport com.beust.jcommander.Parameter;\nimport com.beust.jcommander.Parameters;\nimport com.containersol.minimesos.MinimesosException;\nimport com.containersol.minimesos.cluster.ClusterRepository;\nimport com.containersol.minimesos.cluster.MesosCluster;\nimport com.containersol.minimesos.config.ClusterConfig;\nimport com.containersol.minimesos.config.ConfigParser;\nimport com.containersol.minimesos.config.MesosMasterConfig;\nimport com.containersol.minimesos.config.ZooKeeperConfig;\nimport com.containersol.minimesos.mesos.MesosClusterContainersFactory;\n\nimport org.apache.commons.io.IOUtils;\nimport org.slf4j.LoggerFactory;\n\n/**\n * Parameters for the 'up' command\n */\n@Parameters(separators = \"=\", commandDescription = \"Create a minimesos cluster\")\npublic class CommandUp implements Command {\n    private static final String MINIMESOS_CLUSTER_CONFIG = \"minimesos.file\";\n\n    private static final org.slf4j.Logger LOGGER = LoggerFactory.getLogger(CommandUp.class);\n\n    public static final String CLINAME = \"up\";\n\n    private ClusterRepository repository = new ClusterRepository();\n\n    @Parameter(names = \"--mapPortsToHost\", description = \"Map the Mesos, Marathon UI, Zookeeper and Consul ports to the host level (we recommend to enable this on Mac (e.g. when using docker-machine) and disable on Linux).\")\n    private Boolean mapPortsToHost = null;\n\n    @Parameter(names = \"--clusterConfig\", description = \"Path to file with cluster configuration. Defaults to minimesosFile\")\n    private String clusterConfigPath = ClusterConfig.DEFAULT_CONFIG_FILE;\n\n    private MesosCluster startedCluster = null;\n\n    private PrintStream output = System.out; //NOSONAR\n\n    private MesosClusterContainersFactory mesosClusterFactory;\n\n    public CommandUp() {\n        mesosClusterFactory = new MesosClusterContainersFactory();\n    }\n\n    public CommandUp(PrintStream ps) {\n        this();\n        this.output = ps;\n    }\n\n    public Boolean isMapPortsToHost() {\n        return mapPortsToHost;\n    }\n\n    public void setMapPortsToHost(Boolean mapPortsToHost) {\n        this.mapPortsToHost = mapPortsToHost;\n    }\n\n    public String getClusterConfigPath() {\n        String sp = System.getProperty(MINIMESOS_CLUSTER_CONFIG);\n        if (sp != null) {\n            return sp;\n        }\n        return clusterConfigPath;\n    }\n\n    public void setClusterConfigPath(String clusterConfigPath) {\n        this.clusterConfigPath = clusterConfigPath;\n    }\n\n    @Override\n    public void execute() {\n        LOGGER.debug(\"Executing up command\");\n\n        MesosCluster cluster = getCluster();\n        if (cluster != null) {\n            output.println(\"Cluster \" + cluster.getClusterId() + \" is already running\");\n            return;\n        }\n        ClusterConfig clusterConfig = readClusterConfigFromMinimesosFile();\n        updateWithParameters(clusterConfig);\n\n        startedCluster = mesosClusterFactory.createMesosCluster(clusterConfig);\n        // save cluster ID first, so it becomes available for 'destroy' even if its part failed to start\n        repository.saveClusterFile(startedCluster);\n\n        startedCluster.start();\n        startedCluster.waitForState(state -> state != null);\n\n        new CommandInfo(output).execute();\n    }\n\n    /**\n     * Reads ClusterConfig from minimesosFile.\n     *\n     * @return configuration of the cluster from the minimesosFile\n     * @throws MinimesosException if minimesosFile is not found or malformed\n     */\n    public ClusterConfig readClusterConfigFromMinimesosFile() {\n        InputStream clusterConfigFile = MesosCluster.getInputStream(getClusterConfigPath());\n        if (clusterConfigFile != null) {\n            ConfigParser configParser = new ConfigParser();\n            try {\n                return configParser.parse(IOUtils.toString(clusterConfigFile, \"UTF-8\"));\n            } catch (Exception e) {\n                String msg = String.format(\"Failed to load cluster configuration from %s: %s\", getClusterConfigPath(), e.getMessage());\n                throw new MinimesosException(msg, e);\n            }\n        }\n        throw new MinimesosException(\"No minimesosFile found in current directory. Please generate one with 'minimesos init'\");\n    }\n\n    /**\n     * Adjust cluster configuration according to CLI parameters\n     *\n     * @param clusterConfig cluster configuration to update\n     */\n    public void updateWithParameters(ClusterConfig clusterConfig) {\n        if (isMapPortsToHost() != null) {\n            clusterConfig.setMapPortsToHost(isMapPortsToHost());\n        }\n\n        if (clusterConfig.getZookeeper() == null) {\n            clusterConfig.setZookeeper(new ZooKeeperConfig());\n        }\n\n        if (clusterConfig.getMaster() == null) {\n            clusterConfig.setMaster(new MesosMasterConfig(ClusterConfig.DEFAULT_MESOS_VERSION));\n        }\n    }\n\n    public MesosCluster getCluster() {\n        return (startedCluster != null) ? startedCluster : repository.loadCluster(new MesosClusterContainersFactory());\n    }\n\n    @Override\n    public boolean validateParameters() {\n        return true;\n    }\n\n    @Override\n    public String getName() {\n        return CLINAME;\n    }\n\n    public void setMesosClusterFactory(MesosClusterContainersFactory mesosClusterFactory) {\n        this.mesosClusterFactory = mesosClusterFactory;\n    }\n\n}\n"
  },
  {
    "path": "cli/src/main/java/com/containersol/minimesos/main/CommandVersion.java",
    "content": "package com.containersol.minimesos.main;\n\nimport com.beust.jcommander.Parameters;\n\nimport java.io.PrintStream;\n\n/**\n * Command for printing the minimesos version\n */\n@Parameters(separators = \"=\", commandDescription = \"Display the version of minimesos\")\npublic class CommandVersion implements Command {\n\n    public static final String CLI_NAME = \"version\";\n\n    private PrintStream output = System.out; //NOSONAR\n\n    public CommandVersion() { // NOSONAR\n\n    }\n\n    public CommandVersion(PrintStream ps) {\n        this.output = ps;\n    }\n\n    @Override\n    public void execute() {\n        Package mainPackage = Main.class.getPackage();\n        String version = mainPackage.getImplementationVersion();\n        output.println(version);\n    }\n\n    @Override\n    public boolean validateParameters() {\n        return true;\n    }\n\n    @Override\n    public String getName() {\n        return CLI_NAME;\n    }\n}\n"
  },
  {
    "path": "cli/src/main/java/com/containersol/minimesos/main/Main.java",
    "content": "package com.containersol.minimesos.main;\n\nimport java.io.PrintStream;\nimport java.io.PrintWriter;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport com.beust.jcommander.JCommander;\nimport com.beust.jcommander.Parameter;\nimport com.beust.jcommander.Parameters;\nimport com.containersol.minimesos.MinimesosException;\nimport com.containersol.minimesos.cluster.ClusterRepository;\nimport com.containersol.minimesos.cluster.MesosCluster;\nimport com.containersol.minimesos.mesos.MesosClusterContainersFactory;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport ch.qos.logback.classic.Level;\nimport ch.qos.logback.classic.LoggerContext;\n\n/**\n * Main method for interacting with minimesos.\n */\n@Parameters(separators = \"=\", commandDescription = \"Global options\")\npublic class Main {\n\n    private static final Logger LOGGER = LoggerFactory.getLogger(Main.class);\n\n    private static final int EXIT_CODE_OK = 0;\n    private static final int EXIT_CODE_ERR = 1;\n\n    @Parameter(names = {\"--help\", \"-help\", \"-?\", \"-h\"}, description = \"Show help\")\n    private boolean help = false;\n\n    @Parameter(names = \"--debug\", description = \"Enable debug logging.\")\n    private boolean debug = false;\n\n    private PrintStream output = System.out; //NOSONAR\n\n    private final JCommander jc = new JCommander(this);\n\n    private HashMap<String, Command> commands = new HashMap<>();\n\n    private ClusterRepository repository = new ClusterRepository();\n\n    public static void main(String[] args) {\n        Main main = new Main();\n        main.addCommand(new CommandUp());\n        main.addCommand(new CommandDestroy());\n        main.addCommand(new CommandHelp());\n        main.addCommand(new CommandInstall());\n        main.addCommand(new CommandUninstall());\n        main.addCommand(new CommandState());\n        main.addCommand(new CommandInfo());\n        main.addCommand(new CommandInit());\n        main.addCommand(new CommandPs());\n        main.addCommand(new CommandVersion());\n        main.addCommand(new CommandLogs());\n        try {\n            int rc = main.run(args);\n            if (EXIT_CODE_OK != rc) {\n                System.exit(rc);\n            }\n        } catch (MinimesosException mme) {\n            if (main.debug) {\n                LOGGER.error(\"An error, which was handled, occurred\", mme);\n            } else {\n                LOGGER.error(mme.getMessage());\n            }\n            System.exit(EXIT_CODE_ERR);\n        }\n    }\n\n\n    public void setOutput(PrintStream output) {\n        this.output = output;\n    }\n\n    int run(String[] args) {\n        initJCommander();\n\n        try {\n            parseParams(jc, args);\n\n            if (help) {\n                printUsage(null);\n                return EXIT_CODE_OK;\n            }\n\n            if (debug) {\n                initializeDebugLogging();\n            }\n\n            if (jc.getParsedCommand() == null) {\n                return handleNoCommand();\n            }\n\n            if (!commands.containsKey(jc.getParsedCommand())) {\n                LOGGER.error(\"Unknown command: \" + jc.getParsedCommand());\n                return EXIT_CODE_ERR;\n            }\n\n            Command parsedCommand = commands.get(jc.getParsedCommand());\n            if (CommandHelp.CLINAME.equals(parsedCommand.getName())) {\n                printUsage(null);\n            } else {\n                if (parsedCommand.validateParameters()) {\n                    parsedCommand.execute();\n                } else {\n                    printUsage(jc.getParsedCommand());\n                    return EXIT_CODE_ERR;\n                }\n            }\n\n            return EXIT_CODE_OK;\n        } catch (Exception ex) {\n            PrintWriter writer = new PrintWriter(output);\n\n            output.print(\"Failed to run command '\" + jc.getParsedCommand() + \"'.\");\n\n            if (ex.getMessage() != null) {\n                output.print(\" \" + ex.getMessage() + \".\\n\");\n            } else {\n                ex.printStackTrace(writer);\n            }\n\n            writer.close();\n\n            LOGGER.debug(\"Exception while processing\", ex);\n\n            return EXIT_CODE_ERR;\n        }\n    }\n\n    private void initJCommander() {\n        jc.setProgramName(\"minimesos\");\n        for (Map.Entry<String, Command> entry : commands.entrySet()) {\n            jc.addCommand(entry.getKey(), entry.getValue());\n        }\n    }\n\n    private void parseParams(JCommander jc, String[] args) {\n        try {\n            jc.parse(args);\n        } catch (Exception e) {\n            LOGGER.error(\"Failed to parse parameters. \" + e.getMessage() + \"\\n\");\n        }\n    }\n\n    private static void initializeDebugLogging() {\n        LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory();\n        ch.qos.logback.classic.Logger rootLogger = (\n            loggerContext.getLogger(\"com.containersol.minimesos\")\n        );\n        rootLogger.setLevel(Level.DEBUG);\n        LOGGER.debug(\"Initialized debug logging\");\n    }\n\n    private int handleNoCommand() {\n        MesosCluster cluster = repository.loadCluster(new MesosClusterContainersFactory());\n        if (cluster != null) {\n            new CommandInfo().execute();\n            return EXIT_CODE_OK;\n        } else {\n            printUsage(null);\n            return EXIT_CODE_ERR;\n        }\n    }\n\n    private void printUsage(String commandName) {\n        StringBuilder builder = new StringBuilder();\n        if (commandName != null) {\n            jc.usage(commandName, builder);\n        } else {\n            jc.usage(builder);\n        }\n        output.println(builder.toString());\n    }\n\n    void addCommand(Command command) {\n        commands.put(command.getName(), command);\n    }\n\n}\n"
  },
  {
    "path": "cli/src/test/java/com/containersol/minimesos/main/CommandInstallTest.java",
    "content": "package com.containersol.minimesos.main;\n\nimport com.containersol.minimesos.cluster.ClusterRepository;\nimport com.containersol.minimesos.cluster.Marathon;\nimport com.containersol.minimesos.cluster.MesosCluster;\nimport org.apache.commons.io.IOUtils;\nimport org.junit.Before;\nimport org.junit.Test;\n\nimport java.io.FileReader;\nimport java.io.IOException;\n\nimport static org.mockito.Matchers.any;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.when;\n\npublic class CommandInstallTest {\n\n    private Marathon marathon;\n\n    private MesosCluster mesosCluster;\n\n    private ClusterRepository repository;\n\n    private CommandInstall command;\n\n    @Before\n    public void before() {\n        marathon = mock(Marathon.class);\n\n        mesosCluster = mock(MesosCluster.class);\n        when(mesosCluster.getMarathon()).thenReturn(marathon);\n\n        repository = mock(ClusterRepository.class);\n        when(repository.loadCluster(any())).thenReturn(mesosCluster);\n\n        command = new CommandInstall();\n        command.repository = repository;\n    }\n\n    @Test\n    public void testInstallMarathonFile() throws IOException {\n        // Given\n        command.marathonFile = \"src/test/resources/app.json\";\n\n        // When\n        command.execute();\n\n        // Then\n        verify(marathon).deployApp(IOUtils.toString(new FileReader(command.marathonFile)));\n    }\n\n    @Test\n    public void testInstallMarathonApp() throws IOException {\n        // Given\n        command.app = \"src/test/resources/app.json\";\n\n        // When\n        command.execute();\n\n        // Then\n        verify(marathon).deployApp(IOUtils.toString(new FileReader(command.app)));\n    }\n\n    @Test\n    public void testInstallMarathonGroup() throws IOException {\n        // Given\n        command.group = \"src/test/resources/group.json\";\n\n        // When\n        command.execute();\n\n        // Then\n        verify(marathon).deployGroup(IOUtils.toString(new FileReader(command.group)));\n    }\n\n}\n"
  },
  {
    "path": "cli/src/test/java/com/containersol/minimesos/main/MainTest.java",
    "content": "package com.containersol.minimesos.main;\n\nimport org.apache.commons.io.output.ByteArrayOutputStream;\nimport org.junit.Before;\nimport org.junit.Test;\n\nimport java.io.IOException;\nimport java.io.PrintStream;\n\nimport static junit.framework.TestCase.assertFalse;\nimport static org.junit.Assert.assertTrue;\nimport static org.mockito.Mockito.doNothing;\nimport static org.mockito.Mockito.spy;\nimport static org.mockito.Mockito.verify;\n\npublic class MainTest {\n\n    private ByteArrayOutputStream outputStream;\n    private Main main;\n\n    private CommandInfo commandInfo;\n    private CommandDestroy commandDestroy;\n    private CommandState commandState;\n    private CommandInstall commandInstall;\n    private CommandUp commandUp;\n    private CommandLogs commandLogs;\n\n    @Before\n    public void before() {\n\n        outputStream = new ByteArrayOutputStream();\n        PrintStream ps = new PrintStream(outputStream, true);\n\n        commandUp = spy(new CommandUp());\n        doNothing().when(commandUp).execute();\n\n        commandDestroy = spy(new CommandDestroy());\n        doNothing().when(commandDestroy).execute();\n\n        commandInfo = spy(new CommandInfo(ps));\n        doNothing().when(commandInfo).execute();\n\n        commandState = spy(new CommandState(ps));\n        doNothing().when(commandState).execute();\n\n        commandInstall = spy(new CommandInstall());\n        doNothing().when(commandInstall).execute();\n\n        commandLogs = spy(new CommandLogs());\n        doNothing().when(commandLogs).execute();\n\n        main = new Main();\n        main.setOutput(ps);\n        main.addCommand(commandUp);\n        main.addCommand(commandDestroy);\n        main.addCommand(commandInfo);\n        main.addCommand(commandState);\n        main.addCommand(commandInstall);\n        main.addCommand(commandLogs);\n        main.addCommand(new CommandHelp());\n\n    }\n\n    @Test\n    public void testUp() {\n        main.run(new String[]{\"up\"});\n        verify(commandUp).execute();\n    }\n\n    @Test\n    public void testDestroy() {\n        main.run(new String[]{\"destroy\"});\n        verify(commandDestroy).execute();\n    }\n\n    @Test\n    public void testInfo() throws IOException {\n        main.run(new String[]{\"info\"});\n        verify(commandInfo).execute();\n    }\n\n    @Test\n    public void testState() throws IOException {\n        main.run(new String[]{\"state\"});\n        verify(commandState).execute();\n    }\n\n    @Test\n    public void testInstall() throws IOException {\n        main.run(new String[]{\"install\", \"--marathonFile\", \"bla.json\"});\n        verify(commandInstall).execute();\n    }\n\n    @Test\n    public void testLogs() throws IOException {\n        main.run(new String[]{\"logs\", \"--task\", \"something\"});\n        verify(commandLogs).execute();\n    }\n\n    @Test\n    public void testLogsNoParameter() throws IOException {\n        main.run(new String[]{\"logs\", \"--task\"});\n        String result = outputStream.toString(\"UTF-8\");\n        assertTrue(result.contains(\"Usage: logs [options]\"));\n    }\n\n    @Test\n    public void testUnsupportedCommand() throws IOException {\n        main.run(new String[]{\"unsupported\"});\n        String result = outputStream.toString(\"UTF-8\");\n        assertUsageText(result);\n    }\n\n    @Test\n    public void testMinusMinusHelp() throws IOException {\n        main.run(new String[]{\"--help\"});\n        String result = outputStream.toString(\"UTF-8\");\n        assertUsageText(result);\n    }\n\n    @Test\n    public void testInstallNoParameters() throws IOException {\n        main.run(new String[]{\"install\"});\n        String output = outputStream.toString(\"UTF-8\");\n        assertTrue(output.contains(\"Usage: install [options]\"));\n    }\n\n    @Test\n    public void testHelp() throws IOException {\n        main.run(new String[]{\"help\"});\n        String result = outputStream.toString(\"UTF-8\");\n        assertUsageText(result);\n    }\n\n    @Test\n    public void testNonExistingCommand() throws IOException {\n        main.run(new String[]{\"foo\"});\n        String result = outputStream.toString(\"UTF-8\");\n        assertUsageText(result);\n        assertFalse(result.contains(\"Failed to run command 'null'. null\"));\n    }\n\n    private static void assertUsageText(String output) {\n        assertTrue(output.contains(\"Usage: minimesos [options] [command] [command options]\"));\n        assertTrue(output.contains(\"Options:\"));\n        assertTrue(output.contains(\"Commands:\"));\n        assertTrue(output.contains(\"Usage: up [options]\"));\n        assertTrue(output.contains(\"Usage: install [options]\"));\n        assertTrue(output.contains(\"Usage: state [options]\"));\n    }\n\n}\n"
  },
  {
    "path": "cli/src/test/resources/app.json",
    "content": "{\n  \"id\": \"hello\",\n  \"container\": {\n    \"type\": \"MESOS\",\n    \"docker\": {\n      \"image\": \"weaveworks/\",\n      \"network\": \"HOST\"\n    }\n  },\n  \"cpus\": 0.1,\n  \"mem\": 16.0,\n  \"instances\": 1\n}\n"
  },
  {
    "path": "cli/src/test/resources/group.json",
    "content": "{\n  \"id\": \"pingPongGroup\",\n  \"groups\": [\n    {\n      \"id\": \"ping\",\n      \"cmd\": \"echo 'ping'\",\n      \"cpus\": 0.1,\n      \"mem\": 16.0,\n      \"instances\": 1\n    }\n   ,{\n      \"id\": \"pong\",\n      \"cmd\": \"echo 'pong'\",\n      \"cpus\": 0.1,\n      \"mem\": 16.0,\n      \"instances\": 1\n   }\n  ]\n}\n"
  },
  {
    "path": "docs/index.md",
    "content": "# minimesos introduction\n\nThe experimentation and testing tool for Apache Mesos. `minimesos` is a tool created for a quick and easy creation of a Mesos cluster. This is achieved by running Mesos processes in Docker containers. `minimesos` implements simple to remember and discover CLI commands that allow creating and destroying local Mesos cluster in seconds.\n\nIf you have used Vagrant and Docker before, the set of the commands will be very familiar to you, if you have not - don't worry! We will walk you through them.\n\n## Resources\n\n - Website https://minimesos.org/\n - Blog https://minimesos.org/blog\n - Interactive tutorial https://minimesos.org/try\n\n\n## System Requirements\n\nminimesos runs Docker containers with a configurable version version of Mesos. See the [minimesos-docker](https://github.com/ContainerSolutions/minimesos-docker) repository\nwith an overview of the images supported by minimesos.\n\nThe Docker client in these Mesos images should be able to talk to Docker daemon on your host machine. The Docker daemon is expected to run version 1.11.0 or higher\nof Docker or Docker Machine. See Docker [API compatibility](https://docs.docker.com/engine/reference/api/docker_remote_api/) table.\n\n\n\n## Installing\n\n```\n$ curl -sSL https://minimesos.org/install | sh\n```\n\nThis installs the minimesos binary into ``${HOME}/.minimesos/bin``\n\nYou can add it to your executables search path using following command:\n```\n$ export PATH=$PATH:$HOME/.minimesos/bin\n```\n\nOnce the installation has been successful, let's try running ```minimesos --help```\nThis should print the list of all possible commands and command line arguments.\n\n\nThese are the options you might want to change to configure your cluster.\n\n## Command line interface\n\n```\nUsage: minimesos [options] [command] [command options]\n  Options:\n    --debug\n       Enable debug logging.\n       Default: false\n    --help, -help, -?, -h\n       Show help\n       Default: false\n  Commands:\n    help      Display help\n      Usage: help [options]\n\n    init      Initialize a minimesosFile\n      Usage: init [options]\n\n    install      Install a framework with Marathon\n      Usage: install [options]\n        Options:\n          --marathonFile\n             Marathon JSON app install file location. Either this or --stdin\n             parameter must be used\n          --stdin\n             Use JSON from standard import. Allow piping JSON from other\n             processes. Either this or --marathonFile parameter must be used\n             Default: false\n          --update\n             Update a running application instead of attempting to deploy a new\n             application\n             Default: false\n\n    destroy      Destroy a minimesos cluster\n      Usage: destroy [options]\n\n    up      Create a minimesos cluster\n      Usage: up [options]\n        Options:\n          --clusterConfig\n             Path to file with cluster configuration. Defaults to minimesosFile\n             Default: minimesosFile\n          --mapPortsToHost\n             Map the Mesos and Marathon UI ports on the host level (we\n             recommend to enable this on Mac (e.g. when using docker-machine) and disable\n             on Linux).\n             Default: false\n          --num-agents\n             Number of agents to start\n             Default: -1\n          --timeout\n             Time to wait for a container to get responsive, in seconds.\n             Default: 60\n\n    state      Display state.json file of a master or an agent\n      Usage: state [options]\n        Options:\n          --agent\n             Specify an agent to query, otherwise query a master\n             Default: <empty string>\n\n    info      Display cluster information\n      Usage: info [options]\n```\n\n## minimesosFile and ```minimesos init```\nminimesos config is stored in `minimesosFile`, the file that is generated with sensible defaults when running ```minimesos init```\n\nAgain, you might notice similarity with ```vagrant init``` and Vagrantfile.\nOpen the minimesosFile and let's look at the list of the blocks.\n\nThe configuration file is a list of blocks, logically grouped by curly brackets ```{ }```\nScalar values are simple key-value strings.\n\n| Option name           | type    | Meaning                                                                            |\n|-----------------------|---------|------------------------------------------------------------------------------------|\n| clusterName           | String  | The name of the Mesos cluster                                                      |\n| mapPortsToHost        | Boolean | Whether to map container ports to the host network                                     |\n| loggingLevel          | String  | Debug level in the terminal output                                                 |\n| mapAgentSandboxVolume | Boolean | Creates a volume mapping to the agent sandbox under ${PWD}/.minimesos/sandbox-.../ |\n| mesosVersion          | String  | Mesos version                                                                      |\n| timeout               | Integer | Amount of seconds to wait for the cluster to become alive before giving up         |\n| agent                 | Block   | Describes a single instance of a mesos agent                                       |\n| agent resources       | Block   | Describes resources of the mesos agent                                             |\n| agent resources cpu   | Block   | Describes CPU resources                                                            |\n| agent resources mem   | Block   | Describes memory resources                                                         |\n| agent resources ports | Block   | Describes network ports resources                                                  |\n\n## Consul and registrator\n\nBy default, minimesos starts consul and registrator containers giving you ability to configure service discovery.\n\n## Mesos DNS\n\nMesos DNS registers Mesos processes and frameworks in its DNS server\n\n## Java API\n\nIn this snippet we're configuring the Mesos cluster to start 3 agents with different resources.\n\n```\npublic class MesosClusterTest {\n\n    @ClassRule\n    public static MesosClusterTestRule testRule =\n        MesosClusterTestRule.fromFile(\"src/test/resources/configFiles/testMinimesosFile\");\n\n    public static MesosCluster cluster = testRule.getMesosCluster();\n\n    @Test\n    public void mesosClusterCanBeStarted() throws Exception {\n        JSONObject stateInfo = cluster.getStateInfoJSON();\n        Assert.assertEquals(3, stateInfo.getInt(\"activated_slaves\"));\n        Assert.assertTrue(cluster.getMesosMasterURL().contains(\":5050\"));\n    }\n}\n```\n## TDD for Mesos frameworks\n\nA possible testing scenario could be:\n\n 1. In the test setup launch the Mesos cluster container\n 2. Call the scheduler directly from your test and point to Zookeeper to detect the master or passing the master URL directly.\n 3. The scheduler launches a task on a suitable agent.\n 4. Poll the state of the Mesos cluster to verify that you framework is running\n 5. The test utilities take care of stopping and removing the Mesos cluster\n\n![minimesos](minimesos.png?raw=true \"minimesos\")\n\n![Creative Commons Licence](cc-cc.png \"Creative Commons Licence\") Licenced under CC BY [remember to play](http://remembertoplay.co/) in collaboration with [Container Solutions](http://www.container-solutions.com/)\n\n## Building and running on MAC with Docker Machine\n\n### Install DockerToolbox (including Docker Machine)\n\nDownload package from <https://www.docker.com/docker-toolbox> and install it.\nTested with [DockerToolbox-1.9.0d.pkg](https://github.com/docker/toolbox/releases/download/v1.9.0d/DockerToolbox-1.9.0d.pkg)\n\n### Creating VM for minimesos\n\nCreate a docker machine, make sure its environment variables are visible to the test, ensure the docker containers' IP addresses are available on the host\n\n```\n$ docker-machine create -d virtualbox --virtualbox-memory 8192 --virtualbox-cpu-count 1 --engine-opt dns=8.8.8.8 minimesos\n$ eval $(docker-machine env minimesos)\n```\n\nWhen VM is ready you can either *build latest version* of minimesos or *install a released version*\n\n### Building latest version of minimesos\n\nIn a terminal window, run the following commands:\n\n```\n# changing route is required to let Java process on host to find minimesos in virtual machine.\n$ sudo route delete 172.17.0.0/16; sudo route -n add 172.17.0.0/16 $(docker-machine ip ${DOCKER_MACHINE_NAME})\n$ ./gradlew clean build --info --stacktrace\n```\n\nIn Idea, add the ```docker-machine env minimesos``` variables to the Idea junit testing dialog. E.g.\n\n```\nDOCKER_TLS_VERIFY=1\nDOCKER_HOST=tcp://192.168.99.100:2376\nDOCKER_CERT_PATH=/home/user/.docker/machine/machines/minimesos\n```\n\nOne of the minimesos build results is new docker image. E.g.\n\n```\n$ docker images\nREPOSITORY                      TAG                     IMAGE ID            CREATED             VIRTUAL SIZE\ncontainersol/minimesos-cli      latest                  cf854cfb1865        2 minutes ago       529.3 MB\n```\n\nRunning ```./gradlew install``` will make latest version of minimesos script available on the PATH\n\n### Running minimesos from CLI\n\nTo create minimesos cluster execute ```minimesos up```. It will create temporary container with minimesos process, which will start other containers and will exit.\nWhen cluster is started ```.minimesos/minimesos.cluster``` file with cluster ID is created in local directory. This cluster is destroyed with ```minimesos destroy```\n\n```\n$ minimesos init\nInitialized minimesosFile in this directory\n\n$ minimesos up\nMinimesos cluster is running: 3878417609\nMesos version: 1.0.0\nexport MINIMESOS_NETWORK_GATEWAY=172.17.0.1\nexport MINIMESOS_AGENT=http://172.17.0.5:5051; export MINIMESOS_AGENT_IP=172.17.0.5\nexport MINIMESOS_ZOOKEEPER=zk://172.17.0.3:2181/mesos; export MINIMESOS_ZOOKEEPER_IP=172.17.0.3\nexport MINIMESOS_MARATHON=http://172.17.0.6:8080; export MINIMESOS_MARATHON_IP=172.17.0.6\nexport MINIMESOS_CONSUL=http://172.17.0.7:8500; export MINIMESOS_CONSUL_IP=172.17.0.7\nexport MINIMESOS_MASTER=http://172.17.0.4:5050; export MINIMESOS_MASTER_IP=172.17.0.4\n\n$ minimesos state | jq \".version\"\n1.0.0\n\n$ minimesos destroy\nDestroyed minimesos cluster 3878417609\n```\n\nThe `minimesos up` command supports `--mapPortsToHost` flag, that automatically binds Mesos and Marathon ports `5050`, resp. `8080` to the host machine, providing you with easy access to the services. Let the following table explain what the host machine is in different contexts:\n\n| --mapPortsToHost   | Linux                            | OS X                                |\n|--------------------|----------------------------------|-------------------------------------|\n| disabled           | container IP addresses (default) | n/a                                 |\n| enabled            | host computer                    | docker-machine IP address (default) |\n\nHaving `--mapPortsToHost` enabled on Linux makes minimesos containers effectively accessible to anyone who has network access to your computer.\nWe don't recommend this. Not using `--mapPortsToHost` flag on Max OS X on the other hand makes the containers inaccessible, because they run inside another virtual machine. This machine is typically managed by `docker-machine`.\nMinimesos tries to choose the appropriate configuration for your system automatically.\n\nAn other alternative if you use docker-machine, is to access the reported IP address in browser, it's necessary to add routing of docker IP range to IP address of the docker machine\n\n```\nsudo route delete 172.17.0.0/16; sudo route -n add 172.17.0.0/16 $(docker-machine ip ${DOCKER_MACHINE_NAME})\n```\n\n### Volume maappings\n\nThe table below show the volume mappings, on the host, on Docker machine and in the minimesos container.\n\n| OSX Host        | Docker Machine        | minimesos container           |\n| --------------- | --------------------- | ----------------------------- |\n| $PWD/.minimesos | $PWD/.minimesos       | /tmp/.minimesos               |\n|                 | /var/lib/docker       | /var/lib/docker               |\n|                 | /var/run/docker.sock  | /var/run/docker.sock          |\n|                 | /usr/local/bin/docker | /usr/local/bin/docker         |\n|                 | /sys/fs/cgroup        | /sys/fs/cgroup                |\n\n\n## Caveats\n\n`minimesos up` command supports `--mesosImageTag` parameter, which can be used to override the version of Mesos to be used.\nWhen running an older version of Mesos, you may encounter [compatibility issues between Mesos 0.22 and Docker v. greater than 1.7](https://issues.apache.org/jira/browse/INFRA-10621).\n\nSince version 0.3.0 minimesos uses 'flat' container structure, which means that all containers (agents, master, zookeeper) as well as all Docker executor tasks are run in the same Docker context - the host machine.\nThis has following benefits:\n  1. Shared repository with the host Docker\n  2. Transparency of your test cluster.\n  3. Ability to keep track of executor tasks\n  4. Easy access to the logs\n\nHowever, you should account for this when developing a Mesos framework.\nBy default, Mesos starts Docker containerized executor tasks with the ```--host``` mode.\nLibprocess tries to bind on a loopback interface and fails to establish communication with the master node.\n\nTo work around this, start the executor using [```--bridge``` mode](https://issues.apache.org/jira/browse/MESOS-1621) and provide LIBPROCESS_IP environment variable with the IP address of the executor container, for example using this:\n\n```\nexport LIBPROCESS_IP=$(ifconfig | grep -Eo 'inet (addr:)?([0-9]*\\.){3}[0-9]*' | grep -Eo '([0-9]*\\.){3}[0-9]*' | grep -v '127.0.0.1' | head -n 1)\n\n```\n\nThis ensures your executor task will be assigned an interface to allow communication within the cluster.\n"
  },
  {
    "path": "gradle/quality.gradle",
    "content": "apply plugin: 'findbugs'\napply plugin: 'checkstyle'\napply plugin: 'pmd'\n\ntasks.withType(FindBugs) {\n  excludeFilter = file(\"$rootProject.projectDir/config/findbugs/excludeFilter.xml\")\n}\n\ncheckstyle {\n    toolVersion = \"6.6\"\n}\n\npmd {\n    toolVersion = \"5.1.3\"\n    ruleSets = [\n            'java-basic',\n            'java-braces',\n            'java-clone',\n            'java-codesize',\n\t        'java-finalizers'\n    ]\n}"
  },
  {
    "path": "gradle/spock.gradle",
    "content": "// used for unit tests\napply plugin: 'groovy'\n\ndef spockVersion = '1.0-groovy-2.4'\ndef powermockVersion = \"1.6.1\"\n\ndependencies {\n\n    testCompile \"org.codehaus.groovy:groovy-all:2.4.1\"\n    testCompile \"org.spockframework:spock-core:$spockVersion\"\n\n    testCompile 'cglib:cglib-nodep:2.2.2'               // need to mock classes\n\n//    // useful to mock out statics and final classes in Java.\n//    testCompile \"org.powermock:powermock-module-junit4:$powermockVersion\"\n//    testCompile \"org.powermock:powermock-module-junit4-rule:$powermockVersion\"\n//    testCompile \"org.powermock:powermock-classloading-xstream:$powermockVersion\"\n//    testCompile \"org.powermock:powermock-api-mockito:$powermockVersion\"\n}\n\n// for spock to live in test java tree\nsourceSets {\n    test {\n        groovy { srcDir 'src/test/java' }\n    }\n}\n\n"
  },
  {
    "path": "gradle/wrapper/gradle-wrapper.properties",
    "content": "#Thu Apr 21 17:15:12 CEST 2016\ndistributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-2.14-bin.zip\n"
  },
  {
    "path": "gradle.properties",
    "content": "org.gradle.jvmargs=-Xmx1024M -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8\nrelease.useAutomaticVersion = false\nversion=0.14.0\n# faster builds: gradle build -x findBugsM\n\n"
  },
  {
    "path": "gradlew",
    "content": "#!/usr/bin/env bash\n\n##############################################################################\n##\n##  Gradle start up script for UN*X\n##\n##############################################################################\n\n# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nDEFAULT_JVM_OPTS=\"\"\n\nAPP_NAME=\"Gradle\"\nAPP_BASE_NAME=`basename \"$0\"`\n\n# Use the maximum available, or set MAX_FD != -1 to use that value.\nMAX_FD=\"maximum\"\n\nwarn ( ) {\n    echo \"$*\"\n}\n\ndie ( ) {\n    echo\n    echo \"$*\"\n    echo\n    exit 1\n}\n\n# OS specific support (must be 'true' or 'false').\ncygwin=false\nmsys=false\ndarwin=false\ncase \"`uname`\" in\n  CYGWIN* )\n    cygwin=true\n    ;;\n  Darwin* )\n    darwin=true\n    ;;\n  MINGW* )\n    msys=true\n    ;;\nesac\n\n# Attempt to set APP_HOME\n# Resolve links: $0 may be a link\nPRG=\"$0\"\n# Need this for relative symlinks.\nwhile [ -h \"$PRG\" ] ; do\n    ls=`ls -ld \"$PRG\"`\n    link=`expr \"$ls\" : '.*-> \\(.*\\)$'`\n    if expr \"$link\" : '/.*' > /dev/null; then\n        PRG=\"$link\"\n    else\n        PRG=`dirname \"$PRG\"`\"/$link\"\n    fi\ndone\nSAVED=\"`pwd`\"\ncd \"`dirname \\\"$PRG\\\"`/\" >/dev/null\nAPP_HOME=\"`pwd -P`\"\ncd \"$SAVED\" >/dev/null\n\nCLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar\n\n# Determine the Java command to use to start the JVM.\nif [ -n \"$JAVA_HOME\" ] ; then\n    if [ -x \"$JAVA_HOME/jre/sh/java\" ] ; then\n        # IBM's JDK on AIX uses strange locations for the executables\n        JAVACMD=\"$JAVA_HOME/jre/sh/java\"\n    else\n        JAVACMD=\"$JAVA_HOME/bin/java\"\n    fi\n    if [ ! -x \"$JAVACMD\" ] ; then\n        die \"ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nelse\n    JAVACMD=\"java\"\n    which java >/dev/null 2>&1 || die \"ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\nfi\n\n# Increase the maximum file descriptors if we can.\nif [ \"$cygwin\" = \"false\" -a \"$darwin\" = \"false\" ] ; then\n    MAX_FD_LIMIT=`ulimit -H -n`\n    if [ $? -eq 0 ] ; then\n        if [ \"$MAX_FD\" = \"maximum\" -o \"$MAX_FD\" = \"max\" ] ; then\n            MAX_FD=\"$MAX_FD_LIMIT\"\n        fi\n        ulimit -n $MAX_FD\n        if [ $? -ne 0 ] ; then\n            warn \"Could not set maximum file descriptor limit: $MAX_FD\"\n        fi\n    else\n        warn \"Could not query maximum file descriptor limit: $MAX_FD_LIMIT\"\n    fi\nfi\n\n# For Darwin, add options to specify how the application appears in the dock\nif $darwin; then\n    GRADLE_OPTS=\"$GRADLE_OPTS \\\"-Xdock:name=$APP_NAME\\\" \\\"-Xdock:icon=$APP_HOME/media/gradle.icns\\\"\"\nfi\n\n# For Cygwin, switch paths to Windows format before running java\nif $cygwin ; then\n    APP_HOME=`cygpath --path --mixed \"$APP_HOME\"`\n    CLASSPATH=`cygpath --path --mixed \"$CLASSPATH\"`\n    JAVACMD=`cygpath --unix \"$JAVACMD\"`\n\n    # We build the pattern for arguments to be converted via cygpath\n    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`\n    SEP=\"\"\n    for dir in $ROOTDIRSRAW ; do\n        ROOTDIRS=\"$ROOTDIRS$SEP$dir\"\n        SEP=\"|\"\n    done\n    OURCYGPATTERN=\"(^($ROOTDIRS))\"\n    # Add a user-defined pattern to the cygpath arguments\n    if [ \"$GRADLE_CYGPATTERN\" != \"\" ] ; then\n        OURCYGPATTERN=\"$OURCYGPATTERN|($GRADLE_CYGPATTERN)\"\n    fi\n    # Now convert the arguments - kludge to limit ourselves to /bin/sh\n    i=0\n    for arg in \"$@\" ; do\n        CHECK=`echo \"$arg\"|egrep -c \"$OURCYGPATTERN\" -`\n        CHECK2=`echo \"$arg\"|egrep -c \"^-\"`                                 ### Determine if an option\n\n        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition\n            eval `echo args$i`=`cygpath --path --ignore --mixed \"$arg\"`\n        else\n            eval `echo args$i`=\"\\\"$arg\\\"\"\n        fi\n        i=$((i+1))\n    done\n    case $i in\n        (0) set -- ;;\n        (1) set -- \"$args0\" ;;\n        (2) set -- \"$args0\" \"$args1\" ;;\n        (3) set -- \"$args0\" \"$args1\" \"$args2\" ;;\n        (4) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" ;;\n        (5) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" ;;\n        (6) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" ;;\n        (7) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" ;;\n        (8) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" \"$args7\" ;;\n        (9) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" \"$args7\" \"$args8\" ;;\n    esac\nfi\n\n# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules\nfunction splitJvmOpts() {\n    JVM_OPTS=(\"$@\")\n}\neval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS\nJVM_OPTS[${#JVM_OPTS[*]}]=\"-Dorg.gradle.appname=$APP_BASE_NAME\"\n\nexec \"$JAVACMD\" \"${JVM_OPTS[@]}\" -classpath \"$CLASSPATH\" org.gradle.wrapper.GradleWrapperMain \"$@\"\n"
  },
  {
    "path": "minimesos/build.gradle",
    "content": "apply plugin: \"groovy\"\n\nsourceSets {\n    main {\n        groovy {\n            // this makes the groovy-compiler compile groovy- as well as java-files.\n            // Needed, because java is normally compiled before groovy.\n            // Since we are using groovy objects from java, we need it the other way round.\n            srcDirs = ['src/main/groovy', 'src/main/java']\n        }\n        java {\n            srcDirs = [] // don't compile Java code twice\n        }\n    }\n\n    integrationTest {\n        java {\n            compileClasspath += main.output + test.output\n            runtimeClasspath += main.output + test.output\n            srcDir file('src/integration-test/java')\n        }\n        resources.srcDir file('src/integration-test/resources')\n    }\n}\n\ndependencies {\n    compile 'org.codehaus.groovy:groovy-all:2.4.5'\n    compile 'com.github.docker-java:docker-java:3.0.7'\n    compile 'junit:junit:4.11'\n    compile 'com.jayway.awaitility:awaitility:1.6.3'\n    compile 'com.mashape.unirest:unirest-java:1.4.8'\n    compile 'org.slf4j:slf4j-api:1.7.12'\n    compile 'com.mesosphere:marathon-client:0.3.0'\n    compile 'com.google.code.gson:gson-parent:2.8.0'\n\n    compile 'ch.qos.logback:logback-core:1.1.3'\n    compile 'ch.qos.logback:logback-classic:1.1.3'\n\n    compile 'com.beust:jcommander:1.48'\n\n    testCompile \"org.mockito:mockito-core:1.+\"\n    // using guru.nidi as maintenanance of the original project is dropped https://github.com/clarkware/jdepend/pull/9\n    testCompile \"guru.nidi:jdepend:2.9.5\"\n\n    integrationTestCompile 'junit:junit:4.11'\n    integrationTestCompile 'com.jayway.awaitility:awaitility:1.6.3'\n}\n\ncompileGroovy {\n    options.compilerArgs << \"-Xlint:unchecked\" << \"-Xlint:deprecation\"\n}\n\nconfigurations {\n    integrationTestCompile.extendsFrom mainCompile\n    integrationTestCompile.extendsFrom testCompile\n    integrationTestRuntime.extendsFrom mainRuntime\n    integrationTestRuntime.extendsFrom testRuntime\n}\n\ntask integrationTest(type: Test) {\n    testClassesDir = sourceSets.integrationTest.output.classesDir\n    classpath = sourceSets.integrationTest.runtimeClasspath\n    testLogging {\n        showStandardStreams = true\n    }\n}\n\ntask installMinimesosScript(type: Copy) {\n    from \"$rootDir/bin/minimesos\"\n    into \"/usr/local/bin\"\n}\n\nintegrationTest.dependsOn project(\":cli\").buildDockerImage\n"
  },
  {
    "path": "minimesos/src/integration-test/java/com.containersol.minimesos/integrationtest/AuthenticationTest.java",
    "content": "package com.containersol.minimesos.integrationtest;\n\nimport com.containersol.minimesos.cluster.MesosCluster;\nimport com.containersol.minimesos.junit.MesosClusterTestRule;\nimport com.mashape.unirest.http.exceptions.UnirestException;\nimport org.junit.Assert;\nimport org.junit.ClassRule;\nimport org.junit.Test;\n\npublic class AuthenticationTest {\n\n    public static final String aclExampleUnknownSyntaxUsedInStateJson = \"run_tasks {\\n  principals {\\n    values: \\\"foo\\\"\\n    values: \\\"bar\\\"\\n  }\\n  users {\\n    values: \\\"alice\\\"\\n  }\\n}\\n\";\n\n    @ClassRule\n    public static final MesosClusterTestRule RULE = MesosClusterTestRule.fromFile(\"src/test/resources/configFiles/minimesosFile-authenticationTest\");\n\n    public static MesosCluster CLUSTER = RULE.getMesosCluster();\n\n    @Test\n    public void clusterHasZookeeperUrl() throws UnirestException {\n        Assert.assertEquals(\"zk://\" + CLUSTER.getZooKeeper().getIpAddress() + \":2181/mesos\", CLUSTER.getMaster().getState().getFlags().get(\"zk\"));\n    }\n\n    /**\n     * See https://issues.apache.org/jira/browse/MESOS-3792. Because of this bug the  acl values are represented\n     * as separate key value pairs.\n     */\n    @Test\n    public void extraEnvironmentVariablesPassedToMesosMaster() throws UnirestException {\n        Assert.assertEquals(\"true\", CLUSTER.getMaster().getState().getFlags().get(\"authenticate\"));\n        Assert.assertEquals(aclExampleUnknownSyntaxUsedInStateJson, CLUSTER.getMaster().getState().getFlags().get(\"acls\"));\n    }\n\n}\n"
  },
  {
    "path": "minimesos/src/integration-test/java/com.containersol.minimesos/integrationtest/MesosClusterTest.java",
    "content": "package com.containersol.minimesos.integrationtest;\n\nimport com.containersol.minimesos.MinimesosException;\nimport com.containersol.minimesos.cluster.ClusterProcess;\nimport com.containersol.minimesos.cluster.MesosAgent;\nimport com.containersol.minimesos.cluster.MesosCluster;\nimport com.containersol.minimesos.cluster.MesosMaster;\nimport com.containersol.minimesos.cluster.ZooKeeper;\nimport com.containersol.minimesos.config.ClusterConfig;\nimport com.containersol.minimesos.config.MesosAgentConfig;\nimport com.containersol.minimesos.integrationtest.container.HelloWorldContainer;\nimport com.containersol.minimesos.integrationtest.container.MesosExecuteContainer;\nimport com.containersol.minimesos.docker.DockerClientFactory;\nimport com.containersol.minimesos.docker.DockerContainersUtil;\nimport com.containersol.minimesos.junit.MesosClusterTestRule;\nimport com.containersol.minimesos.marathon.MarathonContainer;\nimport com.containersol.minimesos.mesos.MesosAgentContainer;\nimport com.containersol.minimesos.mesos.MesosClusterContainersFactory;\nimport com.containersol.minimesos.state.State;\nimport com.containersol.minimesos.util.Environment;\nimport com.containersol.minimesos.util.ResourceUtil;\nimport com.github.dockerjava.api.command.InspectContainerResponse;\nimport com.github.dockerjava.api.model.ExposedPort;\nimport com.github.dockerjava.api.model.Frame;\nimport com.github.dockerjava.api.model.Link;\nimport com.github.dockerjava.core.command.LogContainerResultCallback;\nimport com.jayway.awaitility.Awaitility;\nimport com.mashape.unirest.http.Unirest;\nimport com.mashape.unirest.http.exceptions.UnirestException;\nimport org.json.JSONObject;\nimport org.junit.*;\n\nimport java.io.FileNotFoundException;\nimport java.net.URI;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.concurrent.TimeUnit;\n\nimport static org.junit.Assert.assertEquals;\n\npublic class MesosClusterTest {\n\n    @ClassRule\n    public static final MesosClusterTestRule RULE = MesosClusterTestRule.fromFile(\"src/test/resources/configFiles/minimesosFile-mesosClusterTest\");\n\n    public static final MesosCluster CLUSTER = RULE.getMesosCluster();\n\n    @After\n    public void after() {\n        DockerContainersUtil.getContainers(false).filterByName(HelloWorldContainer.CONTAINER_NAME_PATTERN).kill().remove();\n    }\n\n    @Test(expected = MinimesosException.class)\n    public void testLoadCluster_noContainersFound() {\n        MesosCluster.loadCluster(\"nonexistent\", new MesosClusterContainersFactory());\n    }\n\n    @Test\n    public void mesosClusterCanBeStarted() throws Exception {\n        MesosMaster master = CLUSTER.getMaster();\n        State state = master.getState();\n\n        Assert.assertEquals(3, state.getActivatedAgents());\n    }\n\n    @Test\n    public void mesosResourcesCorrect() throws Exception {\n        JSONObject stateInfo = CLUSTER.getMaster().getStateInfoJSON();\n        for (int i = 0; i < 3; i++) {\n            Assert.assertEquals((long) 4, stateInfo.getJSONArray(\"slaves\").getJSONObject(0).getJSONObject(\"resources\").getLong(\"cpus\"));\n            Assert.assertEquals(512, stateInfo.getJSONArray(\"slaves\").getJSONObject(0).getJSONObject(\"resources\").getInt(\"mem\"));\n        }\n    }\n\n    @Test\n    public void dockerExposeResourcesPorts() throws Exception {\n        List<MesosAgent> containers = CLUSTER.getAgents();\n\n        for (MesosAgent container : containers) {\n            ArrayList<Integer> ports = ResourceUtil.parsePorts(container.getResources());\n            InspectContainerResponse response = DockerClientFactory.build().inspectContainerCmd(container.getContainerId()).exec();\n            Map bindings = response.getNetworkSettings().getPorts().getBindings();\n            for (Integer port : ports) {\n                Assert.assertTrue(bindings.containsKey(new ExposedPort(port)));\n            }\n        }\n    }\n\n    @Test\n    public void testHelloWorldContainer() throws UnirestException {\n        Assume.assumeFalse(\"Only test hello world container on Linux\", Environment.isRunningInJvmOnMacOsX());\n        HelloWorldContainer container = new HelloWorldContainer();\n        container.start(60);\n        URI url = container.getServiceUrl();\n        Assert.assertEquals(200, Unirest.get(url.toString()).asString().getStatus());\n    }\n\n    @Test\n    public void testMasterLinkedToAgents() throws UnirestException {\n        List<MesosAgent> containers = CLUSTER.getAgents();\n        for (MesosAgent container : containers) {\n            InspectContainerResponse exec = DockerClientFactory.build().inspectContainerCmd(container.getContainerId()).exec();\n\n            List<Link> links = Arrays.asList(exec.getHostConfig().getLinks());\n\n            Assert.assertNotNull(links);\n            Assert.assertEquals(\"link to zookeeper is expected\", 1, links.size());\n            Assert.assertEquals(\"minimesos-zookeeper\", links.get(0).getAlias());\n        }\n    }\n\n    @Test(expected = IllegalStateException.class)\n    public void testStartingClusterSecondTime() {\n        CLUSTER.start(30);\n    }\n\n    @Test\n    public void testMesosVersionRestored() {\n        String clusterId = CLUSTER.getClusterId();\n        MesosCluster cluster = MesosCluster.loadCluster(clusterId, new MesosClusterContainersFactory());\n\n        Assert.assertEquals(\"1.0.0\", cluster.getConfiguredMesosVersion());\n    }\n\n    @Test\n    public void testFindMesosMaster() {\n        Assume.assumeFalse(\"Only test token interpolation on Linux\", Environment.isRunningInJvmOnMacOsX());\n\n        String initString = \"start ${MINIMESOS_MASTER} ${MINIMESOS_MASTER_IP} end\";\n\n        String expected = CLUSTER.getMaster().getServiceUrl().toString();\n        String ip = CLUSTER.getMaster().getIpAddress();\n\n        MarathonContainer marathon = (MarathonContainer) CLUSTER.getMarathon();\n        String updated = marathon.replaceTokens(initString);\n        assertEquals(\"MINIMESOS_MASTER should be replaced\", String.format(\"start %s %s end\", expected, ip), updated);\n    }\n\n    private static class LogContainerTestCallback extends LogContainerResultCallback {\n        final StringBuffer log = new StringBuffer();\n\n        @Override\n        public void onNext(Frame frame) {\n            log.append(new String(frame.getPayload()));\n            super.onNext(frame);\n        }\n\n        @Override\n        public String toString() {\n            return log.toString();\n        }\n    }\n\n    @Test\n    public void testMesosExecuteContainerSuccess() throws InterruptedException {\n        ClusterProcess mesosExecute = new MesosExecuteContainer();\n\n        String containerId = CLUSTER.addAndStartProcess(mesosExecute);\n\n        Awaitility.await(\"Mesos Execute container did not start responding\").atMost(60, TimeUnit.SECONDS).until(() -> {\n            LogContainerTestCallback cb1 = new LogContainerTestCallback();\n            DockerClientFactory.build().logContainerCmd(mesosExecute.getContainerId()).withContainerId(containerId).withStdOut(true).exec(cb1);\n            cb1.awaitCompletion();\n            String log = cb1.toString();\n            return log.contains(\"Received status update TASK_FINISHED for task 'test-cmd'\");\n        });\n    }\n\n    @Test\n    public void noMarathonTest() throws FileNotFoundException {\n        String clusterId = CLUSTER.getClusterId();\n\n        Assert.assertNotNull(\"Cluster ID must be set\", clusterId);\n\n        // this should not throw any exceptions\n        CLUSTER.destroy(RULE.getFactory());\n    }\n\n    @Test\n    public void stopWithNewContainerTest() {\n        MesosAgent extraAgent = new MesosAgentContainer(new MesosAgentConfig(ClusterConfig.DEFAULT_MESOS_VERSION));\n        ZooKeeper zooKeeper = CLUSTER.getZooKeeper();\n        extraAgent.setZooKeeper(zooKeeper);\n\n        String containerId = CLUSTER.addAndStartProcess(extraAgent);\n        Assert.assertNotNull(\"freshly started container is not found\", DockerContainersUtil.getContainer(containerId));\n\n        CLUSTER.destroy(RULE.getFactory());\n        Assert.assertNull(\"new container should be stopped too\", DockerContainersUtil.getContainer(containerId));\n    }\n\n    @Test\n    public void testStartTwiceShouldNoOp() {\n        MesosMaster master = CLUSTER.getMaster();\n        master.start(5);\n    }\n\n}\n"
  },
  {
    "path": "minimesos/src/integration-test/java/com.containersol.minimesos/integrationtest/container/HelloWorldContainer.java",
    "content": "package com.containersol.minimesos.integrationtest.container;\n\nimport com.containersol.minimesos.config.ContainerConfigBlock;\nimport com.containersol.minimesos.docker.DockerClientFactory;\nimport com.github.dockerjava.api.command.CreateContainerCmd;\nimport com.github.dockerjava.api.model.ExposedPort;\n\n/**\n * A container for testing purposes. A small web server on port 80 returns the message \"hello world.\"\n */\npublic class HelloWorldContainer extends AbstractContainer {\n    public static final String SERVICE_NAME = \"hello-world-service\";\n    public static final int SERVICE_PORT = 80;\n    public static final String HELLO_WORLD_IMAGE = \"tutum/hello-world\";\n    public static final String CONTAINER_NAME_PATTERN = \"^helloworld-[0-9a-f\\\\-]*$\";\n\n    public HelloWorldContainer() {\n        super(new ContainerConfigBlock(HELLO_WORLD_IMAGE, \"latest\"));\n    }\n\n    @Override\n    public String getRole() {\n        return \"helloworld\";\n    }\n\n    @Override\n    protected CreateContainerCmd dockerCommand() {\n        ExposedPort exposedPort = ExposedPort.tcp(SERVICE_PORT);\n        // port mapping is not used as port 80 is ofthen occupied on host\n        return DockerClientFactory.build().createContainerCmd(HELLO_WORLD_IMAGE)\n                .withEnv(String.format(\"SERVICE_%d_NAME=%s\", SERVICE_PORT, SERVICE_NAME))\n                .withPrivileged(true)\n                .withName(getName())\n                .withExposedPorts(exposedPort);\n    }\n}\n"
  },
  {
    "path": "minimesos/src/integration-test/java/com.containersol.minimesos/integrationtest/container/MesosExecuteContainer.java",
    "content": "package com.containersol.minimesos.integrationtest.container;\n\nimport com.containersol.minimesos.integrationtest.MesosClusterTest;\nimport com.containersol.minimesos.config.ClusterConfig;\nimport com.containersol.minimesos.config.ContainerConfigBlock;\nimport com.containersol.minimesos.config.MesosAgentConfig;\nimport com.containersol.minimesos.docker.DockerClientFactory;\nimport com.github.dockerjava.api.command.CreateContainerCmd;\n\npublic class MesosExecuteContainer extends AbstractContainer {\n\n    private static final String TASK_CLUSTER_ROLE = \"test\";\n\n    public MesosExecuteContainer() {\n        super(new ContainerConfigBlock(MesosAgentConfig.MESOS_AGENT_IMAGE, ClusterConfig.DEFAULT_MESOS_CONTAINER_TAG));\n    }\n\n    @Override\n    public String getRole() {\n        return TASK_CLUSTER_ROLE;\n    }\n\n    @Override\n    protected CreateContainerCmd dockerCommand() {\n        return DockerClientFactory.build().createContainerCmd(String.format(\"%s:%s\", MesosAgentConfig.MESOS_AGENT_IMAGE, ClusterConfig.DEFAULT_MESOS_CONTAINER_TAG))\n            .withName(getName())\n            .withEntrypoint(\n                \"mesos-execute\",\n                \"--master=\" + MesosClusterTest.CLUSTER.getMaster().getIpAddress() + \":5050\",\n                \"--command=echo 1\",\n                \"--name=test-cmd\",\n                \"--resources=cpus:0.1;mem:128\"\n            );\n    }\n}\n"
  },
  {
    "path": "minimesos/src/main/groovy/com/containersol/minimesos/config/AgentResourcesConfig.groovy",
    "content": "package com.containersol.minimesos.config\n\nimport groovy.util.logging.Slf4j\n\nimport java.util.regex.Matcher\n\n@Slf4j\nclass AgentResourcesConfig extends GroovyBlock {\n\n    static public final ResourceDefScalar DEFAULT_CPU = new ResourceDefScalar(\"*\", 4)\n    static public final ResourceDefScalar DEFAULT_MEM = new ResourceDefScalar(\"*\", 1024)\n    static public final ResourceDefScalar DEFAULT_DISK = new ResourceDefScalar(\"*\", 2000)\n    static public final ResourceDefRanges DEFAULT_PORTS = new ResourceDefRanges(\"*\", \"[31000-32000]\")\n\n    HashMap<String, ResourceDefScalar> cpus\n    HashMap<String, ResourceDefScalar> mems\n    HashMap<String, ResourceDefScalar> disks\n    HashMap<String, ResourceDefRanges> ports\n\n    public AgentResourcesConfig() {\n        this(true)\n    }\n\n    private AgentResourcesConfig(boolean defaults) {\n        cpus = new HashMap<>()\n        mems = new HashMap<>()\n        disks = new HashMap<>()\n        ports = new HashMap<>()\n        if (defaults) {\n            setDefaults()\n        }\n    }\n\n    private void setDefaults() {\n        addResource(cpus, DEFAULT_CPU)\n        addResource(mems, DEFAULT_MEM)\n        addResource(disks, DEFAULT_DISK)\n        addResource(ports, DEFAULT_PORTS)\n    }\n\n    /**\n     * Generates resources object from Mesos string definition\n     * @param strResources in format like ports(*):[8081-8082]; cpus(*):1.2\n     * @return\n     */\n    static AgentResourcesConfig fromString(String strResources) {\n\n        String pattern = \"(\\\\w+)\\\\(([A-Za-z0-9_\\\\*]+)\\\\):(\\\\[?[0-9_\\\\-\\\\*., ]+\\\\]?)\"\n        AgentResourcesConfig resources = new AgentResourcesConfig(false)\n\n        String[] split = strResources.split(\";\")\n        for (String str : split) {\n            Matcher matcher = str.trim() =~ pattern\n            if (matcher.matches() && (matcher.groupCount() == 3)) {\n                String type = matcher.group(1)\n                String role = matcher.group(2)\n                String value = matcher.group(3)\n\n                switch (type) {\n                    case \"ports\":\n                        resources.ports.put(role, new ResourceDefRanges(role, value))\n                        break\n                    case \"cpus\":\n                        resources.cpus.put(role, new ResourceDefScalar(role, Double.valueOf(value)))\n                        break\n                    case \"mem\":\n                        resources.mems.put(role, new ResourceDefScalar(role, Double.valueOf(value)))\n                        break\n                    case \"disk\":\n                        resources.disks.put(role, new ResourceDefScalar(role, Double.valueOf(value)))\n                        break\n                }\n\n            }\n        }\n\n        return resources\n    }\n\n    def cpu(@DelegatesTo(ResourceDef) Closure cl) {\n        addResource(cpus, loadResourceDef(cl, ResourceDefScalar.class))\n    }\n\n    def mem(@DelegatesTo(ResourceDef) Closure cl) {\n        addResource(mems, loadResourceDef(cl, ResourceDefScalar.class))\n    }\n\n    def disk(@DelegatesTo(ResourceDef) Closure cl) {\n        addResource(disks, loadResourceDef(cl, ResourceDefScalar.class))\n    }\n\n    def ports(@DelegatesTo(ResourceDef) Closure cl) {\n        addResource(ports, loadResourceDef(cl, ResourceDefRanges.class))\n    }\n\n    ResourceDef loadResourceDef(Closure cl, Class<ResourceDef> resourceDefClass) {\n        ResourceDef resource = resourceDefClass.newInstance()\n        delegateTo(resource, cl)\n        return resource\n    }\n\n    /**\n     *\n     * @return formatted string with definition of resources. Example: ports(*):[31000-32000]; cpus(*):0.2; mem(*):256; disk(*):200\n     */\n    String asMesosString() {\n\n        StringBuilder builder = new StringBuilder()\n\n        for (ResourceDef resourceDef : ports.values()) {\n            appendResource(builder, resourceDef, \"ports\")\n        }\n        for (ResourceDef resourceDef : cpus.values()) {\n            appendResource(builder, resourceDef, \"cpus\")\n        }\n        for (ResourceDef resourceDef : mems.values()) {\n            appendResource(builder, resourceDef, \"mem\")\n        }\n        for (ResourceDef resourceDef : disks.values()) {\n            appendResource(builder, resourceDef, \"disk\")\n        }\n\n        return builder.toString()\n\n    }\n\n    static void appendResource(StringBuilder builder, ResourceDef resourceDef, String res) {\n        if (builder.length() > 0) {\n            builder.append(\"; \")\n        }\n        builder.append(res).append(\"(\").append(resourceDef.role).append(\"):\").append(resourceDef.valueAsString());\n    }\n\n    static void addResource(HashMap<String, ResourceDef> resources, ResourceDef resource) {\n        resources.put(resource.role, resource)\n    }\n\n}\n"
  },
  {
    "path": "minimesos/src/main/groovy/com/containersol/minimesos/config/AppConfig.groovy",
    "content": "package com.containersol.minimesos.config\n\n/**\n * Configuration for a Marathon app. Path is relative to the minimesosFile.\n */\nclass AppConfig {\n\n    private String marathonJson\n\n    void setMarathonJson(String marathonJson) {\n        this.marathonJson = marathonJson\n    }\n\n    String getMarathonJson() {\n        return marathonJson\n    }\n\n}\n"
  },
  {
    "path": "minimesos/src/main/groovy/com/containersol/minimesos/config/ClusterConfig.groovy",
    "content": "package com.containersol.minimesos.config\n\nimport groovy.util.logging.Slf4j\nimport org.apache.commons.lang.StringUtils\n\n@Slf4j\nclass ClusterConfig extends GroovyBlock {\n\n    public static final int DEFAULT_TIMEOUT_SECS = 60\n    public static final String DEFAULT_MESOS_VERSION = \"1.0.0\"\n    public static final String DEFAULT_MINIMESOS_DOCKER_VERSION = \"0.1.0\"\n    public static final String DEFAULT_MESOS_CONTAINER_TAG = DEFAULT_MESOS_VERSION + \"-\" + DEFAULT_MINIMESOS_DOCKER_VERSION\n    public static final String DEFAULT_CONFIG_FILE = \"minimesosFile\"\n    public static final String DEFAULT_LOGGING_LEVEL = \"INFO\"\n\n    def call(Closure cl) {\n        cl.setDelegate(this)\n        cl.setResolveStrategy(Closure.DELEGATE_ONLY)\n        cl.call()\n    }\n\n    boolean mapPortsToHost = false\n    boolean mapAgentSandboxVolume = false\n\n    int timeout = DEFAULT_TIMEOUT_SECS\n    String mesosVersion = DEFAULT_MESOS_VERSION\n    String clusterName = null\n    String loggingLevel = DEFAULT_LOGGING_LEVEL\n\n    MesosMasterConfig master = null\n    List<MesosAgentConfig> agents = new ArrayList<>()\n    ZooKeeperConfig zookeeper = null\n    MarathonConfig marathon = null\n    MesosDNSConfig mesosdns = null\n    ConsulConfig consul = null\n    RegistratorConfig registrator = null\n\n    def master(@DelegatesTo(MesosMasterConfig) Closure cl) {\n        if (master != null) {\n            throw new RuntimeException(\"Multiple Masters are not supported in this version yet\")\n        }\n        master = new MesosMasterConfig(mesosVersion)\n        delegateTo(master, cl)\n    }\n\n    def agent(@DelegatesTo(MesosAgentConfig) Closure cl) {\n        def agent = new MesosAgentConfig(mesosVersion)\n        delegateTo(agent, cl)\n        agents.add(agent)\n    }\n\n    def zookeeper(@DelegatesTo(ZooKeeperConfig) Closure cl) {\n        if (zookeeper != null) {\n            throw new RuntimeException(\"Multiple Zookeepers are not supported in this version yet\")\n        }\n        zookeeper = new ZooKeeperConfig()\n        delegateTo(zookeeper, cl)\n    }\n\n    def marathon(@DelegatesTo(MarathonConfig) Closure cl) {\n        if (marathon != null) {\n            throw new RuntimeException(\"Cannot have more than 1 marathon\")\n        }\n        marathon = new MarathonConfig()\n        delegateTo(marathon, cl)\n    }\n\n    def mesosdns(@DelegatesTo(MesosDNSConfig) Closure cl) {\n        if (mesosdns != null) {\n            throw new RuntimeException(\"Cannot have more than 1 mesosDNS\")\n        }\n        mesosdns = new MesosDNSConfig()\n        delegateTo(mesosdns, cl)\n    }\n\n    def consul(@DelegatesTo(ConsulConfig) Closure cl) {\n        if (consul != null) {\n            throw new RuntimeException(\"Cannot have more than 1 Consul server\")\n        }\n        consul = new ConsulConfig()\n        delegateTo(consul, cl)\n    }\n\n    def registrator(@DelegatesTo(RegistratorConfig) Closure cl) {\n        if (registrator != null) {\n            throw new RuntimeException(\"Cannot have more than 1 registrator\")\n        }\n        registrator = new RegistratorConfig()\n        delegateTo(registrator, cl)\n    }\n\n    void setLoggingLevel(String loggingLevel) {\n        if (!StringUtils.equalsIgnoreCase(loggingLevel, \"WARNING\") && !StringUtils.equalsIgnoreCase(loggingLevel, \"INFO\") && !StringUtils.equalsIgnoreCase(loggingLevel, \"ERROR\")) {\n            throw new RuntimeException(\"Property 'loggingLevel' can only have the values INFO, WARNING or ERROR. Got '\" + loggingLevel + \"'\")\n        }\n        this.loggingLevel = loggingLevel.toUpperCase()\n    }\n\n    void setMesosVersion(String mesosVersion) {\n        if (!MesosContainerConfig.MESOS_VERSIONS.contains(mesosVersion)) {\n            throw new RuntimeException(\"Property 'mesosVersion' supports values: \" + StringUtils.join(MesosContainerConfig.MESOS_VERSIONS, \",\"))\n        }\n        this.mesosVersion = mesosVersion\n    }\n\n    String getLoggingLevel() {\n        return loggingLevel\n    }\n}\n"
  },
  {
    "path": "minimesos/src/main/groovy/com/containersol/minimesos/config/ConfigParser.groovy",
    "content": "package com.containersol.minimesos.config\n\nimport groovy.util.logging.Slf4j\n\nimport java.text.DecimalFormat\nimport java.text.DecimalFormatSymbols\n\n/**\n * Parser for the minimesosFile. Turns minimesosFile with Groovy DSL specification into a {@link ClusterConfig} object.\n *\n * The minimesosFile DSL contains two components: blocks, and properties. A block starts and ends with curly braces: {}* and properties are nested inside the block. The main block is minimesos and it contains cluster-wide properties.\n * Other blocks contain properties that only affect the block itself like 'imageName' inside an agent block.\n */\n@Slf4j\nclass ConfigParser {\n\n    public static final String CONFIG_VARIABLE = \"minimesos\"\n\n    private DecimalFormat format = null;\n\n    private final Map<String, String> propsDictionary = [\n            \"agents\": \"agent\",\n            \"cpus\"  : \"cpu\",\n            \"mems\"  : \"mem\",\n            \"disks\" : \"disk\",\n            \"apps\"  : \"app\"\n    ]\n    private final List<String> ignoredProperties = [\"class\", \"format\"]\n\n    private final Map<String, String> comments = [\n            \"minimesos.marathon.apps\": \"Add 'app { marathonJson = \\\"<path or URL to JSON file>\\\" }' for every task you want to execute\",\n            \"minimesos.marathon.cmd\": \"BEWARE: this option customize the marathon starting command, changing it can break the cluster\"\n    ]\n\n    public ClusterConfig parse(String config) {\n        Binding binding = new Binding()\n\n        ClusterConfig minimesosDsl = new ClusterConfig()\n        binding.setVariable(CONFIG_VARIABLE, minimesosDsl)\n\n        GroovyShell shell = new GroovyShell(binding)\n        Script script = shell.parse(config)\n        script.run()\n\n        return minimesosDsl\n    }\n\n    /**\n     * Prints cluster configuration into a string\n     *\n     * @param config of the cluster to print\n     * @return string representation of the cluster configuration\n     */\n    public String toString(ClusterConfig config) {\n        String dslPath = CONFIG_VARIABLE\n        StringBuilder buffer = new StringBuilder()\n        appendPathDescription(buffer, \"\", dslPath)\n        buffer.append(CONFIG_VARIABLE).append(\" {\\n\")\n        printProperties(buffer, \"    \", config.properties, dslPath)\n        buffer.append(\"}\\n\")\n\n        buffer.toString()\n    }\n\n    private void printProperties(StringBuilder buffer, String intent, Map properties, String dslPath) {\n\n        List<String> propNames = properties.keySet().sort()\n        List<String> complexProps = new ArrayList<>()\n\n        for (String propName : propNames) {\n            if (!ignoredProperties.contains(propName)) {\n\n                Object value = properties.get(propName)\n                String strValue = formatSimpleValue(value)\n\n                if (strValue != null) {\n                    appendPathDescription(buffer, intent, dslPath + \".\" + propName)\n                    String line = String.format(\"%s%s = %s\\n\", intent, propName, strValue)\n                    buffer.append(line)\n                } else {\n                    complexProps.add(propName)\n                }\n\n            }\n        }\n\n        if (complexProps.size() > 0) {\n            for (String propName : complexProps) {\n\n                Object value = properties.get(propName)\n                String propToPrint = propName\n                if (propsDictionary.get(propName) != null) {\n                    propToPrint = propsDictionary.get(propName)\n                }\n\n                if (Collection.class.isAssignableFrom(value.getClass())) {\n\n                    Collection values = (Collection) value\n                    printCollection(buffer, intent, propToPrint, values, dslPath + \".\" + propName)\n\n                } else if (Map.class.isAssignableFrom(value.getClass())) {\n\n                    Map values = (Map) value\n                    printCollection(buffer, intent, propToPrint, values.values(), dslPath + \".\" + propName)\n\n                } else {\n                    buffer.append(\"\\n\").append(intent).append(propToPrint).append(\" {\\n\")\n                    printProperties(buffer, intent + \"    \", value.properties, dslPath + \".\" + propName)\n                    buffer.append(intent).append(\"}\\n\")\n                }\n            }\n        }\n    }\n\n    private void appendPathDescription(StringBuilder buffer, String intent, String dslPath) {\n        String comment = comments[dslPath]\n        if (comment != null) {\n            buffer.append(intent).append(\"// \").append(comment).append(\"\\n\")\n        }\n    }\n\n    private String formatSimpleValue(Object value) {\n        String strValue = null\n\n        if (value == null) {\n            strValue = \"null\"\n        } else {\n\n            Class clazz = value.getClass()\n            if (String.class.isAssignableFrom(clazz)) {\n                strValue = \"\\\"\" + value + \"\\\"\"\n            } else if (Integer.class.isAssignableFrom(clazz)) {\n                strValue = value.toString()\n            } else if (Boolean.class.isAssignableFrom(clazz)) {\n                strValue = value.toString()\n            } else if (Double.class.isAssignableFrom(clazz)) {\n                strValue = getFormat().format(value);\n            }\n\n        }\n\n        strValue\n    }\n\n    private void printCollection(StringBuilder buffer, String intent, String propName, Collection values, String dslPath) {\n        buffer.append(\"\\n\")\n        appendPathDescription(buffer, intent, dslPath)\n        for (Object single : values) {\n            String strSingle = formatSimpleValue(single)\n            if (strSingle != null) {\n                String line = String.format(\"%s%s = %s\\n\", intent, propName, strSingle)\n                buffer.append(line)\n            } else {\n                buffer.append(intent).append(propName).append(\" {\\n\")\n                printProperties(buffer, intent + \"    \", single.properties, dslPath)\n            }\n            buffer.append(intent).append(\"}\\n\")\n        }\n    }\n\n    private DecimalFormat getFormat() {\n        if (format == null) {\n            // see http://mesos.apache.org/documentation/latest/attributes-resources/\n            // make format locale independent\n            DecimalFormatSymbols symbols = new DecimalFormatSymbols();\n            symbols.setDecimalSeparator('.' as char)\n            format = new DecimalFormat(\"#.##\", symbols)\n        }\n        return format\n    }\n}\n"
  },
  {
    "path": "minimesos/src/main/groovy/com/containersol/minimesos/config/ConsulConfig.groovy",
    "content": "package com.containersol.minimesos.config;\n\npublic class ConsulConfig extends ContainerConfigBlock implements ContainerConfig {\n\n    public static final String CONSUL_IMAGE_NAME = \"consul\"\n    public static final String CONSUL_TAG_NAME = \"0.7.1\"\n\n    public static final int CONSUL_HTTP_PORT = 8500\n    public static final int CONSUL_DNS_PORT = 8600\n\n    public ConsulConfig() {\n        imageName = CONSUL_IMAGE_NAME\n        imageTag = CONSUL_TAG_NAME\n    }\n\n}\n"
  },
  {
    "path": "minimesos/src/main/groovy/com/containersol/minimesos/config/ContainerConfig.groovy",
    "content": "package com.containersol.minimesos.config\n\n/**\n * Common methods for containers' configuration\n */\ninterface ContainerConfig {\n\n    String getImageName()\n\n    void setImageName(String imageName)\n\n    String getImageTag()\n\n    void setImageTag(String imageTag)\n\n}\n"
  },
  {
    "path": "minimesos/src/main/groovy/com/containersol/minimesos/config/ContainerConfigBlock.groovy",
    "content": "package com.containersol.minimesos.config;\n\npublic class ContainerConfigBlock extends GroovyBlock implements ContainerConfig {\n\n    String imageName;\n    String imageTag;\n\n    public ContainerConfigBlock() {\n    }\n\n    public ContainerConfigBlock(String name, String tag) {\n        this.imageName = name\n        this.imageTag = tag;\n    }\n\n}\n"
  },
  {
    "path": "minimesos/src/main/groovy/com/containersol/minimesos/config/GroovyBlock.groovy",
    "content": "package com.containersol.minimesos.config;\n\n/**\n * Contains a collection of properties for a configuration object.\n */\nclass GroovyBlock {\n\n    def delegateTo(Object obj, Closure cl) {\n        def code = cl.rehydrate(obj, this, this)\n        code.resolveStrategy = Closure.DELEGATE_ONLY\n        code()\n    }\n\n    def methodMissing(String methodName, args) {\n        throw new MissingPropertyException(\"Block '\" + methodName + \"' not supported\")\n    }\n\n}\n"
  },
  {
    "path": "minimesos/src/main/groovy/com/containersol/minimesos/config/GroupConfig.groovy",
    "content": "package com.containersol.minimesos.config;\n\n/**\n * Configuration for a Marathon group. Path is relative to the minimesosFile.\n */\nclass GroupConfig {\n\n    private String marathonJson\n\n    void setMarathonJson(String marathonJson) {\n        this.marathonJson = marathonJson\n    }\n\n    String getMarathonJson() {\n        return marathonJson\n    }\n\n}\n"
  },
  {
    "path": "minimesos/src/main/groovy/com/containersol/minimesos/config/MarathonConfig.groovy",
    "content": "package com.containersol.minimesos.config\n\nimport com.containersol.minimesos.MinimesosException\n\nclass MarathonConfig extends ContainerConfigBlock implements ContainerConfig {\n\n    public static final String MARATHON_IMAGE = \"mesosphere/marathon\"\n    public static final String MARATHON_IMAGE_TAG = \"v1.3.5\"\n    public static final int MARATHON_PORT = 8080\n    public static final String MARATHON_CMD = \"--master zk://minimesos-zookeeper:2181/mesos --zk zk://minimesos-zookeeper:2181/marathon\"\n\n    List<AppConfig> apps = new ArrayList<>()\n    List<GroupConfig> groups = new ArrayList<>()\n    String cmd\n\n    MarathonConfig() {\n        imageName = MARATHON_IMAGE\n        imageTag = MARATHON_IMAGE_TAG\n        cmd = MARATHON_CMD\n    }\n\n    def app(@DelegatesTo(AppConfig) Closure cl) {\n        def app = new AppConfig()\n        delegateTo(app, cl)\n\n        if (app.getMarathonJson() == null) {\n            throw new MinimesosException(\"App config must have a 'marathonJson' property\")\n        }\n        apps.add(app)\n    }\n\n    def group(@DelegatesTo(GroupConfig) Closure cl) {\n        def group = new GroupConfig()\n        delegateTo(group, cl)\n\n        if (group.getMarathonJson() == null) {\n            throw new MinimesosException(\"Group config must have a 'marathonJson' property\")\n        }\n        groups.add(group)\n    }\n\n}\n"
  },
  {
    "path": "minimesos/src/main/groovy/com/containersol/minimesos/config/MesosAgentConfig.groovy",
    "content": "package com.containersol.minimesos.config\n\nimport groovy.util.logging.Slf4j\n\n@Slf4j\nclass MesosAgentConfig extends MesosContainerConfig {\n\n    public static final String MESOS_AGENT_IMAGE = \"containersol/mesos-agent\"\n    public static final int DEFAULT_MESOS_AGENT_PORT = 5051\n    public static final String DEFAULT_MESOS_ATTRIBUTES = \"\"\n\n    int portNumber = DEFAULT_MESOS_AGENT_PORT\n    String attributes = DEFAULT_MESOS_ATTRIBUTES\n\n    AgentResourcesConfig resources = new AgentResourcesConfig()\n\n    public MesosAgentConfig(String mesosVersion) {\n        imageName = MESOS_AGENT_IMAGE\n        imageTag = mesosVersion + \"-\" + MINIMESOS_DOCKER_TAG\n    }\n\n    def resources(@DelegatesTo(AgentResourcesConfig) Closure cl) {\n        delegateTo(resources, cl)\n    }\n\n}\n"
  },
  {
    "path": "minimesos/src/main/groovy/com/containersol/minimesos/config/MesosContainerConfig.groovy",
    "content": "package com.containersol.minimesos.config\n\nabstract class MesosContainerConfig extends ContainerConfigBlock implements ContainerConfig {\n\n    public static final String MESOS_LOGGING_LEVEL_INHERIT = \"# INHERIT FROM CLUSTER\"\n    public static final String MINIMESOS_DOCKER_TAG = \"0.1.0\"\n\n    private String loggingLevel = MESOS_LOGGING_LEVEL_INHERIT\n\n    public static final List<String> MESOS_VERSIONS = [\n            \"0.25\",\n            \"0.25.0\",\n            \"0.26\",\n            \"0.27\",\n            \"0.28.0\",\n            \"0.28.1\",\n            \"0.28\",\n            \"1.0.0\",\n    ]\n\n    public String getLoggingLevel() {\n        return loggingLevel\n    }\n\n    public void setLoggingLevel(String loggingLevel) {\n        this.loggingLevel = loggingLevel.toUpperCase()\n    }\n}\n"
  },
  {
    "path": "minimesos/src/main/groovy/com/containersol/minimesos/config/MesosDNSConfig.groovy",
    "content": "package com.containersol.minimesos.config;\n\npublic class MesosDNSConfig extends ContainerConfigBlock implements ContainerConfig {\n\n    public static final String MESOS_DNS_IMAGE_NAME = \"xebia/mesos-dns\"\n    public static final String MESOS_DNS_TAG_NAME = \"0.0.5\"\n\n    public MesosDNSConfig() {\n        imageName = MESOS_DNS_IMAGE_NAME\n        imageTag = MESOS_DNS_TAG_NAME\n    }\n\n}\n"
  },
  {
    "path": "minimesos/src/main/groovy/com/containersol/minimesos/config/MesosMasterConfig.groovy",
    "content": "package com.containersol.minimesos.config\n\nimport groovy.util.logging.Slf4j\n\n@Slf4j\nclass MesosMasterConfig extends MesosContainerConfig {\n\n    public static final String MESOS_MASTER_IMAGE = \"containersol/mesos-master\"\n    public static final int MESOS_MASTER_PORT = 5050\n\n    public MesosMasterConfig(String mesosVersion) {\n        imageName = MESOS_MASTER_IMAGE\n        imageTag = mesosVersion + \"-\" + MINIMESOS_DOCKER_TAG\n    }\n\n    boolean authenticate = false\n    String aclJson\n\n}\n"
  },
  {
    "path": "minimesos/src/main/groovy/com/containersol/minimesos/config/RegistratorConfig.groovy",
    "content": "package com.containersol.minimesos.config;\n\npublic class RegistratorConfig extends ContainerConfigBlock implements ContainerConfig {\n\n    public static final String REGISTRATOR_IMAGE_NAME = \"gliderlabs/registrator\"\n    public static final String REGISTRATOR_TAG_NAME = \"v6\"\n\n    public RegistratorConfig() {\n        imageName = REGISTRATOR_IMAGE_NAME\n        imageTag = REGISTRATOR_TAG_NAME\n    }\n\n}\n"
  },
  {
    "path": "minimesos/src/main/groovy/com/containersol/minimesos/config/ResourceDef.groovy",
    "content": "package com.containersol.minimesos.config\n\n/**\n * Check http://mesos.apache.org/documentation/latest/attributes-resources/ for possible types of values\n */\nabstract class ResourceDef extends GroovyBlock {\n\n    String role\n\n    protected ResourceDef() {\n    }\n\n    protected ResourceDef(String role) {\n        this.role = role\n    }\n\n    abstract public String valueAsString()\n\n}\n"
  },
  {
    "path": "minimesos/src/main/groovy/com/containersol/minimesos/config/ResourceDefRanges.groovy",
    "content": "package com.containersol.minimesos.config\n\n/**\n * Check http://mesos.apache.org/documentation/latest/attributes-resources/ for possible types of values\n */\nclass ResourceDefRanges extends ResourceDef {\n\n    String value\n\n    public ResourceDefRanges() {\n    }\n\n    public ResourceDefRanges(String role, String value) {\n        super(role)\n        this.value = value\n    }\n\n    @Override\n    String valueAsString() {\n        return value\n    }\n}\n"
  },
  {
    "path": "minimesos/src/main/groovy/com/containersol/minimesos/config/ResourceDefScalar.groovy",
    "content": "package com.containersol.minimesos.config\n\nimport java.text.DecimalFormat\nimport java.text.DecimalFormatSymbols\n\n/**\n * Check http://mesos.apache.org/documentation/latest/attributes-resources/ for possible types of values\n */\nclass ResourceDefScalar extends ResourceDef {\n\n    private DecimalFormat format = null;\n    private double value\n\n    public ResourceDefScalar() {\n    }\n\n    public ResourceDefScalar(String role, double value) {\n        super(role)\n        this.value = value\n    }\n\n    private DecimalFormat getFormat() {\n        if (format == null) {\n            // see http://mesos.apache.org/documentation/latest/attributes-resources/\n            // make format locale independent\n            DecimalFormatSymbols symbols = new DecimalFormatSymbols();\n            symbols.setDecimalSeparator('.' as char)\n            format = new DecimalFormat(\"#.##\", symbols)\n        }\n        return format\n    }\n\n    public void setValue(double value) {\n        this.value = value\n    }\n\n    /**\n     * Without this explicit setter groovy assigns unexpected values, if they are surrounded by \"\"\n     * @param value to get double from\n     */\n    public void setValue(String value) {\n        // correct format is intValue ( \".\" intValue )?\n        if (value.contains(\",\")) {\n            throw new NumberFormatException(value + \" is not valid scalar value\")\n        }\n        this.value = getFormat().parse(value).doubleValue()\n    }\n\n    public double getValue() {\n        value\n    }\n\n    @Override\n    String valueAsString() {\n        return getFormat().format(value);\n    }\n\n}\n"
  },
  {
    "path": "minimesos/src/main/groovy/com/containersol/minimesos/config/ZooKeeperConfig.groovy",
    "content": "package com.containersol.minimesos.config\n\npublic class ZooKeeperConfig extends ContainerConfigBlock implements ContainerConfig {\n\n    public static final String DEFAULT_MESOS_ZK_PATH = \"/mesos\";\n    public static final int DEFAULT_ZOOKEEPER_PORT = 2181;\n\n    public static final String MESOS_LOCAL_IMAGE = \"jplock/zookeeper\"\n    public static final String ZOOKEEPER_IMAGE_TAG = \"3.4.6\"\n\n    public ZooKeeperConfig() {\n        imageName = MESOS_LOCAL_IMAGE\n        imageTag = ZOOKEEPER_IMAGE_TAG\n    }\n\n}\n"
  },
  {
    "path": "minimesos/src/main/java/com/containersol/minimesos/MinimesosException.java",
    "content": "package com.containersol.minimesos;\n\n/**\n * Thrown when a minimesos command fails.\n */\npublic class MinimesosException extends RuntimeException {\n\n    public MinimesosException(String message) {\n        super(message);\n    }\n\n    public MinimesosException(String message, Throwable cause) {\n        super(message, cause);\n    }\n\n}\n"
  },
  {
    "path": "minimesos/src/main/java/com/containersol/minimesos/cluster/ClusterProcess.java",
    "content": "package com.containersol.minimesos.cluster;\n\nimport java.net.URI;\n\n/**\n * Generic functionality of every cluster member\n */\npublic interface ClusterProcess {\n\n    MesosCluster getCluster();\n\n    void setCluster(MesosCluster mesosCluster);\n\n    /**\n     * @return the IP address of the container\n     */\n    String getIpAddress();\n\n    /**\n     * @return URI the service is available at (or null)\n     */\n    URI getServiceUrl();\n\n    /**\n     * Builds container name following the naming convention\n     *\n     * @return container name\n     */\n    String getName();\n\n    /**\n     * @return the ID of the container.\n     */\n    String getContainerId();\n\n    /**\n     * Starts the container and waits until is started\n     *\n     * @param timeout in seconds\n     */\n    void start(int timeout);\n\n    String getRole();\n\n    /**\n     * Removes a container with force\n     */\n    void remove();\n\n}\n"
  },
  {
    "path": "minimesos/src/main/java/com/containersol/minimesos/cluster/ClusterRepository.java",
    "content": "package com.containersol.minimesos.cluster;\n\nimport com.containersol.minimesos.MinimesosException;\nimport org.apache.commons.io.FileUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.nio.file.Files;\nimport java.nio.file.Paths;\n\n/**\n * Manages persistent information about the minimesos cluster\n */\npublic class ClusterRepository {\n\n    private static final Logger LOGGER = LoggerFactory.getLogger(ClusterRepository.class);\n\n    public static final String MINIMESOS_FILE_PROPERTY = \"minimesos.cluster\";\n\n    /**\n     * Loads representation of the running cluster\n     *\n     * @return representation of the cluster, which ID is found in the file\n     */\n    public MesosCluster loadCluster(MesosClusterFactory factory) {\n        String clusterId = readClusterId();\n        if (clusterId != null) {\n            try {\n                return MesosCluster.loadCluster(clusterId, factory);\n            } catch (MinimesosException e) {\n                deleteMinimesosFile();\n            }\n        }\n        return null;\n    }\n\n    /**\n     * Writes cluster id to file\n     *\n     * @param cluster cluster to store ID\n     */\n    public void saveClusterFile(MesosCluster cluster) {\n        String clusterId = cluster.getClusterId();\n        File dotMinimesosDir = getMinimesosDir();\n        try {\n            FileUtils.forceMkdir(dotMinimesosDir);\n            String clusterIdPath = dotMinimesosDir.getAbsolutePath() + \"/\" + MINIMESOS_FILE_PROPERTY;\n            Files.write(Paths.get(clusterIdPath), clusterId.getBytes());\n            LOGGER.debug(\"Writing cluster ID \" + clusterId + \" to \" + clusterIdPath);\n        } catch (IOException ie) {\n            LOGGER.error(\"Could not write .minimesos folder\", ie);\n            throw new RuntimeException(ie);\n        }\n    }\n\n    /**\n     * Deletes cluster file\n     */\n    public void deleteClusterFile() {\n        deleteMinimesosFile();\n    }\n\n    public String readClusterId() {\n        try {\n            File minimesosFile = getMinimesosFile();\n            String clusterId = FileUtils.readFileToString(minimesosFile, \"UTF-8\");\n            LOGGER.debug(\"Reading cluster ID from \" + minimesosFile + \": \" + clusterId);\n            return clusterId;\n        } catch (IOException e) {\n            return null;\n        }\n    }\n\n    /**\n     * @return file, possibly non-existing, where cluster information is stored\n     */\n    public File getMinimesosFile() {\n        return new File(getMinimesosDir(), MINIMESOS_FILE_PROPERTY);\n    }\n\n    /**\n     * @return directory, where minimesos stores ID file\n     */\n    public File getMinimesosDir() {\n        File hostDir = MesosCluster.getClusterHostDir();\n        File minimesosDir = new File(hostDir, \".minimesos\");\n        if (!minimesosDir.exists()) {\n            if (!minimesosDir.mkdirs()) {\n                throw new MinimesosException(\"Failed to create \" + minimesosDir.getAbsolutePath() + \" directory\");\n            }\n        }\n\n        return minimesosDir;\n    }\n\n    private void deleteMinimesosFile() {\n        File minimesosFile = getMinimesosFile();\n        LOGGER.debug(\"Deleting minimesos.cluster file at \" + getMinimesosFile());\n        if (minimesosFile.exists()) {\n            try {\n                FileUtils.forceDelete(minimesosFile);\n            } catch (IOException e) {\n                // ignore\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "minimesos/src/main/java/com/containersol/minimesos/cluster/ClusterUtil.java",
    "content": "package com.containersol.minimesos.cluster;\n\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Optional;\n\nimport static com.containersol.minimesos.cluster.Filter.withRole;\n\n/**\n * Helper methods for ClusterArchitecture\n */\npublic class ClusterUtil {\n\n    /**\n     * Disable constructors\n     */\n    private ClusterUtil() {\n    }\n\n    /**\n     * Filters given list of processes and returns only those with distinct roles\n     *\n     * @param processes complete list of processes\n     * @return processes with distinct roles\n     */\n    public static List<ClusterProcess> getDistinctRoleProcesses(List<ClusterProcess> processes) {\n\n        List<ClusterProcess> distinct = new ArrayList<>();\n        Map<String, Integer> roles = new HashMap<>();\n\n        // count processes per role\n        for (ClusterProcess process : processes) {\n            Integer prev = roles.get(process.getRole());\n            int count = (prev != null) ? prev : 0;\n            roles.put(process.getRole(), count+1 );\n        }\n\n        for (Map.Entry<String, Integer> role : roles.entrySet() ) {\n            if (role.getValue() == 1) {\n                Optional<ClusterProcess> process = processes.stream().filter(withRole(role.getKey())).findFirst();\n                distinct.add(process.get());\n            }\n        }\n\n        return distinct;\n\n    }\n\n}\n"
  },
  {
    "path": "minimesos/src/main/java/com/containersol/minimesos/cluster/Consul.java",
    "content": "package com.containersol.minimesos.cluster;\n\n/**\n * Consul functionality\n */\npublic interface Consul extends ClusterProcess {\n}\n"
  },
  {
    "path": "minimesos/src/main/java/com/containersol/minimesos/cluster/Filter.java",
    "content": "package com.containersol.minimesos.cluster;\n\nimport java.util.function.Predicate;\n\npublic class Filter {\n\n    private Filter() {\n    }\n\n    public static Predicate<ClusterProcess> zooKeeper() {\n        return process -> process instanceof ZooKeeper;\n    }\n\n    public static Predicate<ClusterProcess> consul() {\n        return process -> process instanceof Consul;\n    }\n\n    public static Predicate<ClusterProcess> mesosMaster() {\n        return process -> process instanceof MesosMaster;\n    }\n\n    public static Predicate<ClusterProcess> mesosAgent() {\n        return process -> process instanceof MesosAgent;\n    }\n\n    public static Predicate<ClusterProcess> marathon() {\n        return process -> process instanceof Marathon;\n    }\n\n    public static Predicate<ClusterProcess> withRole(String role) {\n        return process -> role.equals(process.getRole());\n    }\n\n    public static Predicate<ClusterProcess> registrator() { return process -> process instanceof Registrator; }\n\n    public static Predicate<ClusterProcess> mesosDns() {\n        return process -> process instanceof MesosDns;\n    }\n}\n"
  },
  {
    "path": "minimesos/src/main/java/com/containersol/minimesos/cluster/Marathon.java",
    "content": "package com.containersol.minimesos.cluster;\n\nimport com.mashape.unirest.http.HttpResponse;\nimport com.mashape.unirest.http.JsonNode;\nimport mesosphere.marathon.client.model.v2.Result;\n\n/**\n * Functionality, which is expected from Marathon\n */\npublic interface Marathon extends ClusterProcess {\n\n    /**\n     * If Marathon configuration requires, installs the applications\n     */\n    void installMarathonApps();\n\n    /**\n     * Deploys a Marathon app by JSON string\n     *\n     * @param marathonJson JSON string\n     */\n    void deployApp(String marathonJson);\n\n    /**\n     * Updates a Marathon app by JSON string\n     *\n     * @param marathonJson JSON string\n     */\n    void updateApp(String marathonJson);\n\n    /**\n     * Kill all apps that are currently running.\n     */\n    void killAllApps();\n\n    void setZooKeeper(ZooKeeper zookeeper);\n\n    /**\n     * Delete the given app\n     *\n     * @param app to be deleted\n     */\n    Result deleteApp(String app);\n\n    /**\n     * Deploy a Marathon application group.\n     *\n     * @param groupJson JSON string with Marathon application group definition\n     */\n    void deployGroup(String groupJson);\n\n    /**\n     * Deploy a Marathon application group.\n     *\n     * @param group group name\n     */\n    Result deleteGroup(String group);\n}\n"
  },
  {
    "path": "minimesos/src/main/java/com/containersol/minimesos/cluster/MesosAgent.java",
    "content": "package com.containersol.minimesos.cluster;\n\n/**\n * Functionality of Mesos Master\n */\npublic interface MesosAgent extends MesosContainer {\n    String getResources();\n}\n"
  },
  {
    "path": "minimesos/src/main/java/com/containersol/minimesos/cluster/MesosCluster.java",
    "content": "package com.containersol.minimesos.cluster;\n\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.FileNotFoundException;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.PrintStream;\nimport java.net.URI;\nimport java.security.SecureRandom;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Optional;\nimport java.util.concurrent.TimeUnit;\nimport java.util.stream.Collectors;\n\nimport com.containersol.minimesos.MinimesosException;\nimport com.containersol.minimesos.config.ClusterConfig;\nimport com.containersol.minimesos.state.State;\nimport com.containersol.minimesos.util.Environment;\nimport com.containersol.minimesos.util.Predicate;\nimport com.fasterxml.jackson.core.JsonParseException;\nimport com.fasterxml.jackson.databind.JsonMappingException;\nimport com.github.dockerjava.api.exception.InternalServerErrorException;\nimport com.github.dockerjava.api.exception.NotFoundException;\nimport com.mashape.unirest.http.exceptions.UnirestException;\n\nimport org.apache.commons.io.FileUtils;\nimport org.apache.commons.lang.StringUtils;\nimport org.json.JSONObject;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport static com.jayway.awaitility.Awaitility.*;\nimport static org.junit.Assert.assertTrue;\n\n/**\n * Mesos cluster with lifecycle methods such as start, install, info, state, stop and destroy.\n */\npublic class MesosCluster {\n\n    private static final Logger LOGGER = LoggerFactory.getLogger(MesosCluster.class);\n\n    public static final String MINIMESOS_HOST_DIR_PROPERTY = \"minimesos.host.dir\";\n    public static final String MINIMESOS_TOKEN_PREFIX = \"MINIMESOS_\";\n\n    public static final String TOKEN_NETWORK_GATEWAY = MINIMESOS_TOKEN_PREFIX + \"NETWORK_GATEWAY\";\n\n    private String clusterId;\n\n    private final ClusterConfig clusterConfig;\n\n    private List<ClusterProcess> memberProcesses = Collections.synchronizedList(new ArrayList<>());\n\n    private ClusterRepository repository = new ClusterRepository();\n\n    private boolean running = false;\n\n    /**\n     * Create a new MesosCluster with a specified cluster architecture.\n     */\n    public MesosCluster(ClusterConfig clusterConfig, List<ClusterProcess> processes) {\n        this.memberProcesses = processes;\n        this.clusterConfig = clusterConfig;\n\n        clusterId = Integer.toUnsignedString(new SecureRandom().nextInt());\n        for (ClusterProcess process : processes) {\n            process.setCluster(this);\n        }\n    }\n\n    /**\n     * Recreate a MesosCluster object based on an existing cluster ID.\n     *\n     * @param clusterId the cluster ID of the cluster that is already running\n     */\n    public static MesosCluster loadCluster(String clusterId, MesosClusterFactory factory) {\n        return new MesosCluster(clusterId, factory);\n    }\n\n    /**\n     * This constructor is used for deserialization of running cluster\n     *\n     * @param clusterId ID of the cluster to deserialize\n     */\n    private MesosCluster(String clusterId, MesosClusterFactory factory) {\n        this.clusterId = clusterId;\n        this.clusterConfig = new ClusterConfig();\n\n        if (Environment.isRunningInJvmOnMacOsX() || Environment.isRunningInDockerOnMac()) {\n            LOGGER.info(\"Detected Mac Environment X so running with --mapPortsToHost so master and marathon ports are mapped to localhost.\");\n            setMapPortsToHost(true);\n        }\n\n        factory.loadRunningCluster(this);\n\n        if (memberProcesses.isEmpty()) {\n            throw new MinimesosException(\"No containers found for cluster ID \" + clusterId);\n        }\n\n        ZooKeeper zookeeper = getZooKeeper();\n        MesosMaster master = getMaster();\n\n        if (master != null && zookeeper != null) {\n            for (MesosAgent mesosAgent : getAgents()) {\n                mesosAgent.setZooKeeper(zookeeper);\n            }\n            if (getMarathon() != null) {\n                getMarathon().setZooKeeper(zookeeper);\n            }\n        }\n\n        running = true;\n    }\n\n    /**\n     * Starts the Mesos cluster and its containers with 60 second timeout.\n     * The method is used by frameworks\n     */\n    public void start() {\n        start(clusterConfig.getTimeout());\n    }\n\n    /**\n     * Starts the Mesos cluster and its containers with given timeout.\n     *\n     * @param timeoutSeconds seconds to wait until timeout\n     */\n    public void start(int timeoutSeconds) {\n        if (running) {\n            throw new IllegalStateException(\"Cluster \" + clusterId + \" is already running\");\n        }\n\n        if (Environment.isRunningInJvmOnMacOsX() || Environment.isRunningInDockerOnMac()) {\n            LOGGER.info(\"Detected Mac Environment X, running with '--mapPortsToHost' so master and marathon ports are mapped to localhost\");\n            clusterConfig.setMapPortsToHost(true);\n        }\n\n        LOGGER.debug(\"Cluster \" + getClusterId() + \" - start\");\n        this.memberProcesses.forEach((container) -> container.start(timeoutSeconds));\n        // wait until the given number of agents are registered\n        getMaster().waitFor();\n\n        Marathon marathon = getMarathon();\n        if (marathon != null) {\n            marathon.installMarathonApps();\n        }\n\n        running = true;\n    }\n\n    /**\n     * Prints the state of the Mesos master or agent\n     */\n    public void state(PrintStream out) {\n        JSONObject stateInfo = getClusterStateInfo();\n        if (stateInfo != null) {\n            out.println(stateInfo.toString(2));\n        } else {\n            throw new MinimesosException(\"Could not retrieve the state from the cluster at \" + getMaster().getServiceUrl() + \". Is it running?\");\n        }\n    }\n\n    /**\n     * Destroys the Mesos cluster and its containers\n     */\n    public void destroy(MesosClusterFactory factory) {\n        LOGGER.debug(\"Cluster \" + getClusterId() + \" - destroy\");\n\n        Marathon marathon = getMarathon();\n        if (marathon != null) {\n            marathon.killAllApps();\n        }\n\n        if (memberProcesses.size() > 0) {\n            for (int i = memberProcesses.size() - 1; i >= 0; i--) {\n                ClusterProcess container = memberProcesses.get(i);\n                LOGGER.debug(\"Removing container [\" + container.getContainerId() + \"]\");\n                try {\n                    container.remove();\n                } catch (NotFoundException e) {\n                    LOGGER.error(String.format(\"Cannot remove container %s, maybe it's already dead?\", container.getContainerId()));\n                }\n            }\n        }\n        this.running = false;\n        this.memberProcesses.clear();\n\n        if (clusterId != null) {\n            factory.destroyRunningCluster(clusterId);\n\n            File sandboxLocation = new File(MesosCluster.getClusterHostDir(), \".minimesos/sandbox-\" + clusterId);\n            if (sandboxLocation.exists()) {\n                try {\n                    FileUtils.forceDelete(sandboxLocation);\n                } catch (IOException e) {\n                    String msg = String.format(\"Failed to force delete the cluster sandbox at %s\", sandboxLocation.getAbsolutePath());\n                    throw new MinimesosException(msg, e);\n                }\n            }\n\n        } else {\n            LOGGER.info(\"Minimesos cluster is not running\");\n        }\n\n        repository.deleteClusterFile();\n\n        this.running = false;\n    }\n\n    /**\n     * Starts a container. This container will be removed when the Mesos cluster is shut down.\n     *\n     * @param process container to be started\n     * @param timeout in seconds\n     * @return container ID\n     */\n    public String addAndStartProcess(ClusterProcess process, int timeout) {\n        process.setCluster(this);\n        memberProcesses.add(process);\n\n        LOGGER.debug(String.format(\"Starting %s (%s) container\", process.getName(), process.getContainerId()));\n\n        try {\n            process.start(timeout);\n        } catch (Exception exc) {\n            String msg = String.format(\"Failed to start %s (%s) container\", process.getName(), process.getContainerId());\n            LOGGER.error(msg, exc);\n            throw new MinimesosException(msg, exc);\n        }\n\n        return process.getContainerId();\n    }\n\n    /**\n     * Starts a container. This container will be removed when the Mesos cluster is shut down.\n     * The method is used by frameworks\n     *\n     * @param clusterProcess container to be started\n     * @return container ID\n     */\n    public String addAndStartProcess(ClusterProcess clusterProcess) {\n        return addAndStartProcess(clusterProcess, clusterConfig.getTimeout());\n    }\n\n    /**\n     * Retrieves JSON with Mesos Cluster master state\n     *\n     * @return stage JSON \n     */\n    public JSONObject getClusterStateInfo() {\n        try {\n            return getMaster().getStateInfoJSON();\n        } catch (UnirestException e) {\n            throw new MinimesosException(\"Failed to retrieve state from Mesos Master\", e);\n        }\n    }\n\n    /**\n     * Retrieves JSON with Mesos state of the given container\n     *\n     * @param containerId ID of the container to get state from\n     * @return stage JSON\n     */\n    public JSONObject getAgentStateInfo(String containerId) {\n        MesosAgent theAgent = null;\n        for (MesosAgent agent : getAgents()) {\n            if (agent.getContainerId().startsWith(containerId)) {\n                if (theAgent == null) {\n                    theAgent = agent;\n                } else {\n                    throw new MinimesosException(\"Provided ID \" + containerId + \" is not enough to uniquely identify container\");\n                }\n            }\n        }\n\n        try {\n            return (theAgent != null) ? theAgent.getStateInfoJSON() : null;\n        } catch (UnirestException e) {\n            throw new MinimesosException(\"Failed to retrieve state from Mesos Agent container \" + theAgent.getContainerId(), e);\n        }\n    }\n\n    public List<ClusterProcess> getMemberProcesses() {\n        return memberProcesses;\n    }\n\n    public List<MesosAgent> getAgents() {\n        return memberProcesses.stream().filter(Filter.mesosAgent()).map(c -> (MesosAgent) c).collect(Collectors.toList());\n    }\n\n    public MesosMaster getMaster() {\n        Optional<MesosMaster> master = getOne(Filter.mesosMaster());\n        return master.isPresent() ? master.get() : null;\n    }\n\n    public ZooKeeper getZooKeeper() {\n        Optional<ZooKeeper> zooKeeper = getOne(Filter.zooKeeper());\n        return zooKeeper.isPresent() ? zooKeeper.get() : null;\n    }\n\n    public Marathon getMarathon() {\n        Optional<Marathon> marathon = getOne(Filter.marathon());\n        return marathon.isPresent() ? marathon.get() : null;\n    }\n\n    public Consul getConsul() {\n        Optional<Consul> container = getOne(Filter.consul());\n        return container.isPresent() ? container.get() : null;\n    }\n\n    public MesosDns getMesosDns() {\n        Optional<MesosDns> container = getOne(Filter.mesosDns());\n        return container.isPresent() ? container.get() : null;\n    }\n\n    /**\n     * Optionally get one of a certain type of type T. Note, this cast will always work because we are filtering on that type.\n     * If it doesn't find that type, the optional is empty so the cast doesn't need to be performed.\n     *\n     * @param filter A predicate that is true when an {@link ClusterProcess} in the list is of type T\n     * @param <T>    A container of type T that extends {@link ClusterProcess}\n     * @return the first container it comes across.\n     */\n    @SuppressWarnings(\"unchecked\")\n    public <T extends ClusterProcess> Optional<T> getOne(java.util.function.Predicate<ClusterProcess> filter) {\n        return (Optional<T>) getMemberProcesses().stream().filter(filter).findFirst();\n    }\n\n    public String getClusterId() {\n        return clusterId;\n    }\n\n    public boolean isMapPortsToHost() {\n        return clusterConfig.getMapPortsToHost();\n    }\n\n    public boolean getMapAgentSandboxVolume() {\n        return clusterConfig.getMapAgentSandboxVolume();\n    }\n\n    public void setMapPortsToHost(boolean mapPortsToHost) {\n        clusterConfig.setMapPortsToHost(mapPortsToHost);\n    }\n\n    public void waitForState(final Predicate<State> predicate) {\n        await(\"Mesos master startup\" + clusterConfig.getTimeout()).atMost(clusterConfig.getTimeout(), TimeUnit.SECONDS).until(() -> {\n            try {\n                assertTrue(predicate.test(State.fromJSON(getMaster().getStateInfoJSON().toString())));\n            } catch (InternalServerErrorException | JsonParseException | UnirestException | JsonMappingException e) { //NOSONAR\n                throw new AssertionError(\"Mesos master did not start after \" + clusterConfig.getTimeout(), e);\n            }\n        });\n    }\n\n    /**\n     * Returns the directory on the host from which the cluster was created.\n     *\n     * @return directory\n     */\n    public static File getClusterHostDir() {\n        String sp = System.getProperty(MINIMESOS_HOST_DIR_PROPERTY);\n        if (sp == null) {\n            sp = System.getProperty(\"user.dir\");\n        }\n        return new File(sp);\n    }\n\n    /**\n     * Taking either URI or path to a file, returns string with its content\n     *\n     * @param location either absolute URI or path to a file\n     * @return input stream with location content or null\n     */\n    public static InputStream getInputStream(String location) {\n        InputStream is = null;\n\n        if (location != null) {\n            URI uri;\n            try {\n                uri = URI.create(location);\n                if (!uri.isAbsolute()) {\n                    uri = null;\n                }\n            } catch (IllegalArgumentException ignored) { //NOSONAR\n                // means this is not a valid URI, could be filepath\n                uri = null;\n            }\n\n            if (uri != null) {\n\n                try {\n                    is = uri.toURL().openStream();\n                } catch (IOException e) {\n                    throw new MinimesosException(\"Failed to open URL \" + location + \". Check URL syntax, your network connectivity or DNS settings.\", e);\n                }\n\n            } else {\n                // location is not an absolute URI, therefore treat it as relative or absolute path\n                File file = new File(location);\n                if (!file.exists()) {\n                    file = new File(getClusterHostDir(), location);\n                }\n\n                if (file.exists()) {\n                    try {\n                        is = new FileInputStream(file);\n                    } catch (FileNotFoundException e) {\n                        throw new MinimesosException(\"Failed to open \" + file.getAbsolutePath() + \" file\", e);\n                    }\n                }\n            }\n        }\n\n        return is;\n    }\n\n    /**\n     * @return configured or default logging level of all Mesos containers in the cluster\n     */\n    public String getLoggingLevel() {\n        return clusterConfig.getLoggingLevel();\n    }\n\n    /**\n     * @return Mesos version that is configured\n     */\n    public String getConfiguredMesosVersion() {\n        return clusterConfig.getMesosVersion();\n    }\n\n    /**\n     * @return Mesos version of running cluster\n     */\n    public String getMesosVersion() {\n        return getMaster().getState().getVersion();\n    }\n\n    /**\n     * @return either configured or composed with ID cluster name\n     */\n    public String getClusterName() {\n        String name = clusterConfig.getClusterName();\n        if (StringUtils.isBlank(name)) {\n            name = \"minimesos-\" + clusterId;\n        }\n        return name;\n    }\n\n    public ClusterConfig getClusterConfig() {\n        return clusterConfig;\n    }\n\n    @Override\n    public boolean equals(Object o) {\n        if (this == o) return true;\n        if (o == null || getClass() != o.getClass()) return false;\n\n        MesosCluster cluster = (MesosCluster) o;\n\n        return clusterId.equals(cluster.clusterId);\n    }\n\n    @Override\n    public int hashCode() {\n        // logic of hashCode() has to match logic of equals()\n        return clusterId.hashCode();\n    }\n\n    @Override\n    public String toString() {\n        return \"MesosCluster{\" +\n            \"clusterId='\" + clusterId + '\\'' +\n            \", processes=\" + memberProcesses +\n            '}';\n    }\n}\n"
  },
  {
    "path": "minimesos/src/main/java/com/containersol/minimesos/cluster/MesosClusterFactory.java",
    "content": "package com.containersol.minimesos.cluster;\n\n/**\n * Interface for creating members of the cluster and destroying running cluster\n */\npublic abstract class MesosClusterFactory {\n\n    /**\n     * Fills given cluster with discovered members\n     *\n     * @param cluster to load with discovered members\n     */\n    public abstract void loadRunningCluster(MesosCluster cluster);\n\n    /**\n     * Destroys members of the cluster with given ID\n     *\n     * @param clusterId ID of the cluster to destroy\n     */\n    public abstract void destroyRunningCluster(String clusterId);\n\n}\n"
  },
  {
    "path": "minimesos/src/main/java/com/containersol/minimesos/cluster/MesosContainer.java",
    "content": "package com.containersol.minimesos.cluster;\n\nimport com.containersol.minimesos.state.State;\nimport com.mashape.unirest.http.exceptions.UnirestException;\nimport org.json.JSONObject;\n\n/**\n * Functionality of Mesos Cluster core members\n */\npublic interface MesosContainer extends ClusterProcess {\n\n    void setZooKeeper(ZooKeeper zookeeper);\n\n    JSONObject getStateInfoJSON() throws UnirestException;\n\n    /**\n     * Retrieve state of the Master or Agent.\n     *\n     * @return state object with frameworks and tasks\n     */\n    State getState();\n\n}\n"
  },
  {
    "path": "minimesos/src/main/java/com/containersol/minimesos/cluster/MesosDns.java",
    "content": "package com.containersol.minimesos.cluster;\n\n/**\n * Mesos DNS\n */\npublic interface MesosDns extends ClusterProcess {\n\n}\n"
  },
  {
    "path": "minimesos/src/main/java/com/containersol/minimesos/cluster/MesosMaster.java",
    "content": "package com.containersol.minimesos.cluster;\n\n/**\n * Functionality of Mesos Master\n */\npublic interface MesosMaster extends MesosContainer {\n\n    String getStateUrl();\n\n    void waitFor();\n\n}\n"
  },
  {
    "path": "minimesos/src/main/java/com/containersol/minimesos/cluster/Registrator.java",
    "content": "package com.containersol.minimesos.cluster;\n\n/**\n * Consul functionality\n */\npublic interface Registrator extends ClusterProcess {\n\n    void setConsul(Consul consul);\n}\n"
  },
  {
    "path": "minimesos/src/main/java/com/containersol/minimesos/cluster/ZooKeeper.java",
    "content": "package com.containersol.minimesos.cluster;\n\n/**\n * Expected from ZooKeeper functionality\n */\npublic interface ZooKeeper extends ClusterProcess {\n\n    /**\n     * @return ZooKeeper URL based on real IP address\n     */\n    String getFormattedZKAddress();\n\n}\n"
  },
  {
    "path": "minimesos/src/main/java/com/containersol/minimesos/docker/DockerClientFactory.java",
    "content": "package com.containersol.minimesos.docker;\n\nimport com.github.dockerjava.api.DockerClient;\nimport com.github.dockerjava.core.DefaultDockerClientConfig;\nimport com.github.dockerjava.core.DockerClientBuilder;\nimport com.github.dockerjava.core.DockerClientConfig;\nimport org.apache.commons.lang.StringUtils;\n\n/**\n * Factory for creating {@link DockerClient}s\n */\npublic class DockerClientFactory {\n\n    private static DockerClient dockerClient;\n\n    public static DockerClient build() {\n        if (dockerClient == null) {\n            DefaultDockerClientConfig.Builder builder = new DefaultDockerClientConfig.Builder();\n            builder = builder.withApiVersion(\"1.12\");\n\n            String dockerHostEnv = System.getenv(\"DOCKER_HOST\");\n            if (StringUtils.isBlank(dockerHostEnv)) {\n                builder.withDockerHost(\"unix:///var/run/docker.sock\");\n            }\n\n            DockerClientConfig config = builder.build();\n            dockerClient = DockerClientBuilder.getInstance(config).build();\n        }\n        return dockerClient;\n    }\n\n}\n"
  },
  {
    "path": "minimesos/src/main/java/com/containersol/minimesos/docker/DockerContainersUtil.java",
    "content": "package com.containersol.minimesos.docker;\n\nimport java.util.ArrayList;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Optional;\nimport java.util.Set;\nimport java.util.concurrent.CompletableFuture;\nimport java.util.concurrent.ExecutionException;\nimport java.util.concurrent.TimeUnit;\nimport java.util.concurrent.TimeoutException;\n\nimport com.containersol.minimesos.MinimesosException;\nimport com.github.dockerjava.api.command.InspectContainerResponse;\nimport com.github.dockerjava.api.command.LogContainerCmd;\nimport com.github.dockerjava.api.exception.DockerException;\nimport com.github.dockerjava.api.model.Container;\nimport com.github.dockerjava.api.model.Frame;\nimport com.github.dockerjava.api.model.PullResponseItem;\nimport com.github.dockerjava.core.command.LogContainerResultCallback;\nimport com.github.dockerjava.core.command.PullImageResultCallback;\n\n/**\n * Immutable utility class, which represents set of docker containers with filters and operations on this list\n */\npublic class DockerContainersUtil {\n\n    private final List<Container> containers;\n\n    private DockerContainersUtil(List<Container> containers) {\n        this.containers = containers;\n    }\n\n    /**\n     * Use this getter if you need to iterate over docker objects\n     *\n     * @return set of docker containers\n     */\n    public List<Container> getContainers() {\n        return containers;\n    }\n\n    /**\n     * @param showAll should the list include stopped containers\n     * @return set of docker containers\n     */\n    public static DockerContainersUtil getContainers(boolean showAll) {\n        List<Container> newContainers = new ArrayList<>(DockerClientFactory.build().listContainersCmd().withShowAll(showAll).exec());\n        return new DockerContainersUtil(newContainers);\n    }\n\n    public int size() {\n        return (containers != null) ? containers.size() : 0;\n    }\n\n    /**\n     * Filters the set based on the container name\n     *\n     * @param pattern regular expression pattern of the container name\n     * @return filtered set\n     */\n    public DockerContainersUtil filterByName(String pattern) {\n        if (this.containers == null) {\n            return this;\n        }\n\n        List<Container> matched = new ArrayList<>();\n        for (Container container : containers) {\n            String[] names = container.getNames();\n            for (String name : names) {\n                // all names start with '/'\n                if (name.substring(1).matches(pattern)) {\n                    matched.add(container);\n                }\n            }\n        }\n\n        return new DockerContainersUtil(matched);\n    }\n\n    /**\n     * Filters the set based on the constainer name\n     *\n     * @param pattern regular expression pattern of the container name\n     * @return filtered set\n     */\n    public DockerContainersUtil filterByImage(String pattern) {\n        if (this.containers == null) {\n            return this;\n        }\n\n        List<Container> matched = new ArrayList<>();\n        for (Container container : containers) {\n            if (container.getImage().matches(pattern)) {\n                matched.add(container);\n            }\n        }\n\n        return new DockerContainersUtil(matched);\n    }\n\n    /**\n     * Removes all containers in the util object\n     */\n    public void remove() {\n        if (containers != null) {\n            for (Container container : containers) {\n                DockerClientFactory.build().removeContainerCmd(container.getId()).withForce(true).withRemoveVolumes(true).exec();\n            }\n        }\n    }\n\n    /**\n     * Removes all containers in the util object\n     */\n    public DockerContainersUtil kill() {\n        return kill(false);\n    }\n\n    /**\n     * Removes all containers in the util object\n     *\n     * @param ignoreFailure - use <code>true</code> if you expect containers might be stopped by this time\n     */\n    public DockerContainersUtil kill(boolean ignoreFailure) {\n        if (containers != null) {\n            for (Container container : containers) {\n                try {\n                    DockerClientFactory.build().killContainerCmd(container.getId()).exec();\n                } catch (DockerException failure) {\n                    if (!ignoreFailure) {\n                        throw failure;\n                    }\n                }\n            }\n        }\n        return this;\n    }\n\n    /**\n     * @return IP addresses of containers\n     */\n    public Set<String> getIpAddresses() {\n        Set<String> ips = new HashSet<>();\n        if (containers != null) {\n            for (Container container : containers) {\n                ips.add(getIpAddress(container.getId()));\n            }\n        }\n        return ips;\n    }\n\n    /**\n     * @param containerId id of the container to inspect\n     * @return IP Address of the container\n     */\n    public static String getIpAddress(String containerId) {\n        InspectContainerResponse response = DockerClientFactory.build().inspectContainerCmd(containerId).exec();\n        return response.getNetworkSettings().getIpAddress();\n    }\n\n    /**\n     * Synchronized method for returning logs of docker container\n     *\n     * @param containerId - ID of the container ot lookup logs\n     * @return list of strings, where every string is log line\n     */\n    public static List<String> getDockerLogs(String containerId) {\n\n        final List<String> logs = new ArrayList<>();\n\n        LogContainerCmd logContainerCmd = DockerClientFactory.build().logContainerCmd(containerId);\n        logContainerCmd.withStdOut(true).withStdErr(true);\n        try {\n            logContainerCmd.exec(new LogContainerResultCallback() {\n                @Override\n                public void onNext(Frame item) {\n                    logs.add(item.toString());\n                }\n            }).awaitCompletion();\n        } catch (InterruptedException e) {\n            throw new MinimesosException(\"Failed to retrieve logs of container \" + containerId, e);\n        }\n\n        return logs;\n    }\n\n    /**\n     * Pulls a Docker image with given name and version. Throws exception when it times out after given timeout.\n     *\n     * @param imageName    image to pull\n     * @param imageVersion image version to pull\n     * @param timeoutSecs  pulling timeout in seconds\n     */\n    public static void pullImage(String imageName, String imageVersion, long timeoutSecs) {\n        try {\n            final CompletableFuture<Void> result = new CompletableFuture<>();\n            DockerClientFactory.build().pullImageCmd(imageName).withTag(imageVersion).exec(new PullImageResultCallback()).awaitCompletion();\n        } catch (InterruptedException | RuntimeException e) {\n            throw new MinimesosException(\"Error pulling image or image not found in registry: \" + imageName + \":\" + imageVersion, e);\n        }\n    }\n\n    /**\n     * @return IP Address of the container's gateway (which would be docker0)\n     */\n    public static String getGatewayIpAddress(String containerId) {\n        InspectContainerResponse response = DockerClientFactory.build().inspectContainerCmd(containerId).exec();\n        return response.getNetworkSettings().getGateway();\n    }\n\n    /**\n     * @param containerId id of the container to retrieve\n     * @return container or null\n     */\n    public static Container getContainer(String containerId) {\n        List<Container> containers = DockerClientFactory.build().listContainersCmd().withShowAll(true).exec();\n        Container container = null;\n        if (containers != null && !containers.isEmpty()) {\n            Optional<Container> optional = containers.stream().filter(c -> c.getId().equals(containerId)).findFirst();\n            if (optional.isPresent()) {\n                container = optional.get();\n            }\n        }\n        return container;\n    }\n}\n"
  },
  {
    "path": "minimesos/src/main/java/com/containersol/minimesos/integrationtest/container/AbstractContainer.java",
    "content": "package com.containersol.minimesos.integrationtest.container;\n\nimport java.net.URI;\nimport java.net.URISyntaxException;\nimport java.security.SecureRandom;\nimport java.util.List;\nimport java.util.concurrent.TimeUnit;\n\nimport com.containersol.minimesos.MinimesosException;\nimport com.containersol.minimesos.cluster.ClusterProcess;\nimport com.containersol.minimesos.cluster.MesosCluster;\nimport com.containersol.minimesos.config.ContainerConfig;\nimport com.containersol.minimesos.docker.DockerClientFactory;\nimport com.containersol.minimesos.docker.DockerContainersUtil;\nimport com.github.dockerjava.api.command.CreateContainerCmd;\nimport com.github.dockerjava.api.model.Container;\nimport com.github.dockerjava.api.model.Image;\nimport com.jayway.awaitility.core.ConditionTimeoutException;\n\nimport org.apache.commons.lang.StringUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport static com.jayway.awaitility.Awaitility.await;\n\n/**\n * Extend this class to start and manage your own containers\n */\npublic abstract class AbstractContainer implements ClusterProcess {\n\n    private static final Logger LOGGER = LoggerFactory.getLogger(AbstractContainer.class);\n\n    private static final int IMAGE_PULL_TIMEOUT_SECS = 30;\n\n    private MesosCluster cluster;\n    private final ContainerConfig config;\n    private final String uuid;\n    private String containerId;\n    private String ipAddress = null;\n\n    protected AbstractContainer(ContainerConfig config) {\n        this.config = config;\n        this.uuid = Integer.toUnsignedString(new SecureRandom().nextInt());\n    }\n\n    public AbstractContainer(MesosCluster cluster, String uuid, String containerId, ContainerConfig config) {\n        this.cluster = cluster;\n        this.uuid = uuid;\n        this.containerId = containerId;\n        this.config = config;\n    }\n\n    /**\n     * Implement this method to pull your image. This will be called before the container is run.\n     */\n    public void pullImage() {\n        pullImage(getImageName(), getImageTag());\n    }\n\n    /**\n     * @return name of the container to use\n     */\n    public String getImageName() {\n        return config.getImageName();\n    }\n\n    /**\n     * @return verstion of the container to use\n     */\n    public String getImageTag() {\n        return config.getImageTag();\n    }\n\n    /**\n     * Implement this method to create your container.\n     *\n     * @return Your {@link CreateContainerCmd} for docker.\n     */\n    protected abstract CreateContainerCmd dockerCommand();\n\n    /**\n     * Starts the container and waits until is started\n     *\n     * @param timeout in seconds\n     */\n    @Override\n    public void start(int timeout) {\n        if (containerId != null) {\n            return;\n        }\n\n        pullImage();\n\n        CreateContainerCmd createCommand = dockerCommand();\n        LOGGER.debug(\"Creating container [\" + createCommand.getName() + \"]\");\n        containerId = createCommand.exec().getId();\n\n        DockerClientFactory.build().startContainerCmd(containerId).exec();\n\n        try {\n\n            await(\"Container did not start within \" + timeout + \" seconds\").atMost(timeout, TimeUnit.SECONDS).pollDelay(1, TimeUnit.SECONDS).until(() -> {\n                List<Container> containers = DockerClientFactory.build().listContainersCmd().withShowAll(true).exec();\n                for (Container container : containers) {\n                    if (container.getId().equals(containerId)) {\n                        return true;\n                    }\n                }\n                return false;\n            });\n\n        } catch (ConditionTimeoutException cte) {\n            String errorMessage = String.format(\"Container [%s] did not start within %d seconds.\", createCommand.getName(), timeout);\n            LOGGER.error(errorMessage);\n            try {\n                for (String logLine : DockerContainersUtil.getDockerLogs(containerId)) {\n                    LOGGER.error(logLine);\n                }\n            } catch (Exception e) {\n                LOGGER.error(\"Could not print container logs\", e);\n            }\n            throw new MinimesosException(errorMessage + \" See container logs above\");\n        }\n\n        LOGGER.debug(String.format(\"Container %s is up and running\", containerId));\n    }\n\n    /**\n     * @return the ID of the container.\n     */\n    @Override\n    public String getContainerId() {\n        return containerId;\n    }\n\n    @Override\n    public URI getServiceUrl() {\n\n        URI serviceUri = null;\n\n        String protocol = getServiceProtocol();\n        String host = getIpAddress();\n        int port = getServicePort();\n        String path = getServicePath();\n\n        if (StringUtils.isNotEmpty(host)) {\n            try {\n                serviceUri = new URI(protocol, null, host, port, path, null, null);\n            } catch (URISyntaxException e) {\n                throw new MinimesosException(\"Failed to form service URL for \" + getName(), e);\n            }\n        }\n\n        return serviceUri;\n    }\n\n    /**\n     * Enables derived classes to override\n     * @return protocol of the service\n     */\n    protected String getServiceProtocol() {\n        return \"http\";\n    }\n\n    /**\n     * Enables derived classes to override\n     * @return port of the service\n     */\n    protected int getServicePort() {\n        return 80;\n    }\n\n    /**\n     * Enables derived classes to override\n     * @return protocol of the service\n     */\n    protected String getServicePath() {\n        return \"\";\n    }\n\n    /**\n     * @return the IP address of the container\n     */\n    @Override\n    public String getIpAddress() {\n        if (ipAddress == null) {\n            retrieveIpAddress();\n        }\n        return ipAddress;\n    }\n\n    private synchronized void retrieveIpAddress() {\n        String res = \"\";\n        if (!getContainerId().isEmpty()) {\n            res = DockerContainersUtil.getIpAddress(getContainerId());\n        }\n        this.ipAddress = res;\n    }\n\n    /**\n     * Builds container name following the naming convention\n     *\n     * @return container name\n     */\n    @Override\n    public String getName() {\n        return ContainerName.get(this);\n    }\n\n    /**\n     * Removes a container with force\n     */\n    @Override\n    public void remove() {\n        try {\n            if (DockerContainersUtil.getContainer(containerId) != null) {\n                DockerClientFactory.build().removeContainerCmd(containerId).withForce(true).withRemoveVolumes(true).exec();\n            }\n        } catch (Exception e) {\n            LOGGER.error(\"Could not remove container \" + dockerCommand().getName(), e);\n        }\n    }\n\n    protected Boolean imageExists(String imageName, String registryTag) {\n        List<Image> images = DockerClientFactory.build().listImagesCmd().exec();\n        if (images.isEmpty()) {\n            throw new MinimesosException(\"Failed to find image '\" +  imageName + \":\" + registryTag + \". No images found\");\n        }\n        for (Image image : images) {\n            if (image.getRepoTags() == null) {\n                continue;\n            }\n            for (String repoTag : image.getRepoTags()) {\n                if (repoTag.equals(imageName + \":\" + registryTag)) {\n                    return true;\n                }\n            }\n        }\n        return false;\n    }\n\n    protected void pullImage(String imageName, String registryTag) {\n        LOGGER.debug(\"Checking if image [\" + imageName + \":\" + registryTag + \"] exists.\");\n\n        if (imageExists(imageName, registryTag)) {\n            return;\n        }\n\n        LOGGER.debug(\"Image [\" + imageName + \":\" + registryTag + \"] not found. Pulling...\");\n        DockerContainersUtil.pullImage(imageName, registryTag, IMAGE_PULL_TIMEOUT_SECS);\n\n        if (!imageExists(imageName, registryTag)) {\n            throw new MinimesosException(\"Pulling of \" + imageName + \":\" + registryTag + \" completed. However the image is not found\");\n        }\n    }\n\n    @Override\n    public void setCluster(MesosCluster cluster) {\n        this.cluster = cluster;\n    }\n\n    @Override\n    public MesosCluster getCluster() {\n        return cluster;\n    }\n\n    /**\n     * @return if set, ID of the cluster the container belongs to\n     */\n    public String getClusterId() {\n        return (cluster != null) ? cluster.getClusterId() : null;\n    }\n\n    @Override\n    public String toString() {\n        return String.format(\": %s-%s-%s\", getRole(), getClusterId(), uuid);\n    }\n\n    public String getUuid() {\n        return uuid;\n    }\n\n    @Override\n    public boolean equals(Object o) {\n        if (this == o) return true;\n        if (o == null || getClass() != o.getClass()) return false;\n\n        AbstractContainer that = (AbstractContainer) o;\n\n        if (!StringUtils.equals(this.getClusterId(), that.getClusterId())) return false;\n\n        if (!uuid.equals(that.uuid)) return false;\n        return containerId.equals(that.containerId);\n    }\n\n    @Override\n    public int hashCode() {\n        int result = (cluster != null) ? cluster.hashCode() : 0;\n        result = 31 * result + uuid.hashCode();\n        result = 31 * result + containerId.hashCode();\n        return result;\n    }\n}\n\n"
  },
  {
    "path": "minimesos/src/main/java/com/containersol/minimesos/integrationtest/container/ContainerName.java",
    "content": "package com.containersol.minimesos.integrationtest.container;\n\n/**\n * Utility class to assist container naming convention\n */\npublic class ContainerName {\n\n    // disable creation on instances\n    private ContainerName() {\n    }\n\n    public static String getContainerNamePattern(String clusterId) {\n        return \"^minimesos-\\\\w+-\" + clusterId + \"-\\\\w+$\";\n    }\n\n    /**\n     * @param container to build the name for\n     * @return name of the container\n     */\n    public static String get(AbstractContainer container) {\n        return String.format(\"minimesos-%s-%s-%s\", container.getRole(), container.getClusterId(), container.getUuid());\n    }\n\n    /**\n     * Based on container name check if it has the given role in the cluster\n     *\n     * @param containerName to analyse\n     * @param clusterId     cluster to check\n     * @param role          role to check\n     * @return true if container has the role\n     */\n    public static boolean hasRoleInCluster(String containerName, String clusterId, String role) {\n        String expectedStart = String.format(\"minimesos-%s-%s-\", role, clusterId);\n        return containerName.startsWith(expectedStart);\n    }\n\n    /**\n     * Based on container name check if it has the given role in the cluster\n     *\n     * @param dockerNames as returned by <code>container.getNames()</code>\n     * @param clusterId   cluster to check\n     * @param role        role to check\n     * @return true if container has the role\n     */\n    public static boolean hasRoleInCluster(String[] dockerNames, String clusterId, String role) {\n        String name = getFromDockerNames(dockerNames);\n        return hasRoleInCluster(name, clusterId, role);\n    }\n\n    /**\n     * @return true, if container with this name belongs to the cluster\n     */\n    public static boolean belongsToCluster(String containerName, String clusterId) {\n        String pattern = getContainerNamePattern(clusterId);\n        return containerName.matches(pattern);\n    }\n\n    /**\n     * @return true, if container with these docker names belongs to the cluster\n     */\n    public static boolean belongsToCluster(String[] dockerNames, String clusterId) {\n        String name = getFromDockerNames(dockerNames);\n        return belongsToCluster(name, clusterId);\n    }\n\n    /**\n     * Docker supports multiple names for a single container, when the container is linked from others.\n     * This method selects the original name of the container and removes leading \"/\"\n     *\n     * @param dockerNames names, as they returned by <code>container.getNames()</code>\n     * @return name of the container, which is not inherited from link\n     */\n    public static String getFromDockerNames(String[] dockerNames) {\n        String name = null;\n        for (String dockerName : dockerNames) {\n            String slashLess = dockerName;\n            if (dockerName.startsWith(\"/\")) {\n                slashLess = dockerName.substring(1);\n            }\n            if (!slashLess.contains(\"/\")) {\n                name = slashLess;\n                break;\n            }\n        }\n        return name;\n    }\n\n}\n"
  },
  {
    "path": "minimesos/src/main/java/com/containersol/minimesos/junit/MesosClusterTestRule.java",
    "content": "package com.containersol.minimesos.junit;\n\nimport java.io.FileInputStream;\nimport java.io.FileNotFoundException;\nimport java.io.IOException;\nimport java.io.InputStream;\n\nimport com.containersol.minimesos.MinimesosException;\nimport com.containersol.minimesos.cluster.MesosCluster;\nimport com.containersol.minimesos.cluster.MesosClusterFactory;\nimport com.containersol.minimesos.mesos.MesosClusterContainersFactory;\n\nimport org.junit.rules.TestRule;\nimport org.junit.runner.Description;\nimport org.junit.runners.model.Statement;\n\n/**\n * JUnit Rule extension of Mesos Cluster to use in JUnit.\n */\npublic class MesosClusterTestRule implements TestRule {\n\n    private MesosClusterFactory factory = new MesosClusterContainersFactory();\n\n    private MesosCluster mesosCluster;\n\n    public static MesosClusterTestRule fromClassPath(String path) {\n        try (InputStream is = MesosClusterTestRule.class.getResourceAsStream(path)) {\n            MesosCluster cluster = new MesosClusterContainersFactory().createMesosCluster(is);\n            return new MesosClusterTestRule(cluster);\n        } catch (IOException e) {\n            throw new MinimesosException(\"Could not read minimesosFile on classpath \" + path, e);\n        }\n    }\n\n    public static MesosClusterTestRule fromFile(String minimesosFilePath) {\n        try {\n            MesosCluster cluster = new MesosClusterContainersFactory().createMesosCluster(new FileInputStream(minimesosFilePath));\n            return new MesosClusterTestRule(cluster);\n        } catch (FileNotFoundException e) {\n            throw new MinimesosException(\"Could not read minimesosFile at \" + minimesosFilePath, e);\n        }\n    }\n\n    private MesosClusterTestRule(MesosCluster mesosCluster) {\n        this.mesosCluster = mesosCluster;\n    }\n\n    /**\n     * Modifies the method-running {@link Statement} to implement this test-running rule.\n     *\n     * @param base        The {@link Statement} to be modified\n     * @param description A {@link Description} of the test implemented in {@code base}\n     * @return a new statement, which may be the same as {@code base}, a wrapper around {@code base}, or a completely new Statement.\n     */\n    @Override\n    public Statement apply(Statement base, Description description) {\n        return new Statement() {\n            @Override\n            public void evaluate() throws Throwable {\n                before();\n                try {\n                    base.evaluate();\n                } finally {\n                    after();\n                }\n            }\n        };\n    }\n\n    /**\n     * Execute before the test\n     */\n    protected void before() {\n        mesosCluster.start();\n        Runtime.getRuntime().addShutdownHook(new Thread() {\n            @Override\n            public void run() {\n                factory.destroyRunningCluster(mesosCluster.getClusterId());\n            }\n        });\n    }\n\n    /**\n     * Execute after the test\n     */\n    protected void after() {\n        stop();\n    }\n\n    /**\n     * Destroys cluster using docker based factory of cluster members\n     */\n    public void stop() {\n        mesosCluster.destroy(factory);\n    }\n\n    public MesosCluster getMesosCluster() {\n        return mesosCluster;\n    }\n\n    public MesosClusterFactory getFactory() {\n        return factory;\n    }\n}\n"
  },
  {
    "path": "minimesos/src/main/java/com/containersol/minimesos/marathon/MarathonContainer.java",
    "content": "package com.containersol.minimesos.marathon;\n\nimport com.containersol.minimesos.MinimesosException;\nimport com.containersol.minimesos.cluster.ClusterProcess;\nimport com.containersol.minimesos.cluster.ClusterUtil;\nimport com.containersol.minimesos.cluster.Marathon;\nimport com.containersol.minimesos.cluster.MesosCluster;\nimport com.containersol.minimesos.cluster.ZooKeeper;\nimport com.containersol.minimesos.config.AppConfig;\nimport com.containersol.minimesos.config.GroupConfig;\nimport com.containersol.minimesos.config.MarathonConfig;\nimport com.containersol.minimesos.integrationtest.container.AbstractContainer;\nimport com.containersol.minimesos.docker.DockerClientFactory;\nimport com.containersol.minimesos.docker.DockerContainersUtil;\nimport com.containersol.minimesos.util.Environment;\nimport com.containersol.minimesos.util.CollectionsUtils;\nimport com.github.dockerjava.api.command.CreateContainerCmd;\nimport com.github.dockerjava.api.model.ExposedPort;\nimport com.github.dockerjava.api.model.Ports;\nimport com.mashape.unirest.http.Unirest;\nimport com.mashape.unirest.http.exceptions.UnirestException;\nimport mesosphere.marathon.client.model.v2.Group;\nimport mesosphere.marathon.client.model.v2.Result;\nimport org.apache.commons.io.IOUtils;\nimport org.apache.commons.lang.StringUtils;\nimport org.json.JSONArray;\nimport org.json.JSONObject;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport mesosphere.marathon.client.model.v2.App;\nimport mesosphere.marathon.client.MarathonClient;\nimport mesosphere.marathon.client.utils.MarathonException;\nimport com.google.gson.Gson;\n\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.net.URI;\nimport java.net.URISyntaxException;\nimport java.util.List;\nimport java.util.concurrent.Callable;\nimport java.util.concurrent.TimeUnit;\n\nimport static com.containersol.minimesos.config.MarathonConfig.*;\nimport static com.jayway.awaitility.Awaitility.await;\nimport static java.lang.String.format;\nimport static javax.ws.rs.core.MediaType.APPLICATION_JSON;\n\n/**\n * Marathon is a cluster-wide init and control system for services. See https://mesosphere.github.io/marathon/docs/\n */\npublic class MarathonContainer extends AbstractContainer implements Marathon {\n\n    private static final Logger LOGGER = LoggerFactory.getLogger(MarathonContainer.class);\n\n    private static final String TOKEN_HOST_DIR = \"MINIMESOS_HOST_DIR\";\n\n    private static final String APPS_ENDPOINT = \"/v2/apps\";\n\n    private static final String HEADER_ACCEPT = \"accept\";\n\n    private final MarathonConfig config;\n\n    private ZooKeeper zooKeeper;\n\n    public MarathonContainer(MarathonConfig config) {\n        super(config);\n        this.config = config;\n    }\n\n    public MarathonContainer(MesosCluster cluster, String uuid, String containerId) {\n        this(cluster, uuid, containerId, new MarathonConfig());\n    }\n\n    private MarathonContainer(MesosCluster cluster, String uuid, String containerId, MarathonConfig config) {\n        super(cluster, uuid, containerId, config);\n        this.config = config;\n    }\n\n    @Override\n    public String getRole() {\n        return \"marathon\";\n    }\n\n    @Override\n    public void setZooKeeper(ZooKeeper zooKeeper) {\n        this.zooKeeper = zooKeeper;\n    }\n\n    @Override\n    public URI getServiceUrl() {\n        URI serviceUri = null;\n\n        String protocol = getServiceProtocol();\n\n        String host;\n        if (Environment.isRunningInJvmOnMacOsX()) {\n            host = \"localhost\";\n        } else {\n            host = getIpAddress();\n        }\n\n        int port = getServicePort();\n        String path = getServicePath();\n\n        if (StringUtils.isNotEmpty(host)) {\n            try {\n                serviceUri = new URI(protocol, null, host, port, path, null, null);\n            } catch (URISyntaxException e) {\n                throw new MinimesosException(\"Failed to form service URL for \" + getName(), e);\n            }\n        }\n\n        return serviceUri;\n    }\n\n    @Override\n    protected CreateContainerCmd dockerCommand() {\n        ExposedPort exposedPort = ExposedPort.tcp(MARATHON_PORT);\n        Ports portBindings = new Ports();\n        if (getCluster().isMapPortsToHost()) {\n            portBindings.bind(exposedPort, Ports.Binding.bindPort(MARATHON_PORT));\n        }\n        return DockerClientFactory.build().createContainerCmd(config.getImageName() + \":\" + config.getImageTag())\n                .withName(getName())\n                .withExtraHosts(\"minimesos-zookeeper:\" + this.zooKeeper.getIpAddress())\n                .withCmd(CollectionsUtils.splitCmd(config.getCmd()))\n                .withExposedPorts(exposedPort)\n                .withPortBindings(portBindings);\n    }\n\n    /**\n     * Returns a Marathon endpoint\n     *\n     * @return String endpoint\n     */\n    private String getMarathonEndpoint() {\n        return getServiceUrl().toString();\n    }\n\n    /**\n     * Deploys a Marathon app by JSON string\n     *\n     * @param marathonJson JSON string\n     */\n    @Override\n    public void deployApp(String marathonJson) {\n        mesosphere.marathon.client.Marathon marathon = MarathonClient.getInstance(getMarathonEndpoint());\n        try {\n            marathon.createApp(constructApp(marathonJson));\n        } catch (MarathonException e) {\n            throw new MinimesosException(\"Marathon did not accept the app, error: \" + e.toString());\n        }\n        LOGGER.debug(format(\"Installed app at '%s'\", getMarathonEndpoint()));\n    }\n\n    @Override\n    public Result deleteApp(String appId) {\n        mesosphere.marathon.client.Marathon marathon = MarathonClient.getInstance(getMarathonEndpoint());\n        try {\n            Result result = marathon.deleteApp(appId);\n            LOGGER.debug(format(\"Deleted app '%s' at '%s'\", appId, getMarathonEndpoint()));\n            return result;\n        } catch (MarathonException e) {\n            throw new MinimesosException(\"Could not delete app '\" + appId  + \"'. \" + e.getMessage());\n        }\n    }\n\n    @Override\n    public void deployGroup(String groupJson) {\n        mesosphere.marathon.client.Marathon marathon = MarathonClient.getInstance(getMarathonEndpoint());\n        try {\n            Group group = constructGroup(groupJson);\n            marathon.createGroup(group);\n        } catch (Exception e) {\n            throw new MinimesosException(\"Marathon did not accept the app, error: \" + e.toString(), e);\n        }\n        LOGGER.debug(format(\"Installing group at %s\", getMarathonEndpoint()));\n    }\n\n    @Override\n    public Result deleteGroup(String groupId) {\n        mesosphere.marathon.client.Marathon marathon = MarathonClient.getInstance(getMarathonEndpoint());\n        try {\n            Result result = marathon.deleteGroup(groupId);\n            LOGGER.debug(format(\"Deleted app '%s' at '%s'\", groupId, getMarathonEndpoint()));\n            return result;\n        } catch (MarathonException e) {\n            throw new MinimesosException(\"Could not delete group '\" + groupId  + \"'. \" + e.getMessage());\n        }\n    }\n\n    /**\n     * Updates a Marathon app by JSON string\n     *\n     * @param marathonJson JSON string\n     */\n    @Override\n    public void updateApp(String marathonJson) {\n        mesosphere.marathon.client.Marathon marathon = MarathonClient.getInstance(getMarathonEndpoint());\n        try {\n            App app = constructApp(marathonJson);\n            marathon.updateApp(app.getId(), app, true);\n        } catch (MarathonException e) {\n            throw new MinimesosException(\"Marathon could not update the app, error: \" + e.toString());\n        }\n        LOGGER.debug(format(\"Installing an app on marathon %s\", getMarathonEndpoint()));\n    }\n\n    private Group constructGroup(String groupJson) {\n        Gson gson = new Gson();\n        return gson.fromJson(replaceTokens(groupJson), Group.class);\n    }\n\n    private App constructApp(String appJson) {\n        Gson gson = new Gson();\n        return gson.fromJson(replaceTokens(appJson), App.class);\n    }\n\n    /**\n     * Replaces ${MINIMESOS_[ROLE]}, ${MINIMESOS_[ROLE]_IP} and ${MINIMESOS_[ROLE]_PORT} tokens in the given string with actual values.\n     * Also supports ${NETWORK_GATEWAY}\n     *\n     * @param source string to replace values in\n     * @return updated string\n     */\n    public String replaceTokens(String source) {\n        MesosCluster cluster = getCluster();\n        // received JSON might contain tokens, which should be replaced before the installation\n        List<ClusterProcess> uniqueRoles = ClusterUtil.getDistinctRoleProcesses(cluster.getMemberProcesses());\n        String updatedJson = source;\n        for (ClusterProcess process : uniqueRoles) {\n            URI serviceUri = process.getServiceUrl();\n            if (serviceUri != null) {\n                updatedJson = replaceToken(updatedJson, MesosCluster.MINIMESOS_TOKEN_PREFIX + process.getRole().toUpperCase(), serviceUri.toString());\n                updatedJson = replaceToken(updatedJson, MesosCluster.MINIMESOS_TOKEN_PREFIX + process.getRole().toUpperCase() + \"_IP\", serviceUri.getHost());\n                updatedJson = replaceToken(updatedJson, MesosCluster.MINIMESOS_TOKEN_PREFIX + process.getRole().toUpperCase() + \"_PORT\", Integer.toString(serviceUri.getPort()));\n            }\n        }\n\n        // replace independent from roles tokens\n        String masterContainer = cluster.getMaster().getContainerId();\n        updatedJson = replaceToken(updatedJson, MesosCluster.TOKEN_NETWORK_GATEWAY, DockerContainersUtil.getGatewayIpAddress(masterContainer));\n        updatedJson = replaceToken(updatedJson, TOKEN_HOST_DIR, MesosCluster.getClusterHostDir().getAbsolutePath());\n\n        return updatedJson;\n    }\n\n    private static String replaceToken(String input, String token, String value) {\n        String tokenRegex = format(\"\\\\$\\\\{%s\\\\}\", token);\n        return input.replaceAll(tokenRegex, value);\n    }\n\n    /**\n     * Kill all apps that are currently running.\n     */\n    @Override\n    public void killAllApps() {\n        String marathonEndpoint = getServiceUrl().toString();\n        JSONObject appsResponse;\n        try {\n            appsResponse = Unirest.get(marathonEndpoint + APPS_ENDPOINT).header(HEADER_ACCEPT, APPLICATION_JSON).asJson().getBody().getObject();\n            if (appsResponse.length() == 0) {\n                return;\n            }\n        } catch (UnirestException e) {\n            throw new MinimesosException(\"Could not retrieve apps from Marathon at \" + marathonEndpoint, e);\n        }\n\n        JSONArray apps = appsResponse.getJSONArray(\"apps\");\n        for (int i = 0; i < apps.length(); i++) {\n            JSONObject app = apps.getJSONObject(i);\n            String appId = app.getString(\"id\");\n            try {\n                Unirest.delete(marathonEndpoint + APPS_ENDPOINT + appId).asJson();\n            } catch (UnirestException e) { //NOSONAR\n                // failed to delete one app; continue with others\n                LOGGER.error(\"Could not delete app \" + appId + \" at \" + marathonEndpoint, e);\n            }\n        }\n    }\n\n    @Override\n    protected int getServicePort() {\n        return MARATHON_PORT;\n    }\n\n    @SuppressWarnings(\"WeakerAccess\")\n    public void waitFor() {\n        LOGGER.debug(\"Waiting for Marathon to be ready at \" + getServiceUrl().toString());\n        await(\"Marathon did not start responding\").atMost(getCluster().getClusterConfig().getTimeout(), TimeUnit.SECONDS).pollDelay(1, TimeUnit.SECONDS).until(new MarathonApiIsReady());\n    }\n\n    public MarathonConfig getConfig() {\n        return config;\n    }\n\n    private class MarathonApiIsReady implements Callable<Boolean> {\n        @Override\n        public Boolean call() throws Exception {\n            try {\n                Unirest.get(getServiceUrl().toString() + APPS_ENDPOINT).header(HEADER_ACCEPT, APPLICATION_JSON).asJson().getBody().getObject();\n            } catch (UnirestException e) { //NOSONAR\n                // meaning API is not ready\n                return false;\n            }\n            return true;\n        }\n    }\n\n    /**\n     * If Marathon configuration requires, installs the applications\n     */\n    @Override\n    public void installMarathonApps() {\n        waitFor();\n\n        List<AppConfig> apps = getConfig().getApps();\n        for (AppConfig app : apps) {\n            try {\n                InputStream json = MesosCluster.getInputStream(app.getMarathonJson());\n                if (json != null) {\n                    deployApp(IOUtils.toString(json, \"UTF-8\"));\n                } else {\n                    throw new MinimesosException(\"Could not deploy app. Failed to find content of \" + app.getMarathonJson());\n                }\n            } catch (IOException ioe) {\n                throw new MinimesosException(\"Could not deploy app. Failed to load JSON from \" + app.getMarathonJson(), ioe);\n            }\n        }\n\n        List<GroupConfig> groups = getConfig().getGroups();\n        for (GroupConfig group : groups) {\n            try {\n\n                InputStream json = MesosCluster.getInputStream(group.getMarathonJson());\n                if (json != null) {\n                    deployGroup(IOUtils.toString(json, \"UTF-8\"));\n                } else {\n                    throw new MinimesosException(\"Could not deploy group. Failed to find content of \" + group.getMarathonJson());\n                }\n            } catch (IOException ioe) {\n                throw new MinimesosException(\"Could not deploy group. Failed to load JSON from \" + group.getMarathonJson(), ioe);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "minimesos/src/main/java/com/containersol/minimesos/mesos/ClusterContainers.java",
    "content": "package com.containersol.minimesos.mesos;\n\nimport com.containersol.minimesos.cluster.ClusterProcess;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Optional;\nimport java.util.function.Predicate;\n\n/**\n * Holds the containers and helper methods for the Mesos cluster\n */\npublic class ClusterContainers {\n\n    private final List<ClusterProcess> containers;\n\n    /**\n     * Create a container List from scratch\n     */\n    public ClusterContainers() {\n        containers = new ArrayList<>();\n    }\n\n    /**\n     * Create a container List from another List\n     *\n     * @param containers another List of {@link ClusterProcess}\n     */\n    public ClusterContainers(List<ClusterProcess> containers) {\n        this.containers = containers;\n    }\n\n    /**\n     * Add a container to the list of containers.\n     *\n     * @param container of type {@link ClusterProcess}\n     * @return this, for fluent adding.\n     */\n    public ClusterContainers add(ClusterProcess container) {\n        containers.add(container);\n        return this;\n    }\n\n    public List<ClusterProcess> getContainers() {\n        return containers;\n    }\n\n    /**\n     * Optionally get one of a certain type of type T. Note, this cast will always work because we are filtering on that type.\n     * If it doesn't find that type, the optional is empty so the cast doesn't need to be performed.\n     *\n     * @param filter A predicate that is true when an {@link ClusterProcess} in the list is of type T\n     * @param <T>    A container of type T that extends {@link ClusterProcess}\n     * @return the first container it comes across.\n     */\n    @SuppressWarnings(\"unchecked\")\n    public <T extends ClusterProcess> Optional<T> getOne(Predicate<ClusterProcess> filter) {\n        return (Optional<T>) getContainers().stream().filter(filter).findFirst();\n    }\n\n    /**\n     * Checks to see whether a container exists\n     *\n     * @param filter A predicate that is true when an {@link ClusterProcess} in the list is of type T\n     * @return true if it exists\n     */\n    public Boolean isPresent(Predicate<ClusterProcess> filter) {\n        return getOne(filter).isPresent();\n    }\n\n}\n"
  },
  {
    "path": "minimesos/src/main/java/com/containersol/minimesos/mesos/ConsulContainer.java",
    "content": "package com.containersol.minimesos.mesos;\n\nimport com.containersol.minimesos.MinimesosException;\nimport com.containersol.minimesos.cluster.Consul;\nimport com.containersol.minimesos.cluster.MesosCluster;\nimport com.containersol.minimesos.config.ConsulConfig;\nimport com.containersol.minimesos.integrationtest.container.AbstractContainer;\nimport com.containersol.minimesos.docker.DockerClientFactory;\nimport com.containersol.minimesos.util.Environment;\nimport com.github.dockerjava.api.command.CreateContainerCmd;\nimport com.github.dockerjava.api.model.ExposedPort;\nimport com.github.dockerjava.api.model.Ports;\nimport org.apache.commons.lang.StringUtils;\n\nimport java.net.URI;\nimport java.net.URISyntaxException;\n\n/**\n * This is the Consul-in-a-container container. Consul adds service discovery through DNS, and a distributed k/v store.\n */\npublic class ConsulContainer extends AbstractContainer implements Consul {\n\n    private final ConsulConfig config;\n\n    public ConsulContainer(ConsulConfig config) {\n        super(config);\n        this.config = config;\n    }\n\n    public ConsulContainer(MesosCluster cluster, String uuid, String containerId) {\n        this(cluster, uuid, containerId, new ConsulConfig());\n    }\n\n    private ConsulContainer(MesosCluster cluster, String uuid, String containerId, ConsulConfig config) {\n        super(cluster, uuid, containerId, config);\n        this.config = config;\n    }\n\n    @Override\n    public String getRole() {\n        return \"consul\";\n    }\n\n    @Override\n    protected int getServicePort() {\n        return ConsulConfig.CONSUL_HTTP_PORT;\n    }\n\n    @Override\n    public URI getServiceUrl() {\n        URI serviceUri = null;\n\n        String protocol = getServiceProtocol();\n\n        String host;\n        if (Environment.isRunningInJvmOnMacOsX()) {\n            host = \"localhost\";\n        } else {\n            host = getIpAddress();\n        }\n\n        int port = getServicePort();\n        String path = getServicePath();\n\n        if (StringUtils.isNotEmpty(host)) {\n            try {\n                serviceUri = new URI(protocol, null, host, port, path, null, null);\n            } catch (URISyntaxException e) {\n                throw new MinimesosException(\"Failed to form service URL for \" + getName(), e);\n            }\n        }\n\n        return serviceUri;\n    }\n\n    @Override\n    protected CreateContainerCmd dockerCommand() {\n        int port = getServicePort();\n        ExposedPort exposedPort = ExposedPort.tcp(port);\n\n        Ports portBindings = new Ports();\n        if (getCluster().isMapPortsToHost()) {\n            portBindings.bind(exposedPort, Ports.Binding.bindPort(port));\n        }\n\n        ExposedPort consulHTTPPort = ExposedPort.tcp(ConsulConfig.CONSUL_HTTP_PORT);\n        ExposedPort consulDNSPort = ExposedPort.udp(ConsulConfig.CONSUL_DNS_PORT);\n\n        return DockerClientFactory.build().createContainerCmd(config.getImageName() + \":\" + config.getImageTag())\n                .withName(getName())\n                .withCmd(\"agent\", \"-server\", \"-bootstrap\", \"-client\", \"0.0.0.0\")\n                .withExposedPorts(consulHTTPPort, consulDNSPort)\n                .withPortBindings(portBindings);\n    }\n\n}\n"
  },
  {
    "path": "minimesos/src/main/java/com/containersol/minimesos/mesos/MesosAgentContainer.java",
    "content": "package com.containersol.minimesos.mesos;\n\nimport com.containersol.minimesos.MinimesosException;\nimport com.containersol.minimesos.cluster.MesosAgent;\nimport com.containersol.minimesos.cluster.MesosCluster;\nimport com.containersol.minimesos.cluster.MesosDns;\nimport com.containersol.minimesos.config.MesosAgentConfig;\nimport com.containersol.minimesos.docker.DockerClientFactory;\nimport com.containersol.minimesos.state.Executor;\nimport com.containersol.minimesos.state.Framework;\nimport com.containersol.minimesos.state.State;\nimport com.containersol.minimesos.state.Task;\nimport com.containersol.minimesos.util.ResourceUtil;\nimport com.github.dockerjava.api.command.CreateContainerCmd;\nimport com.github.dockerjava.api.model.Bind;\nimport com.github.dockerjava.api.model.ExposedPort;\nimport com.github.dockerjava.api.model.Link;\nimport com.github.dockerjava.api.model.Volume;\nimport org.apache.http.client.utils.URIBuilder;\n\nimport java.net.URI;\nimport java.net.URISyntaxException;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.TreeMap;\n\nimport static com.containersol.minimesos.util.EnvironmentBuilder.newEnvironment;\n\n/**\n * Mesos Master adds the \"agent\" component for Apache Mesos\n */\npublic class MesosAgentContainer extends MesosContainerImpl implements MesosAgent {\n\n    private MesosAgentConfig config;\n\n    private final static String MESOS_AGENT_WORK_DIR = \"/var/lib/mesos/\";\n\n    private String hostName;\n\n    public MesosAgentContainer(MesosAgentConfig agentConfig) {\n        super(agentConfig);\n        this.config = agentConfig;\n        this.hostName = getRole() + \"-\" + getUuid();\n    }\n\n    public MesosAgentContainer(MesosCluster cluster, String uuid, String containerId) {\n        this(cluster, uuid, containerId, new MesosAgentConfig(cluster.getConfiguredMesosVersion()));\n    }\n\n    private MesosAgentContainer(MesosCluster cluster, String uuid, String containerId, MesosAgentConfig config) {\n        super(cluster, uuid, containerId, config);\n        this.config     = config;\n    }\n\n    @Override\n    public String getResources() {\n        return config.getResources().asMesosString();\n    }\n\n    public String getAttributeString(){\n        return config.getAttributes();\n    }\n\n    public MesosAgentConfig getConfig() {\n        return config;\n    }\n\n    @Override\n    public int getServicePort() {\n        return config.getPortNumber();\n    }\n\n    private CreateContainerCmd getBaseCommand() {\n        String hostDir = MesosCluster.getClusterHostDir().getAbsolutePath();\n        List<Bind> binds = new ArrayList<>();\n        binds.add(Bind.parse(\"/var/run/docker.sock:/var/run/docker.sock:rw\"));\n        binds.add(Bind.parse(\"/sys/fs/cgroup:/sys/fs/cgroup\"));\n        binds.add(Bind.parse(hostDir + \":\" + hostDir));\n        if (getCluster().getMapAgentSandboxVolume()) {\n            binds.add(Bind.parse(String.format(\"%s:%s:rw\", hostDir + \"/.minimesos/sandbox-\" + getClusterId() + \"/\" + hostName, MESOS_AGENT_WORK_DIR + hostName + \"/slaves\")));\n        }\n        CreateContainerCmd cmd = DockerClientFactory.build().createContainerCmd(getImageName() + \":\" + getImageTag())\n            .withName(getName())\n            .withHostName(hostName)\n            .withPrivileged(true)\n            .withVolumes(new Volume(MESOS_AGENT_WORK_DIR + hostName))\n            .withEnv(newEnvironment()\n                .withValues(getMesosAgentEnvVars())\n                .withValues(getSharedEnvVars())\n                .createEnvironment())\n            .withPidMode(\"host\")\n            .withLinks(new Link(getZooKeeper().getContainerId(), \"minimesos-zookeeper\"))\n            .withBinds(binds.stream().toArray(Bind[]::new));\n\n        MesosDns mesosDns = getCluster().getMesosDns();\n        if (mesosDns != null) {\n            cmd.withDns(mesosDns.getIpAddress());\n        }\n\n        return cmd;\n    }\n\n    @Override\n    public String getRole() {\n        return \"agent\";\n    }\n\n    @Override\n    protected CreateContainerCmd dockerCommand() {\n        ArrayList<ExposedPort> exposedPorts = new ArrayList<>();\n        exposedPorts.add(new ExposedPort(getServicePort()));\n\n        ArrayList<Integer> resourcePorts = ResourceUtil.parsePorts(getResources());\n        for (Integer port : resourcePorts) {\n            exposedPorts.add(new ExposedPort(port));\n        }\n\n        return getBaseCommand()\n                .withExposedPorts(exposedPorts.toArray(new ExposedPort[exposedPorts.size()]));\n    }\n\n    private Map<String, String> getMesosAgentEnvVars() {\n        Map<String, String> envs = new TreeMap<>();\n        envs.put(\"GLOG_v\", \"1\");\n        envs.put(\"MESOS_RESOURCES\", getResources());\n        envs.put(\"MESOS_WORK_DIR\", MESOS_AGENT_WORK_DIR + hostName);\n        envs.put(\"MESOS_DOCKER_STORE_DIR\", MESOS_AGENT_WORK_DIR + hostName + \"/store/docker\");\n        envs.put(\"MESOS_ISOLATION\", \"filesystem/linux,docker/runtime,cgroups/cpu,cgroups/mem\");\n        envs.put(\"MESOS_IMAGE_PROVIDERS\", \"docker\");\n        envs.put(\"MESOS_SYSTEMD_ENABLE_SUPPORT\", \"false\");\n        envs.put(\"MESOS_ATTRIBUTES\", getAttributeString());\n        envs.put(\"MESOS_PORT\", String.valueOf(getServicePort()));\n        envs.put(\"MESOS_MASTER\", getFormattedZKAddress());\n        envs.put(\"MESOS_SWITCH_USER\", \"false\");\n        envs.put(\"MESOS_LOGGING_LEVEL\", getLoggingLevel());\n        envs.put(\"SERVICE_IGNORE\", \"1\");\n        return envs;\n    }\n\n}\n"
  },
  {
    "path": "minimesos/src/main/java/com/containersol/minimesos/mesos/MesosClusterContainersFactory.java",
    "content": "package com.containersol.minimesos.mesos;\n\nimport java.io.FileInputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.util.Comparator;\nimport java.util.List;\nimport java.util.function.Predicate;\n\nimport com.containersol.minimesos.MinimesosException;\nimport com.containersol.minimesos.cluster.ClusterProcess;\nimport com.containersol.minimesos.cluster.Consul;\nimport com.containersol.minimesos.cluster.Filter;\nimport com.containersol.minimesos.cluster.Marathon;\nimport com.containersol.minimesos.cluster.MesosAgent;\nimport com.containersol.minimesos.cluster.MesosCluster;\nimport com.containersol.minimesos.cluster.MesosClusterFactory;\nimport com.containersol.minimesos.cluster.MesosDns;\nimport com.containersol.minimesos.cluster.MesosMaster;\nimport com.containersol.minimesos.cluster.Registrator;\nimport com.containersol.minimesos.cluster.ZooKeeper;\nimport com.containersol.minimesos.config.ClusterConfig;\nimport com.containersol.minimesos.config.ConfigParser;\nimport com.containersol.minimesos.config.MesosMasterConfig;\nimport com.containersol.minimesos.integrationtest.container.ContainerName;\nimport com.containersol.minimesos.docker.DockerContainersUtil;\nimport com.containersol.minimesos.marathon.MarathonContainer;\nimport com.github.dockerjava.api.model.Container;\n\nimport com.github.dockerjava.api.model.ContainerPort;\nimport org.apache.commons.io.IOUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * Docker based factory of minimesos cluster members\n */\npublic class MesosClusterContainersFactory extends MesosClusterFactory {\n\n    private static final Logger LOGGER = LoggerFactory.getLogger(MesosClusterContainersFactory.class);\n\n    public ZooKeeper createZooKeeper(MesosCluster mesosCluster, String uuid, String containerId) {\n        return new ZooKeeperContainer(mesosCluster, uuid, containerId);\n    }\n\n    public MesosAgent createMesosAgent(MesosCluster mesosCluster, String uuid, String containerId) {\n        return new MesosAgentContainer(mesosCluster, uuid, containerId);\n    }\n\n    public MesosMaster createMesosMaster(MesosCluster mesosCluster, String uuid, String containerId) {\n        return new MesosMasterContainer(mesosCluster, uuid, containerId);\n    }\n\n    public Marathon createMarathon(MesosCluster mesosCluster, String uuid, String containerId) {\n        return new MarathonContainer(mesosCluster, uuid, containerId);\n    }\n\n    public Consul createConsul(MesosCluster mesosCluster, String uuid, String containerId) {\n        return new ConsulContainer(mesosCluster, uuid, containerId);\n    }\n\n    public Registrator createRegistrator(MesosCluster mesosCluster, String uuid, String containerId) {\n        return new RegistratorContainer(mesosCluster, uuid, containerId);\n    }\n\n    public MesosDns createMesosDns(MesosCluster cluster, String uuid, String containerId) {\n        return new MesosDnsContainer(cluster, uuid, containerId);\n    }\n\n    @Override\n    public void loadRunningCluster(MesosCluster cluster) {\n        String clusterId = cluster.getClusterId();\n        List<ClusterProcess> containers = cluster.getMemberProcesses();\n\n        List<Container> dockerContainers = DockerContainersUtil.getContainers(false).getContainers();\n        dockerContainers.sort(Comparator.comparingLong(Container::getCreated));\n\n        for (Container container : dockerContainers) {\n            String name = ContainerName.getFromDockerNames(container.getNames());\n            if (ContainerName.belongsToCluster(name, clusterId)) {\n\n                String containerId = container.getId();\n\n                String[] parts = name.split(\"-\");\n                if (parts.length > 3) {\n\n                    String role = parts[1];\n                    String uuid = parts[3];\n\n                    switch (role) {\n                        case \"zookeeper\":\n                            containers.add(createZooKeeper(cluster, uuid, containerId));\n                            break;\n                        case \"agent\":\n                            containers.add(createMesosAgent(cluster, uuid, containerId));\n                            break;\n                        case \"master\":\n                            MesosMaster master = createMesosMaster(cluster, uuid, containerId);\n                            containers.add(master);\n\n                            restoreMapToPorts(cluster, container);\n                            break;\n                        case \"marathon\":\n                            containers.add(createMarathon(cluster, uuid, containerId));\n                            break;\n                        case \"consul\":\n                            containers.add(createConsul(cluster, uuid, containerId));\n                            break;\n                        case \"registrator\":\n                            containers.add(createRegistrator(cluster, uuid, containerId));\n                            break;\n                        case \"mesosdns\":\n                            containers.add(createMesosDns(cluster, uuid, containerId));\n                    }\n                }\n            }\n        }\n    }\n\n    private void restoreMapToPorts(MesosCluster cluster, Container container) {\n        // Restore \"map ports to host\" attribute\n        ContainerPort[] ports = container.getPorts();\n        if (ports != null) {\n            for (ContainerPort port : ports) {\n                if (port.getIp() != null && port.getPrivatePort() == MesosMasterConfig.MESOS_MASTER_PORT) {\n                    cluster.setMapPortsToHost(true);\n                }\n            }\n        }\n    }\n\n    @Override\n    public void destroyRunningCluster(String clusterId) {\n        DockerContainersUtil.getContainers(true).filterByName(ContainerName.getContainerNamePattern(clusterId)).kill(true).remove();\n    }\n\n    public MesosCluster createMesosCluster(String path) {\n        try (InputStream is = new FileInputStream(path)) {\n            return createMesosCluster(is);\n        } catch (IOException e) {\n            LOGGER.debug(\"Could not read minimesos config: \", e.getMessage());\n            throw new MinimesosException(\"Could not read minimesos config: \" + e.getMessage());\n        }\n    }\n\n    public MesosCluster createMesosCluster(InputStream inputStream) {\n        try {\n            ClusterConfig clusterConfig = new ConfigParser().parse(IOUtils.toString(inputStream, \"UTF-8\"));\n            return createMesosCluster(clusterConfig);\n        } catch (IOException e) {\n            throw new MinimesosException(\"Could not read minimesos config:\" + e.getCause());\n        }\n    }\n\n    public MesosCluster createMesosCluster(ClusterConfig clusterConfig) {\n        LOGGER.debug(\"Creating Mesos cluster\");\n\n\n        ClusterContainers clusterContainers = createProcesses(clusterConfig);\n\n        validateProcesses(clusterContainers);\n\n        connectProcesses(clusterContainers);\n\n        return new MesosCluster(clusterConfig, clusterContainers.getContainers());\n    }\n\n    private static ClusterContainers createProcesses(ClusterConfig clusterConfig) {\n        LOGGER.debug(\"Creating cluster processes\");\n\n        ClusterContainers clusterContainers = new ClusterContainers();\n\n        ZooKeeperContainer zooKeeper = new ZooKeeperContainer(clusterConfig.getZookeeper());\n        clusterContainers.add(zooKeeper);\n\n        if (clusterConfig.getMesosdns() != null) {\n            clusterContainers.add(new MesosDnsContainer(clusterConfig.getMesosdns()));\n        }\n\n        MesosMasterContainer mesosMaster = new MesosMasterContainer(clusterConfig.getMaster());\n        clusterContainers.add(mesosMaster);\n\n        if (clusterConfig.getMarathon() != null) {\n            clusterContainers.add(new MarathonContainer(clusterConfig.getMarathon()));\n        }\n\n        clusterConfig.getAgents().forEach(config -> clusterContainers.add(new MesosAgentContainer(config)));\n\n        if (clusterConfig.getConsul() != null) {\n            clusterContainers.add(new ConsulContainer(clusterConfig.getConsul()));\n        }\n\n        if (clusterConfig.getRegistrator() != null) {\n            clusterContainers.add(new RegistratorContainer(clusterConfig.getRegistrator()));\n        }\n\n        return clusterContainers;\n    }\n\n    private static void validateProcesses(ClusterContainers clusterContainers) {\n        LOGGER.debug(\"Validating cluster processes\");\n\n        if (!isPresent(clusterContainers, Filter.mesosMaster())) {\n            throw new MinimesosException(\"Cluster requires a single Mesos Master. Please add one in the minimesosFile.\");\n        }\n\n        if (!isPresent(clusterContainers, Filter.zooKeeper())) {\n            throw new MinimesosException(\"Cluster requires a single ZooKeeper. Please add one in the minimesosFile.\");\n        }\n\n        if (!isPresent(clusterContainers, Filter.mesosAgent())) {\n            throw new MinimesosException(\"Cluster requires at least 1 Mesos Agent. Please add one in the minimesosFile.\");\n        }\n\n        if (isPresent(clusterContainers, Filter.registrator()) && !isPresent(clusterContainers, Filter.consul())) {\n            throw new MinimesosException(\"Registrator requires a single Consul. Please add consul in the minimesosFile.\");\n        }\n    }\n\n    private static void connectProcesses(ClusterContainers clusterContainers) {\n        LOGGER.debug(\"Connecting cluster processes\");\n\n        ZooKeeper zookeeper = (ZooKeeper) clusterContainers.getOne(Filter.zooKeeper()).get();\n        MesosMaster mesosMaster = (MesosMaster) clusterContainers.getOne(Filter.mesosMaster()).get();\n        mesosMaster.setZooKeeper(zookeeper);\n\n        if (clusterContainers.getOne(Filter.marathon()).isPresent()) {\n            Marathon marathon = (Marathon) clusterContainers.getOne(Filter.marathon()).get();\n            marathon.setZooKeeper(zookeeper);\n        }\n\n        clusterContainers.getContainers().stream().filter(Filter.mesosAgent()).forEach(a -> {\n            MesosAgent agent = (MesosAgent) a;\n            agent.setZooKeeper(zookeeper);\n        });\n\n        if (clusterContainers.getOne(Filter.registrator()).isPresent()) {\n            Consul consul = (Consul) clusterContainers.getOne(Filter.consul()).get();\n            Registrator registrator = (Registrator) clusterContainers.getOne(Filter.registrator()).get();\n            registrator.setConsul(consul);\n        }\n    }\n\n    private static Boolean isPresent(ClusterContainers clusterContainers, Predicate<ClusterProcess> filter) {\n        return clusterContainers.isPresent(filter);\n    }\n\n}\n"
  },
  {
    "path": "minimesos/src/main/java/com/containersol/minimesos/mesos/MesosContainerImpl.java",
    "content": "package com.containersol.minimesos.mesos;\n\nimport com.containersol.minimesos.MinimesosException;\nimport com.containersol.minimesos.cluster.MesosCluster;\nimport com.containersol.minimesos.cluster.MesosContainer;\nimport com.containersol.minimesos.cluster.ZooKeeper;\nimport com.containersol.minimesos.config.MesosContainerConfig;\nimport com.containersol.minimesos.config.ZooKeeperConfig;\nimport com.containersol.minimesos.integrationtest.container.AbstractContainer;\nimport com.containersol.minimesos.state.State;\nimport com.fasterxml.jackson.core.JsonParseException;\nimport com.fasterxml.jackson.databind.JsonMappingException;\nimport com.mashape.unirest.http.HttpResponse;\nimport com.mashape.unirest.http.JsonNode;\nimport com.mashape.unirest.http.Unirest;\nimport com.mashape.unirest.http.exceptions.UnirestException;\nimport com.mashape.unirest.request.GetRequest;\nimport org.json.JSONObject;\n\nimport java.util.Map;\nimport java.util.TreeMap;\n\n/**\n * Superclass for Mesos master and agent images.\n * Apache Mesos abstracts CPU, memory, storage, and other compute resources away from machines (physical or virtual), enabling fault-tolerant and elastic distributed systems to easily be built and run effectively.\n */\npublic abstract class MesosContainerImpl extends AbstractContainer implements MesosContainer {\n\n    private ZooKeeper zooKeeperContainer;\n    protected MesosContainerConfig config;\n\n    protected MesosContainerImpl(MesosContainerConfig config) {\n        super(config);\n        this.config = config;\n    }\n\n    protected MesosContainerImpl(MesosCluster cluster, String uuid, String containerId, MesosContainerConfig config) {\n        super(cluster, uuid, containerId, config);\n        this.config = config;\n    }\n\n    @Override\n    public String getImageTag() {\n        String imageTag = config.getImageTag();\n        if (MesosContainerConfig.MINIMESOS_DOCKER_TAG.equalsIgnoreCase(imageTag)) {\n            imageTag = MesosContainerConfig.MINIMESOS_DOCKER_TAG;\n        }\n        return imageTag;\n    }\n\n    protected final Map<String, String> getSharedEnvVars() {\n        Map<String, String> envs = new TreeMap<>();\n        envs.put(\"GLOG_v\", \"1\");\n        envs.put(\"MESOS_EXECUTOR_REGISTRATION_TIMEOUT\", \"5mins\");\n        envs.put(\"MESOS_CONTAINERIZERS\", \"docker,mesos\");\n        envs.put(\"MESOS_ISOLATOR\", \"cgroups/cpu,cgroups/mem\");\n        envs.put(\"MESOS_LOG_DIR\", \"/var/log\");\n        envs.put(\"MESOS_LOGGING_LEVEL\", getLoggingLevel());\n        return envs;\n    }\n\n    @Override\n    public void setZooKeeper(ZooKeeper zooKeeperContainer) {\n        this.zooKeeperContainer = zooKeeperContainer;\n    }\n\n    public ZooKeeper getZooKeeper() {\n        return zooKeeperContainer;\n    }\n\n    public String getFormattedZKAddress() {\n        return zooKeeperContainer.getFormattedZKAddress() + ZooKeeperConfig.DEFAULT_MESOS_ZK_PATH;\n    }\n\n    public String getStateUrl() {\n        return getServiceUrl().toString() + \"/state.json\";\n    }\n\n    @Override\n    public JSONObject getStateInfoJSON() throws UnirestException {\n        String stateUrl = getStateUrl();\n        GetRequest request = Unirest.get(stateUrl);\n        HttpResponse<JsonNode> response = request.asJson();\n        return response.getBody().getObject();\n    }\n\n    public String getLoggingLevel() {\n        String level = config.getLoggingLevel();\n        if (MesosContainerConfig.MESOS_LOGGING_LEVEL_INHERIT.equalsIgnoreCase(level)) {\n            level = getCluster().getLoggingLevel();\n        }\n        return level;\n    }\n\n    @Override\n    public State getState() {\n        try {\n            return State.fromJSON(getStateInfoJSON().toString());\n        } catch (JsonParseException | JsonMappingException | UnirestException e) {\n            throw new MinimesosException(\"Could not retrieve state from Mesos container: \" + getName(), e);\n        }\n    }\n}\n"
  },
  {
    "path": "minimesos/src/main/java/com/containersol/minimesos/mesos/MesosDnsContainer.java",
    "content": "package com.containersol.minimesos.mesos;\n\nimport com.containersol.minimesos.cluster.MesosCluster;\nimport com.containersol.minimesos.cluster.MesosDns;\nimport com.containersol.minimesos.config.MesosDNSConfig;\nimport com.containersol.minimesos.integrationtest.container.AbstractContainer;\nimport com.containersol.minimesos.docker.DockerClientFactory;\nimport com.github.dockerjava.api.command.CreateContainerCmd;\nimport com.github.dockerjava.api.model.ExposedPort;\nimport com.github.dockerjava.api.model.InternetProtocol;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport static com.containersol.minimesos.util.EnvironmentBuilder.newEnvironment;\n\n/**\n * Mesos DNS automatically registers and deregisters Mesos services.\n */\npublic class MesosDnsContainer extends AbstractContainer implements MesosDns {\n\n    private static final String DNS_PORT = \"53\";\n\n    private static final String DOMAIN = \"mm\";\n\n    private static final String REFRESH_SECONDS = \"1\";\n\n    private MesosDNSConfig config;\n\n    public MesosDnsContainer(MesosCluster cluster, String uuid, String containerId) {\n        this(cluster, uuid, containerId, new MesosDNSConfig());\n    }\n\n    private MesosDnsContainer(MesosCluster cluster, String uuid, String containerId, MesosDNSConfig config) {\n        super(cluster, uuid, containerId, config);\n        this.config = config;\n    }\n\n    public MesosDnsContainer(MesosDNSConfig mesosDNS) {\n        super(mesosDNS);\n        this.config = mesosDNS;\n    }\n\n    @Override\n    public String getRole() {\n        return \"mesosdns\";\n    }\n\n    @Override\n    protected CreateContainerCmd dockerCommand() {\n        return DockerClientFactory.build()\n            .createContainerCmd(config.getImageName() + \":\" + config.getImageTag())\n            .withEnv(newEnvironment()\n                .withValues(getMesosDNSEnvVars())\n                .createEnvironment())\n            .withCmd(\"-v=2\", \"-config=/etc/mesos-dns/config.json\")\n            .withExposedPorts(new ExposedPort(Integer.valueOf(DNS_PORT), InternetProtocol.UDP),\n                              new ExposedPort(Integer.valueOf(DNS_PORT), InternetProtocol.TCP))\n            .withName(getName());\n    }\n\n    @Override\n    protected int getServicePort() {\n        return Integer.valueOf(DNS_PORT);\n    }\n\n    private Map<String,String> getMesosDNSEnvVars() {\n        Map<String, String> mesosDNSEnvVars = new HashMap<>();\n        mesosDNSEnvVars.put(\"MESOS_DNS_ZK\", getCluster().getZooKeeper().getFormattedZKAddress() + \"/mesos\");\n        mesosDNSEnvVars.put(\"MESOS_DNS_DOMAIN\", DOMAIN);\n        mesosDNSEnvVars.put(\"MESOS_DNS_PORT\", DNS_PORT);\n        mesosDNSEnvVars.put(\"MESOS_DNS_REFRESH_SECONDS\", REFRESH_SECONDS);\n        return mesosDNSEnvVars;\n    }\n}\n"
  },
  {
    "path": "minimesos/src/main/java/com/containersol/minimesos/mesos/MesosMasterContainer.java",
    "content": "package com.containersol.minimesos.mesos;\n\nimport com.containersol.minimesos.MinimesosException;\nimport com.containersol.minimesos.cluster.MesosCluster;\nimport com.containersol.minimesos.cluster.MesosDns;\nimport com.containersol.minimesos.cluster.MesosMaster;\nimport com.containersol.minimesos.config.ClusterConfig;\nimport com.containersol.minimesos.config.MesosMasterConfig;\nimport com.containersol.minimesos.docker.DockerClientFactory;\nimport com.containersol.minimesos.util.Environment;\nimport com.github.dockerjava.api.command.CreateContainerCmd;\nimport com.github.dockerjava.api.model.ExposedPort;\nimport com.github.dockerjava.api.model.Ports;\nimport com.mashape.unirest.http.Unirest;\nimport com.mashape.unirest.http.exceptions.UnirestException;\nimport org.apache.commons.lang.StringUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport java.net.URI;\nimport java.net.URISyntaxException;\nimport java.util.Map;\nimport java.util.TreeMap;\nimport java.util.concurrent.Callable;\nimport java.util.concurrent.TimeUnit;\n\nimport static com.containersol.minimesos.util.EnvironmentBuilder.newEnvironment;\nimport static com.jayway.awaitility.Awaitility.await;\n\n/**\n * Mesos Master adds the \"server\" component for Apache Mesos\n */\npublic class MesosMasterContainer extends MesosContainerImpl implements MesosMaster {\n\n    public MesosMasterContainer(MesosCluster cluster, String uuid, String containerId) {\n        this(cluster, uuid, containerId, new MesosMasterConfig(ClusterConfig.DEFAULT_MESOS_VERSION));\n    }\n\n    private MesosMasterContainer(MesosCluster cluster, String uuid, String containerId, MesosMasterConfig config) {\n        super(cluster, uuid, containerId, config);\n    }\n\n    public MesosMasterContainer(MesosMasterConfig master) {\n        super(master);\n    }\n\n    @Override\n    public int getServicePort() {\n        return MesosMasterConfig.MESOS_MASTER_PORT;\n    }\n\n    @Override\n    public URI getServiceUrl() {\n        URI serviceUri = null;\n\n        String protocol = getServiceProtocol();\n\n        String host;\n        if (Environment.isRunningInJvmOnMacOsX()) {\n            host = \"localhost\";\n        } else {\n            host = getIpAddress();\n        }\n\n        int port = getServicePort();\n        String path = getServicePath();\n\n        if (StringUtils.isNotEmpty(host)) {\n            try {\n                serviceUri = new URI(protocol, null, host, port, path, null, null);\n            } catch (URISyntaxException e) {\n                throw new MinimesosException(\"Failed to form service URL for \" + getName(), e);\n            }\n        }\n\n        return serviceUri;\n    }\n\n\n    protected Map<String, String> getMesosMasterEnvVars() {\n        Map<String, String> envs = new TreeMap<>();\n        envs.put(\"MESOS_QUORUM\", \"1\");\n        if (((MesosMasterConfig) config).getAuthenticate() && ((MesosMasterConfig) config).getAclJson() != null) {\n            envs.put(\"MESOS_AUTHENTICATE\", String.valueOf(((MesosMasterConfig) config).getAuthenticate()));\n            envs.put(\"MESOS_ACLS\", ((MesosMasterConfig) config).getAclJson());\n        }\n        envs.put(\"MESOS_ZK\", getFormattedZKAddress());\n        envs.put(\"MESOS_LOGGING_LEVEL\", getLoggingLevel());\n        if (getCluster() != null) {\n            envs.put(\"MESOS_CLUSTER\", getCluster().getClusterName());\n        }\n        return envs;\n    }\n\n    @Override\n    public String getRole() {\n        return \"master\";\n    }\n\n    @Override\n    protected CreateContainerCmd dockerCommand() {\n        int port = getServicePort();\n        ExposedPort exposedPort = ExposedPort.tcp(port);\n\n        Ports portBindings = new Ports();\n        if (getCluster().isMapPortsToHost()) {\n            portBindings.bind(exposedPort, Ports.Binding.bindPort(port));\n        }\n\n        CreateContainerCmd cmd = DockerClientFactory.build().createContainerCmd(getImageName() + \":\" + getImageTag())\n            .withName(getName())\n            .withExposedPorts(new ExposedPort(getServicePort()))\n            .withEnv(newEnvironment()\n                .withValues(getMesosMasterEnvVars())\n                .withValues(getSharedEnvVars())\n                .createEnvironment())\n            .withPortBindings(portBindings);\n\n        MesosDns mesosDns = getCluster().getMesosDns();\n        if (mesosDns != null) {\n            cmd.withDns(mesosDns.getIpAddress());\n        }\n\n        return cmd;\n    }\n\n    @Override\n    public void waitFor() {\n        new MesosMasterContainer.MesosClusterStateResponse(getCluster()).waitFor();\n    }\n\n    public static class MesosClusterStateResponse implements Callable<Boolean> {\n\n        private static final Logger LOGGER = LoggerFactory.getLogger(MesosClusterStateResponse.class);\n\n        private final MesosCluster mesosCluster;\n\n        public MesosClusterStateResponse(MesosCluster mesosCluster) {\n            this.mesosCluster = mesosCluster;\n        }\n\n        @Override\n        public Boolean call() throws Exception {\n            String stateUrl = mesosCluster.getMaster().getStateUrl();\n            try {\n                int activatedAgents = Unirest.get(stateUrl).asJson().getBody().getObject().getInt(\"activated_slaves\");\n                if (activatedAgents != mesosCluster.getAgents().size()) {\n                    LOGGER.debug(\"Waiting for \" + mesosCluster.getAgents().size() + \" activated agents - current number of activated agents: \" + activatedAgents);\n                    return false;\n                }\n            } catch (UnirestException e) { //NOSONAR\n                // in case of error just return false\n                LOGGER.debug(\"Polling Mesos Master state on host: \\\"\" + stateUrl + \"\\\"...\");\n                return false;\n            } catch (Exception e) { //NOSONAR\n                // in case of error just return false\n                LOGGER.error(\"An error occured while polling Mesos master\", e);\n                return false;\n            }\n\n            return true;\n        }\n\n        public void waitFor() {\n            await(\"Waiting until Mesos master state endpoint is available\")\n                    .atMost(mesosCluster.getClusterConfig().getTimeout(), TimeUnit.SECONDS)\n                    .pollInterval(1, TimeUnit.SECONDS)\n                    .until(this);\n\n            LOGGER.debug(\"MesosMaster state discovered successfully\");\n        }\n    }\n}\n"
  },
  {
    "path": "minimesos/src/main/java/com/containersol/minimesos/mesos/RegistratorContainer.java",
    "content": "package com.containersol.minimesos.mesos;\n\nimport com.containersol.minimesos.cluster.Consul;\nimport com.containersol.minimesos.cluster.MesosCluster;\nimport com.containersol.minimesos.cluster.Registrator;\nimport com.containersol.minimesos.config.ConsulConfig;\nimport com.containersol.minimesos.config.RegistratorConfig;\nimport com.containersol.minimesos.integrationtest.container.AbstractContainer;\nimport com.containersol.minimesos.docker.DockerClientFactory;\nimport com.github.dockerjava.api.command.CreateContainerCmd;\nimport com.github.dockerjava.api.model.Bind;\n\n/**\n * Registrator automatically registers and deregisters services for any Docker container by inspecting containers as they come online.\n */\npublic class RegistratorContainer extends AbstractContainer implements Registrator {\n\n    private RegistratorConfig config;\n\n    private Consul consul;\n\n    public RegistratorContainer(MesosCluster cluster, String uuid, String containerId) {\n        this(cluster, uuid, containerId, new RegistratorConfig());\n    }\n\n    private RegistratorContainer(MesosCluster cluster, String uuid, String containerId, RegistratorConfig config) {\n        super(cluster, uuid, containerId, config);\n        this.config = config;\n    }\n\n    public RegistratorContainer(RegistratorConfig registrator) {\n        super(registrator);\n        this.config = registrator;\n    }\n\n    @Override\n    public String getRole() {\n        return \"registrator\";\n    }\n\n    @Override\n    protected CreateContainerCmd dockerCommand() {\n         return DockerClientFactory.build().createContainerCmd(config.getImageName() + \":\" + config.getImageTag())\n                .withNetworkMode(\"host\")\n                .withBinds(Bind.parse(\"/var/run/docker.sock:/tmp/docker.sock\"))\n                .withCmd(\"-internal\", String.format(\"consul://%s:%d\", consul.getIpAddress(), ConsulConfig.CONSUL_HTTP_PORT))\n                .withName(getName());\n    }\n\n    public void setConsul(ConsulContainer consul) {\n        this.consul = consul;\n    }\n\n    @Override\n    public void setConsul(Consul consul) {\n        this.consul = consul;\n    }\n}\n"
  },
  {
    "path": "minimesos/src/main/java/com/containersol/minimesos/mesos/ZooKeeperContainer.java",
    "content": "package com.containersol.minimesos.mesos;\n\nimport com.containersol.minimesos.MinimesosException;\nimport com.containersol.minimesos.cluster.MesosCluster;\nimport com.containersol.minimesos.cluster.ZooKeeper;\nimport com.containersol.minimesos.config.ZooKeeperConfig;\nimport com.containersol.minimesos.integrationtest.container.AbstractContainer;\nimport com.containersol.minimesos.docker.DockerClientFactory;\nimport com.containersol.minimesos.util.Environment;\nimport com.github.dockerjava.api.command.CreateContainerCmd;\nimport com.github.dockerjava.api.model.ExposedPort;\nimport com.github.dockerjava.api.model.Ports;\nimport org.apache.commons.lang.StringUtils;\n\nimport java.net.URI;\nimport java.net.URISyntaxException;\n\n/**\n * ZooKeeper is a centralized service for maintaining configuration information, naming, providing distributed synchronization, and providing group services.\n */\npublic class ZooKeeperContainer extends AbstractContainer implements ZooKeeper {\n\n    private final ZooKeeperConfig config;\n\n    public ZooKeeperContainer(ZooKeeperConfig config) {\n        super(config);\n        this.config = config;\n    }\n\n    protected ZooKeeperContainer() {\n        this(new ZooKeeperConfig());\n    }\n\n    public ZooKeeperContainer(MesosCluster cluster, String uuid, String containerId) {\n        this(cluster, uuid, containerId, new ZooKeeperConfig());\n    }\n\n    public ZooKeeperContainer(MesosCluster cluster, String uuid, String containerId, ZooKeeperConfig config) {\n        super(cluster, uuid, containerId, config);\n        this.config = config;\n    }\n\n    @Override\n    public String getRole() {\n        return \"zookeeper\";\n    }\n\n    @Override\n    protected CreateContainerCmd dockerCommand() {\n        int port = getServicePort();\n        ExposedPort exposedPort = ExposedPort.tcp(port);\n\n        Ports portBindings = new Ports();\n        if (getCluster().isMapPortsToHost()) {\n            portBindings.bind(exposedPort, Ports.Binding.bindPort(port));\n        }\n\n        return DockerClientFactory.build().createContainerCmd(config.getImageName() + \":\" + config.getImageTag())\n            .withName(getName())\n            .withExposedPorts(new ExposedPort(ZooKeeperConfig.DEFAULT_ZOOKEEPER_PORT), new ExposedPort(2888), new ExposedPort(3888))\n            .withPortBindings(portBindings);\n    }\n\n    @Override\n    protected String getServiceProtocol() {\n        return \"zk\";\n    }\n\n    @Override\n    protected int getServicePort() {\n        return ZooKeeperConfig.DEFAULT_ZOOKEEPER_PORT;\n    }\n\n    @Override\n    protected String getServicePath() {\n        return ZooKeeperConfig.DEFAULT_MESOS_ZK_PATH;\n    }\n\n    /**\n     * @return ZooKeeper URL based on real IP address\n     */\n    @Override\n    public String getFormattedZKAddress() {\n        return \"zk://\" + getIpAddress() + \":\" + ZooKeeperConfig.DEFAULT_ZOOKEEPER_PORT;\n    }\n\n    @Override\n    public URI getServiceUrl() {\n        URI serviceUri = null;\n\n        String protocol = getServiceProtocol();\n\n        String host;\n        if (Environment.isRunningInJvmOnMacOsX()) {\n            host = \"localhost\";\n        } else {\n            host = getIpAddress();\n        }\n\n        int port = getServicePort();\n        String path = getServicePath();\n\n        if (StringUtils.isNotEmpty(host)) {\n            try {\n                serviceUri = new URI(protocol, null, host, port, path, null, null);\n            } catch (URISyntaxException e) {\n                throw new MinimesosException(\"Failed to form service URL for \" + getName(), e);\n            }\n        }\n\n        return serviceUri;\n    }\n\n}\n"
  },
  {
    "path": "minimesos/src/main/java/com/containersol/minimesos/state/Discovery.java",
    "content": "package com.containersol.minimesos.state;\n\nimport com.fasterxml.jackson.annotation.JsonIgnoreProperties;\n\n/**\n * Maps Mesos task discovery information from JSON string to a Java object.\n */\n@JsonIgnoreProperties(ignoreUnknown = true)\npublic class Discovery {\n\n    private Ports ports;\n\n    public Ports getPorts() {\n        return ports;\n    }\n\n    public void setPorts(Ports ports) {\n        this.ports = ports;\n    }\n}\n"
  },
  {
    "path": "minimesos/src/main/java/com/containersol/minimesos/state/Executor.java",
    "content": "package com.containersol.minimesos.state;\n\nimport com.fasterxml.jackson.annotation.JsonIgnoreProperties;\n\n@JsonIgnoreProperties(ignoreUnknown = true)\npublic class Executor {\n\n    private String id;\n    private String directory;\n\n    public String getId() {\n        return id;\n    }\n\n    public void setId(String id) {\n        this.id = id;\n    }\n\n    public String getDirectory() {\n        return directory;\n    }\n\n    public void setDirectory(String directory) {\n        this.directory = directory;\n    }\n}\n"
  },
  {
    "path": "minimesos/src/main/java/com/containersol/minimesos/state/Framework.java",
    "content": "package com.containersol.minimesos.state;\n\nimport com.fasterxml.jackson.annotation.JsonIgnoreProperties;\nimport com.fasterxml.jackson.annotation.JsonProperty;\n\nimport java.util.ArrayList;\n\n/**\n * Created by peldan on 09/07/15.\n */\n@JsonIgnoreProperties(ignoreUnknown = true)\npublic class Framework {\n\n    private boolean active;\n\n    private boolean checkpoint;\n\n    @JsonProperty(\"failover_timeout\")\n    private int failoverTimeout;\n\n    private String hostname;\n\n    private String id;\n\n    private String name;\n\n    private String role;\n\n    private ArrayList<Task> tasks;\n    private ArrayList<Executor> executors = new ArrayList<>();\n\n    public boolean isActive() {\n        return active;\n    }\n\n    public void setActive(boolean active) {\n        this.active = active;\n    }\n\n    public boolean isCheckpoint() {\n        return checkpoint;\n    }\n\n    public void setCheckpoint(boolean checkpoint) {\n        this.checkpoint = checkpoint;\n    }\n\n    public int getFailoverTimeout() {\n        return failoverTimeout;\n    }\n\n    public void setFailoverTimeout(int failoverTimeout) {\n        this.failoverTimeout = failoverTimeout;\n    }\n\n    public String getHostname() {\n        return hostname;\n    }\n\n    public void setHostname(String hostname) {\n        this.hostname = hostname;\n    }\n\n    public String getId() {\n        return id;\n    }\n\n    public void setId(String id) {\n        this.id = id;\n    }\n\n    public String getName() {\n        return name;\n    }\n\n    public void setName(String name) {\n        this.name = name;\n    }\n\n    public String getRole() {\n        return role;\n    }\n\n    public void setRole(String role) {\n        this.role = role;\n    }\n\n    public ArrayList<Task> getTasks() {\n        return tasks;\n    }\n\n    public void setTasks(ArrayList<Task> tasks) {\n        this.tasks = tasks;\n    }\n\n    public ArrayList<Executor> getExecutors() {\n        return executors;\n    }\n\n    public void setExecutors(ArrayList<Executor> executors) {\n        this.executors = executors;\n    }\n}\n"
  },
  {
    "path": "minimesos/src/main/java/com/containersol/minimesos/state/Port.java",
    "content": "package com.containersol.minimesos.state;\n\nimport com.fasterxml.jackson.annotation.JsonIgnoreProperties;\n\n/**\n * Maps Mesos port information from JSON string to a Java object.\n */\n@JsonIgnoreProperties(ignoreUnknown = true)\npublic class Port {\n\n    private int number;\n\n    private String protocol;\n\n    public int getNumber() {\n        return number;\n    }\n\n    public void setNumber(int number) {\n        this.number = number;\n    }\n\n    public String getProtocol() {\n        return protocol;\n    }\n\n    public void setProtocol(String protocol) {\n        this.protocol = protocol;\n    }\n}\n"
  },
  {
    "path": "minimesos/src/main/java/com/containersol/minimesos/state/Ports.java",
    "content": "package com.containersol.minimesos.state;\n\nimport com.fasterxml.jackson.annotation.JsonIgnoreProperties;\n\nimport java.util.List;\n\n/**\n * Maps Mesos task ports information from JSON string to a Java object.\n */\n@JsonIgnoreProperties(ignoreUnknown = true)\npublic class Ports {\n\n    private List<Port> ports;\n\n    public List<Port> getPorts() {\n        return ports;\n    }\n\n    public void setPorts(List<Port> ports) {\n        this.ports = ports;\n    }\n}\n"
  },
  {
    "path": "minimesos/src/main/java/com/containersol/minimesos/state/State.java",
    "content": "package com.containersol.minimesos.state;\n\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport com.fasterxml.jackson.annotation.JsonIgnoreProperties;\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport com.fasterxml.jackson.core.JsonParseException;\nimport com.fasterxml.jackson.databind.JsonMappingException;\nimport com.fasterxml.jackson.databind.ObjectMapper;\n\n/**\n * This class is populated with the results from a GET request to /state.json on a mesos-master.\n */\n@JsonIgnoreProperties(ignoreUnknown = true)\npublic class State {\n\n    private String id;\n    private Map<String, String> flags = new HashMap<>();\n\n    @JsonProperty(\"activated_slaves\")\n    private int activatedAgents = 0; // NOSONAR\n\n    @JsonProperty(\"version\")\n    private String version;\n\n    private ArrayList<Framework> frameworks = new ArrayList<>();\n\n    public static State fromJSON(String jsonString) throws JsonParseException, JsonMappingException {\n        ObjectMapper mapper = new ObjectMapper();\n        try {\n            return mapper.readValue(jsonString, State.class);\n        } catch (IOException e) {\n            throw new RuntimeException(e);\n        }\n    }\n\n    public ArrayList<Framework> getFrameworks() {\n        return frameworks;\n    }\n\n    public void setFrameworks(ArrayList<Framework> frameworks) {\n        this.frameworks = frameworks;\n    }\n\n    public Framework getFramework(String name) {\n        for (Framework fw : getFrameworks()) {\n            if (fw.getName().equals(name)) return fw;\n        }\n        return null;\n    }\n\n    public Map<String, String> getFlags() {\n        return flags;\n    }\n\n    public int getActivatedAgents() {\n        return activatedAgents;\n    }\n\n    public String getVersion() {\n        return version;\n    }\n\n    public String getId() {\n        return id;\n    }\n\n    public void setId(String id) {\n        this.id = id;\n    }\n}\n"
  },
  {
    "path": "minimesos/src/main/java/com/containersol/minimesos/state/Task.java",
    "content": "package com.containersol.minimesos.state;\n\nimport com.fasterxml.jackson.annotation.JsonIgnoreProperties;\nimport com.fasterxml.jackson.annotation.JsonProperty;\n\n/**\n * Maps Mesos task properties from JSON string to Java object\n */\n@JsonIgnoreProperties(ignoreUnknown = true)\npublic class Task {\n\n    private String id;\n    private String name;\n    private String state;\n\n    @JsonProperty(\"framework_id\")\n    private String frameworkId;\n\n    @JsonProperty(\"executor_id\")\n    private String executorId;\n\n    @JsonProperty(\"slave_id\")\n    private String slaveId;\n\n    private Discovery discovery;\n\n    public String getId() {\n        return id;\n    }\n\n    public void setId(String id) {\n        this.id = id;\n    }\n\n    public String getState() {\n        return state;\n    }\n\n    public void setState(String state) {\n        this.state = state;\n    }\n\n    public String getName() {\n        return name;\n    }\n\n    public void setName(String name) {\n        this.name = name;\n    }\n\n    public String getFrameworkId() {\n        return frameworkId;\n    }\n\n    public void setFrameworkId(String frameworkId) {\n        this.frameworkId = frameworkId;\n    }\n\n    public String getExecutorId() {\n        return executorId;\n    }\n\n    public void setExecutorId(String executorId) {\n        this.executorId = executorId;\n    }\n\n    public String getSlaveId() {\n        return slaveId;\n    }\n\n    public void setSlaveId(String slaveId) {\n        this.slaveId = slaveId;\n    }\n\n    public Discovery getDiscovery() {\n        return discovery;\n    }\n\n    public void setDiscovery(Discovery discovery) {\n        this.discovery = discovery;\n    }\n}\n"
  },
  {
    "path": "minimesos/src/main/java/com/containersol/minimesos/util/CollectionsUtils.java",
    "content": "package com.containersol.minimesos.util;\n\nimport com.containersol.minimesos.MinimesosException;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * Utilities for dealing with collections\n */\npublic class CollectionsUtils {\n\n    private static final Logger LOGGER = LoggerFactory.getLogger(CollectionsUtils.class);\n\n    private CollectionsUtils() {\n        // do not allow creation of instances\n    }\n\n    public static <T> List<T> typedList(List original, Class<T> clazz) {\n\n        ArrayList<T> typed = new ArrayList<>(original.size());\n\n        for (Object obj : original) {\n            if ((obj == null) || clazz.isAssignableFrom(obj.getClass())) {\n                typed.add(clazz.cast(obj));\n            } else {\n                throw new MinimesosException(\"Not possible to cast \" + obj + \" to \" + clazz.getCanonicalName());\n            }\n        }\n\n        return typed;\n\n    }\n\n    /**\n     * This function split the cmd attribute in an array of String to make\n     * it consumable by the withCmd from docker-java.\n     *\n     * It ensures that quotes and double quotes are correctly handled,\n     * the split is performed on spaces.\n    */\n    public static String[] splitCmd(String cmd) {\n        String arg = \"\";\n        ArrayList<String> args = new ArrayList<String>();\n        ArrayList<Character> quotes = new ArrayList<Character>();\n\n        LOGGER.debug(String.format(\"Parsing cmd line: %s\", cmd));\n        for (int i = 0; i < cmd.length(); i++){\n            char c = cmd.charAt(i);\n            if (c == ' ' && quotes.size() == 0) { // split command on spaces\n                args.add(arg);\n                arg = \"\";\n                continue;\n            } else if (c == '\\'' || c == '\"') { // feed state array on quote and double quotes\n                if (quotes.size() > 0 && quotes.get(0) == c) {\n                    quotes.remove(0);\n                } else {\n                    quotes.add(0, c);\n                }\n            }\n            arg += c;\n        }\n        // add last parsed elem\n        if (arg != \"\") {\n            args.add(arg);\n        }\n        // check unconsistent state\n        if (quotes.size() != 0) {\n            throw new MinimesosException(\"Marathon cmd config quotes are not closed properly\");\n\n        }\n        return args.toArray(new String[args.size()]);\n    }\n\n}\n"
  },
  {
    "path": "minimesos/src/main/java/com/containersol/minimesos/util/Downloader.java",
    "content": "package com.containersol.minimesos.util;\n\nimport com.containersol.minimesos.MinimesosException;\nimport com.mashape.unirest.http.HttpResponse;\nimport com.mashape.unirest.http.Unirest;\nimport com.mashape.unirest.http.exceptions.UnirestException;\nimport org.apache.http.HttpStatus;\n\nimport java.net.URI;\n\npublic class Downloader {\n\n    public String getFileContentAsString(String url) throws MinimesosException {\n        HttpResponse<String> response = null;\n        try {\n            response = Unirest.get(url)\n                .header(\"content-type\", \"*/*\")\n                .asString();\n        } catch (UnirestException e) {\n            throw new MinimesosException(String.format(\"Cannot fetch file '%s': '%s'\", url, e.getMessage()));\n        }\n        if (response.getStatus() != HttpStatus.SC_OK) {\n            throw new MinimesosException(String.format(\"Cannot fetch file '%s': '%s'\", url, response.getStatus()));\n        }\n        return response.getBody();\n    }\n}\n"
  },
  {
    "path": "minimesos/src/main/java/com/containersol/minimesos/util/Environment.java",
    "content": "package com.containersol.minimesos.util;\n\n/**\n * Utility for detecting the runtime environment.\n */\npublic class Environment {\n\n    private Environment() {\n\n    }\n\n    /**\n     * Checks if minimesos cli runs in JVM on Mac OS X.\n     *\n     * @return true if it runs on Mac OS X without Docker\n     */\n    public static boolean isRunningInJvmOnMacOsX() {\n        return System.getProperty(\"os.name\").contains(\"Mac OS X\");\n    }\n\n    /**\n     * Checks if minimesos cli runs in Docker on Mac\n     *\n     * @return true if MINIMESOS_OS env var is set by bin/minimesos\n     */\n    public static boolean isRunningInDockerOnMac() {\n        return System.getenv(\"MINIMESOS_OS\") != null && System.getenv(\"MINIMESOS_OS\").contains(\"Mac OS X\");\n    }\n}\n"
  },
  {
    "path": "minimesos/src/main/java/com/containersol/minimesos/util/EnvironmentBuilder.java",
    "content": "package com.containersol.minimesos.util;\n\nimport java.util.Map;\nimport java.util.TreeMap;\n\n/**\n * Provides convenient API for building a map of environment variables.\n * Produces the String[] format needed by CreateContainerCmd.setEnv()\n */\npublic class EnvironmentBuilder {\n\n    private Map<String, String> envMap = new TreeMap<>();\n\n    public static EnvironmentBuilder newEnvironment() {\n        return new EnvironmentBuilder();\n    }\n\n    public EnvironmentBuilder withValue(String key, String value) {\n        envMap.put(key, value);\n        return this;\n    }\n\n    public EnvironmentBuilder withValues(Map<String, String> envMap) {\n        this.envMap.putAll(envMap);\n        return this;\n    }\n\n    public String[] createEnvironment() {\n        return envMap.entrySet().stream().map(e -> e.getKey() + \"=\" + e.getValue()).toArray(String[]::new);\n    }\n}\n"
  },
  {
    "path": "minimesos/src/main/java/com/containersol/minimesos/util/Predicate.java",
    "content": "package com.containersol.minimesos.util;\n\npublic interface Predicate<T> {\n    boolean test(T t);\n}"
  },
  {
    "path": "minimesos/src/main/java/com/containersol/minimesos/util/ResourceUtil.java",
    "content": "package com.containersol.minimesos.util;\n\nimport com.containersol.minimesos.MinimesosException;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\n\n/**\n * Utility for dealing with Mesos resources\n */\npublic class ResourceUtil {\n\n    private ResourceUtil() {\n\n    }\n\n    /**\n     * Turns a Mesos resource string into a List of ports.\n     * <p>\n     * Example: 'ports(*):[31000-32000],;cpus(*):0.2; mem(*):256; disk(*):200' returns [31000, 32000]\n     *\n     * @param mesosResourceString Mesos resource string\n     * @return list of ports if any\n     * @throws MinimesosException if resource string is incorrect\n     */\n    public static ArrayList<Integer> parsePorts(String mesosResourceString) {\n        if (mesosResourceString == null) {\n            throw new MinimesosException(\"Resource string is null\");\n        }\n        String portRangeString = mesosResourceString.replaceAll(\".*ports\\\\(.+\\\\):\\\\[(.*)\\\\].*\", \"$1\");\n        ArrayList<String> portRanges = new ArrayList<>(Arrays.asList(portRangeString.split(\",\")));\n        ArrayList<Integer> returnList = new ArrayList<>();\n        for (String portRange : portRanges) {\n\n            if (!portRange.matches(\"\\\\d+-\\\\d+\")) {\n                throw new MinimesosException(\"Resource string '\" + mesosResourceString + \"' is incorrect. We only support a single port range.\");\n            }\n            String[] ports = portRange.split(\"-\");\n            int startPort = Integer.valueOf(ports[0]);\n            int endPort = Integer.valueOf(ports[1]);\n            if (startPort > endPort) {\n                throw new MinimesosException(\"Incorrect port range. Start port \" + startPort + \" is greater than end port \" + endPort);\n            }\n            for (int i = startPort; i <= endPort; i++) {\n                returnList.add(i);\n            }\n        }\n        return returnList;\n    }\n\n}\n"
  },
  {
    "path": "minimesos/src/main/resources/logback.xml",
    "content": "<configuration debug=\"false\">\n\n\t<appender name=\"JAVA\" class=\"ch.qos.logback.core.ConsoleAppender\">\n\t\t<target>System.out</target>\n\t\t<encoder>\n\t\t\t<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>\n\t\t</encoder>\n\t</appender>\n\n\t<appender name=\"CLI\" class=\"ch.qos.logback.core.ConsoleAppender\">\n\t\t<encoder>\n\t\t\t<pattern>%msg%n</pattern>\n\t\t</encoder>\n\t</appender>\n\n\t<logger name=\"com.containersol.minimesos\" level=\"info\">\n\t\t<appender-ref ref=\"CLI\"/>\n\t</logger>\n\n\t<logger name=\"com.containersol.minimesos.integrationtest.container\" level=\"info\">\n\t\t<appender-ref ref=\"JAVA\"/>\n\t</logger>\n\n</configuration>\n"
  },
  {
    "path": "minimesos/src/main/resources/marathon/elasticsearch.json",
    "content": "{\n  \"id\": \"elasticsearch-mesos-scheduler\",\n  \"container\": {\n\t\"docker\": {\n\t  \"image\": \"mesos/elasticsearch-scheduler\",\n\t  \"network\": \"BRIDGE\"\n\t}\n  },\n  \"args\": [\n\t\"--zookeeperMesosUrl\",\n\t\"{{MINIMESOS_ZOOKEEPER}}\",\n\t\"--useIpAddress\",\n\t\"true\"\n  ],\n  \"cpus\": 0.2,\n  \"mem\": 512.0,\n  \"env\": {\n\t\"JAVA_OPTS\": \"-Xms128m -Xmx256m\"\n  },\n  \"instances\": 1\n}\n"
  },
  {
    "path": "minimesos/src/main/resources/marathon/mesos-consul.json",
    "content": "{\n  \"args\": [\n\t\"--zk={{MINIMESOS_ZOOKEEPER}}\",\n\t\"--consul=1\",\n\t\"--consul-ip={{MINIMESOS_CONSUL_IP}}\"\n  ],\n  \"container\": {\n\t\"type\": \"DOCKER\",\n\t\"docker\": {\n\t  \"network\": \"BRIDGE\",\n\t  \"image\": \"containersol/mesos-consul:latest\"\n\t}\n  },\n  \"id\": \"mesos-consul\",\n  \"instances\": 1,\n  \"cpus\": 0.1,\n  \"mem\": 256\n}\n"
  },
  {
    "path": "minimesos/src/test/groovy/com/containersol/minimesos/config/AgentResourcesConfigTest.groovy",
    "content": "package com.containersol.minimesos.config\n\nimport org.junit.Test\n\nimport static org.junit.Assert.assertEquals;\n\npublic class AgentResourcesConfigTest {\n\n    @Test\n    public void testDefaultResourcesAsString() {\n\n        AgentResourcesConfig resources = new AgentResourcesConfig()\n        String asString = resources.asMesosString()\n        String expected = \"ports(*):[31000-32000]; cpus(*):4; mem(*):1024; disk(*):2000\"\n\n        assertEquals(expected, asString)\n\n    }\n\n    @Test\n    public void testPortsFromString() {\n\n        String strResources = \"ports(*):[8081-8082]\"\n        AgentResourcesConfig resources = AgentResourcesConfig.fromString(strResources);\n\n        assertEquals(0, resources.cpus.size())\n        assertEquals(0, resources.disks.size())\n        assertEquals(0, resources.mems.size())\n\n        assertEquals(1, resources.ports.size())\n        assertEquals(\"[8081-8082]\", String.valueOf(resources.ports[\"*\"].value))\n\n    }\n\n    @Test\n    public void testPortsCpusFromString() {\n\n        String strResources = \"ports(*):[8081-8082]; cpus(*):1.2\"\n        AgentResourcesConfig resources = AgentResourcesConfig.fromString(strResources);\n\n        assertEquals(1, resources.cpus.size())\n        double actual = resources.cpus[\"*\"].value\n        assertEquals(1.2, actual, 0.001)\n\n        assertEquals(0, resources.disks.size())\n        assertEquals(0, resources.mems.size())\n\n        assertEquals(1, resources.ports.size())\n        assertEquals(\"[8081-8082]\", resources.ports[\"*\"].value)\n\n    }\n\n    @Test\n    public void testElasticSearchResources() {\n\n        String resources = \"ports(testRole):[9200-9200,9300-9300]; cpus(testRole):0.2; mem(testRole):256; disk(testRole):200\"\n        AgentResourcesConfig resourcesConfig = AgentResourcesConfig.fromString(resources)\n\n        assertEquals(\"one role is expected for ports\", 1, resourcesConfig.ports.size())\n        assertEquals(\"one role is expected for cpus\", 1, resourcesConfig.cpus.size())\n        assertEquals(\"one role is expected for mem\", 1, resourcesConfig.mems.size())\n        assertEquals(\"one role is expected for disk\", 1, resourcesConfig.disks.size())\n\n        assertEquals(\"[9200-9200,9300-9300]\", resourcesConfig.ports[\"testRole\"].value)\n        assertEquals(0.2, resourcesConfig.cpus[\"testRole\"].value, 0.0001)\n        assertEquals(256, resourcesConfig.mems[\"testRole\"].value, 0.0001)\n        assertEquals(200, resourcesConfig.disks[\"testRole\"].value, 0.0001)\n\n    }\n\n}\n"
  },
  {
    "path": "minimesos/src/test/groovy/com/containersol/minimesos/config/ConfigParserTest.groovy",
    "content": "package com.containersol.minimesos.config\n\nimport com.containersol.minimesos.MinimesosException\nimport org.junit.Before\nimport org.junit.Test\n\nimport static org.junit.Assert.*\n\npublic class ConfigParserTest {\n\n    private ConfigParser parser\n\n    @Before\n    public void before() {\n        parser = new ConfigParser()\n    }\n\n    @Test\n    public void testLoggingLevel() {\n        String config =\n                \"\"\"\n        minimesos {\n\n            loggingLevel = \"WARNING\"\n\n        }\n        \"\"\"\n\n        assertEquals(\"WARNING\", parser.parse(config).getLoggingLevel())\n    }\n\n    @Test\n    public void testLoggingLevel_caseInsensitive() {\n        String config =\n                \"\"\"\n        minimesos {\n\n            loggingLevel = \"warning\"\n\n        }\n        \"\"\"\n\n        assertEquals(\"WARNING\", parser.parse(config).getLoggingLevel())\n    }\n\n    @Test\n    public void testClusterName() {\n        String config =\n                \"\"\"\n        minimesos {\n\n            clusterName = \"testcluster\"\n\n        }\n        \"\"\"\n\n        assertEquals(\"testcluster\", parser.parse(config).getClusterName())\n    }\n\n    @Test\n    public void testTimeout() {\n        String config =\n                \"\"\"\n        minimesos {\n\n            timeout = 500\n\n        }\n        \"\"\"\n\n        assertEquals(500, parser.parse(config).getTimeout())\n    }\n\n    @Test(expected = MissingPropertyException.class)\n    public void testUnsupportedProperty() {\n        String config =\n                \"\"\"\n        minimesos {\n\n            unsupportedProperty = \"foo\"\n\n        }\n        \"\"\"\n\n        parser.parse(config)\n    }\n\n    @Test(expected = MissingPropertyException.class)\n    public void testUnsupportedBlock() {\n        String config =\n                \"\"\"\n        minimesos {\n\n            unsupportedBlock {\n\n            }\n\n        }\n        \"\"\"\n\n        parser.parse(config)\n    }\n\n    @Test\n    public void testLoadSingleAgent() {\n        String config = \"\"\"\n                minimesos {\n                    agent {\n                    }\n                }\n        \"\"\"\n\n        ClusterConfig dsl = parser.parse(config)\n        assertEquals(1, dsl.agents.size())\n\n        MesosAgentConfig agent = dsl.agents.get(0)\n        assertNotNull(agent)\n    }\n\n    @Test\n    public void testLoadTwoAgents() {\n        String config = \"\"\"\n                minimesos {\n                    agent {\n                    }\n                    agent {\n                    }\n                }\n        \"\"\"\n\n        ClusterConfig dsl = parser.parse(config)\n        assertEquals(2, dsl.agents.size())\n    }\n\n    @Test\n    public void testLoadAgentTwoAgents_loggingLevel() {\n        String config = \"\"\"\n                minimesos {\n\n                    loggingLevel = \"warning\"\n\n                    agent {\n                        loggingLevel = \"ERROR\"\n                    }\n\n                    agent {\n\n                    }\n                }\n        \"\"\"\n\n        ClusterConfig dsl = parser.parse(config)\n\n        MesosAgentConfig agent1 = dsl.agents.get(0)\n        assertEquals(\"ERROR\", agent1.getLoggingLevel())\n\n        MesosAgentConfig agent2 = dsl.agents.get(1)\n        assertEquals(MesosContainerConfig.MESOS_LOGGING_LEVEL_INHERIT, agent2.getLoggingLevel())\n    }\n\n    @Test\n    public void testLoadMaster() {\n        String config = \"\"\"\n                minimesos {\n                    master {\n                        imageName  = \"another/master\"\n                    }\n                }\n        \"\"\"\n\n        ClusterConfig dsl = parser.parse(config)\n        assertNotNull(dsl.master)\n        assertEquals(\"another/master\", dsl.master.imageName)\n    }\n\n    @Test(expected = RuntimeException.class)\n    public void testMesosVersion_nonExistentVersion() {\n        String config = \"\"\"\n                minimesos {\n\n                    mesosVersion = \"1.0.0-does-not-exist\"\n\n                    master {\n\n                    }\n                }\n        \"\"\"\n\n        parser.parse(config)\n    }\n\n    @Test\n    public void testMesosVersion_inheritTag() {\n        String config = \"\"\"\n                minimesos {\n\n                    mesosVersion = \"0.26\"\n\n                    master {\n\n                    }\n                }\n        \"\"\"\n\n        ClusterConfig dsl = parser.parse(config)\n        assertNotNull(dsl.master)\n        assertEquals(\"containersol/mesos-master\", dsl.master.imageName)\n        assertEquals(\"0.26-0.1.0\", dsl.master.imageTag)\n    }\n\n    @Test\n    public void testMesosVersion_overrideTag() {\n        String config = \"\"\"\n                minimesos {\n\n                    mesosVersion = \"0.26\"\n\n                    master {\n                        imageTag = \"0.27\"\n                    }\n\n                    agent {\n                        imageTag = \"0.28\"\n                    }\n                }\n        \"\"\"\n\n        ClusterConfig dsl = parser.parse(config)\n        assertNotNull(dsl.master)\n        assertEquals(\"containersol/mesos-master\", dsl.master.imageName)\n        assertEquals(\"0.27\", dsl.master.imageTag)\n\n        assertNotNull(dsl.agents.get(0))\n        assertEquals(\"containersol/mesos-agent\", dsl.agents.get(0).imageName)\n        assertEquals(\"0.28\", dsl.agents.get(0).imageTag)\n    }\n\n    @Test(expected = Exception.class)\n    public void testFailureToLoadTwoMaster() {\n        String config = \"\"\"\n                minimesos {\n                    master {\n                        imageName  = \"another/master\"\n                    }\n                    master {\n                    }\n                }\n        \"\"\"\n\n        parser.parse(config)\n    }\n\n    @Test\n    public void testZookeeper() {\n        String config = \"\"\"\n                minimesos {\n                    zookeeper {\n\n                    }\n                }\n        \"\"\"\n\n        ClusterConfig dsl = parser.parse(config)\n        assertNotNull(dsl.zookeeper)\n        assertEquals(\"jplock/zookeeper\", dsl.zookeeper.imageName)\n        assertEquals(\"3.4.6\", dsl.zookeeper.imageTag)\n    }\n\n    @Test\n    public void testZookeeper_properties() {\n        String config = \"\"\"\n                minimesos {\n                    zookeeper {\n                      imageName = \"containersol/zookeeper\"\n                      imageTag  = \"3.4.5\"\n                    }\n                }\n        \"\"\"\n\n        ClusterConfig dsl = parser.parse(config)\n        assertNotNull(dsl.zookeeper)\n        assertEquals(\"containersol/zookeeper\", dsl.zookeeper.imageName)\n        assertEquals(\"3.4.5\", dsl.zookeeper.imageTag)\n    }\n\n    @Test\n    public void testMarathon() {\n        String config = \"\"\"\n                minimesos {\n                    marathon {\n\n                    }\n                }\n        \"\"\"\n\n        ClusterConfig dsl = parser.parse(config)\n        assertNotNull(dsl.marathon)\n        assertEquals(\"mesosphere/marathon\", dsl.marathon.imageName)\n        assertEquals(\"v1.3.5\", dsl.marathon.imageTag)\n    }\n\n    @Test\n    public void testMarathon_properties() {\n        String config = \"\"\"\n                minimesos {\n                    marathon {\n                      imageName = \"containersol/marathon\"\n                      imageTag  = \"v0.14.0\"\n                    }\n                }\n        \"\"\"\n\n        ClusterConfig dsl = parser.parse(config)\n        assertNotNull(dsl.marathon)\n        assertEquals(\"containersol/marathon\", dsl.marathon.imageName)\n        assertEquals(\"v0.14.0\", dsl.marathon.imageTag)\n    }\n\n    @Test(expected = MinimesosException.class)\n    public void testMarathonApp_noUrlOrPath() {\n        String config = \"\"\"\n                minimesos {\n                    marathon {\n                        app {\n\n                        }\n                    }\n                }\n        \"\"\"\n\n        parser.parse(config)\n    }\n\n    @Test\n    public void testMarathonApp_path() {\n        String config = \"\"\"\n                minimesos {\n                    marathon {\n                        app {\n                            marathonJson = \"src/test/resources/app.json\"\n                        }\n                    }\n                }\n        \"\"\"\n\n        ClusterConfig dsl = parser.parse(config)\n        assertNotNull(dsl.marathon)\n        assertEquals(\"src/test/resources/app.json\", dsl.marathon.apps[0].marathonJson)\n    }\n\n    @Test\n    public void testMarathonApp_url() {\n        String config = \"\"\"\n                minimesos {\n                    marathon {\n                        app {\n                            marathonJson = \"https://www.github.com/organization/repo/app.json\"\n                        }\n                    }\n                }\n        \"\"\"\n\n        ClusterConfig dsl = parser.parse(config)\n        assertNotNull(dsl.marathon)\n        assertEquals(\"https://www.github.com/organization/repo/app.json\", dsl.marathon.apps[0].marathonJson)\n    }\n\n    @Test\n    public void testLoadSingleAgentResourcesNumbers() {\n        String config = \"\"\"\n                minimesos {\n                    agent {\n                        resources {\n                            cpu {\n                                role  = \"logstash\"\n                                value = 1\n                            }\n                            cpu {\n                                role  = \"*\"\n                                value = 4\n                            }\n                            ports {\n                                role  = \"logstash\"\n                                value = \"[514-514]\"\n                            }\n                        }\n                    }\n                }\n        \"\"\"\n\n        ClusterConfig dsl = parser.parse(config)\n        assertEquals(1, dsl.agents.size())\n\n        MesosAgentConfig agent = dsl.agents.get(0)\n\n        assertEquals(4, agent.resources.cpus[\"*\"].value, 0.0001)\n        assertEquals(1, agent.resources.cpus[\"logstash\"].value, 0.0001)\n\n        assertNotNull(agent.resources.mems[\"*\"])\n        assertNull(agent.resources.mems[\"logstash\"])\n\n        assertNotNull(agent.resources.ports[\"*\"])\n        assertEquals(\"[514-514]\", agent.resources.ports[\"logstash\"].value)\n    }\n\n    @Test\n    /**\n     * Explicit test for surrounding numbers with \"\"\n     */\n    public void testLoadSingleAgentResourcesStrings() {\n        String config = \"\"\"\n                minimesos {\n                    agent {\n                        resources {\n                            cpu {\n                                role  = \"logstash\"\n                                value = \"1\"\n                            }\n                            cpu {\n                                role  = \"*\"\n                                value = \"4\"\n                            }\n                            ports {\n                                role  = \"logstash\"\n                                value = \"[514-514]\"\n                            }\n                        }\n                    }\n                }\n        \"\"\"\n\n        ClusterConfig dsl = parser.parse(config)\n        assertEquals(1, dsl.agents.size())\n\n        MesosAgentConfig agent = dsl.agents.get(0)\n\n        assertEquals(4, agent.resources.cpus[\"*\"].value, 0.0001)\n        assertEquals(1, agent.resources.cpus[\"logstash\"].value, 0.0001)\n\n        assertNotNull(agent.resources.mems[\"*\"])\n        assertNull(agent.resources.mems[\"logstash\"])\n\n        assertNotNull(agent.resources.ports[\"*\"])\n        assertEquals(\"[514-514]\", agent.resources.ports[\"logstash\"].value)\n    }\n\n}\n"
  },
  {
    "path": "minimesos/src/test/groovy/com/containersol/minimesos/config/ConfigWriterTest.groovy",
    "content": "package com.containersol.minimesos.config\n\nimport org.junit.Before\nimport org.junit.Test\n\nimport static org.junit.Assert.*\n\npublic class ConfigWriterTest {\n\n    private ConfigParser parser\n\n    @Before\n    public void before() {\n        parser = new ConfigParser()\n    }\n\n    @Test\n    public void testWritingDefaultConfiguration() {\n        ClusterConfig config = new ClusterConfig()\n        String strConfig = parser.toString(config)\n        ClusterConfig read = parser.parse(strConfig)\n        compareClusters(config, read)\n    }\n\n    @Test\n    public void testWritingFilledConfiguration() {\n\n        ClusterConfig config = new ClusterConfig()\n\n        config.master = new MesosMasterConfig()\n        config.zookeeper = new ZooKeeperConfig()\n        config.marathon = new MarathonConfig()\n        config.agents.add(new MesosAgentConfig())\n        config.consul = new ConsulConfig()\n        config.registrator = new RegistratorConfig()\n        config.mesosdns = new MesosDNSConfig()\n\n        AppConfig appConfig = new AppConfig()\n        appConfig.setMarathonJson(\"http://www.google.com\")\n        config.marathon.apps.add(appConfig)\n\n        AppConfig fileAppConfig = new AppConfig()\n        fileAppConfig.setMarathonJson(\"/temp/fileA\")\n        config.marathon.apps.add(fileAppConfig)\n\n        String strConfig = parser.toString(config)\n        ClusterConfig read = parser.parse(strConfig)\n\n        compareClusters(config, read)\n        assertNotNull(\"Marathon container must be set\", read.marathon)\n        assertEquals(config.marathon.apps.size(), read.marathon.apps.size())\n        assertEquals(\"http://www.google.com\", read.marathon.apps[0].marathonJson)\n        assertEquals(\"/temp/fileA\", read.marathon.apps[1].marathonJson)\n\n    }\n\n    static private void compareClusters(ClusterConfig first, ClusterConfig second) {\n\n        assertEquals(first.timeout, second.timeout)\n        assertEquals(first.clusterName, second.clusterName)\n        assertEquals(first.mapPortsToHost, second.mapPortsToHost)\n        assertEquals(first.loggingLevel, second.loggingLevel)\n\n        compareContainers(first.marathon, second.marathon)\n\n        compareContainers(first.zookeeper, second.zookeeper)\n        compareContainers(first.consul, second.consul)\n        compareContainers(first.registrator, second.registrator)\n        compareContainers(first.mesosdns, second.mesosdns)\n\n        compareMesosContainers(first.master, second.master)\n\n        assertEquals(first.agents.size(), second.agents.size())\n        if (first.agents.size() > 0) {\n            compareMesosContainers(first.agents[0], second.agents[0])\n        }\n\n    }\n\n    static void compareContainers(ContainerConfig first, ContainerConfig second) {\n        if (first == null) {\n            if (second != null) {\n                fail(\"Expected null, but found \" + second)\n            }\n        } else {\n            if (second == null) {\n                fail(\"Expected \" + first + \", but null was found\")\n            } else {\n                assertEquals(first.imageName, second.imageName)\n                assertEquals(first.imageTag, second.imageTag)\n            }\n        }\n    }\n\n    static void compareMesosContainers(MesosContainerConfig first, MesosContainerConfig second) {\n        compareContainers(first, second)\n        if (first != null) {\n            assertEquals(first.loggingLevel, second.loggingLevel)\n        }\n    }\n\n}\n"
  },
  {
    "path": "minimesos/src/test/groovy/com/containersol/minimesos/config/ResourceDefScalarTest.groovy",
    "content": "package com.containersol.minimesos.config\n\nimport org.junit.Test\n\nimport static org.junit.Assert.assertEquals;\n\npublic class ResourceDefScalarTest {\n\n    @Test\n    public void testDotParsing() {\n\n        ResourceDefScalar resource = new ResourceDefScalar()\n        resource.setValue(\"1.2\");\n\n        assertEquals(1.2, resource.getValue(), 0.0001)\n\n    }\n\n    @Test(expected = NumberFormatException.class)\n    public void testCommaParsing() {\n\n        ResourceDefScalar resource = new ResourceDefScalar()\n        resource.setValue(\"1,2\");\n\n    }\n\n}"
  },
  {
    "path": "minimesos/src/test/java/com/containersol/minimesos/ClusterBuilderTest.java",
    "content": "package com.containersol.minimesos;\n\nimport com.containersol.minimesos.cluster.MesosCluster;\nimport com.containersol.minimesos.config.ClusterConfig;\nimport com.containersol.minimesos.config.ConfigParser;\nimport com.containersol.minimesos.mesos.MesosAgentContainer;\nimport com.containersol.minimesos.mesos.MesosClusterContainersFactory;\nimport com.containersol.minimesos.util.CollectionsUtils;\nimport org.junit.Test;\n\nimport java.util.List;\n\nimport static org.junit.Assert.assertEquals;\n\npublic class ClusterBuilderTest {\n\n    @Test\n    public void testInheritedImageTag() {\n        String config = \"minimesos { \\n\" +\n                \"mesosVersion = \\\"0.26\\\" \\n\" +\n                \"agent { imageTag = \\\"0.27.0-0.1.0\\\"} \\n\" +\n                \"agent {} \\n\" +\n                \"}\";\n\n        ConfigParser parser = new ConfigParser();\n        ClusterConfig dsl = parser.parse(config);\n\n        MesosCluster cluster = new MesosClusterContainersFactory().createMesosCluster(dsl);\n\n        List<MesosAgentContainer> agents = CollectionsUtils.typedList(cluster.getAgents(), MesosAgentContainer.class);\n        assertEquals(2, agents.size());\n\n        assertEquals(\"0.27.0-\" + ClusterConfig.DEFAULT_MINIMESOS_DOCKER_VERSION, agents.get(0).getImageTag());\n        assertEquals(\"0.26-\" + ClusterConfig.DEFAULT_MINIMESOS_DOCKER_VERSION, agents.get(1).getImageTag());\n    }\n\n    @Test\n    public void testDefaultInAgentLoggingLevel() {\n        String config = \"minimesos { \\n\" +\n                \"loggingLevel = \\\"warning\\\" \\n\" +\n                \"agent { loggingLevel = \\\"ERROR\\\" } \\n\" +\n                \"agent { loggingLevel = \\\"INFO\\\" } \\n\" +\n                \"}\";\n\n        ConfigParser parser = new ConfigParser();\n        ClusterConfig dsl = parser.parse(config);\n\n        MesosCluster cluster = new MesosClusterContainersFactory().createMesosCluster(dsl);\n\n        List<MesosAgentContainer> agents = CollectionsUtils.typedList(cluster.getAgents(), MesosAgentContainer.class);\n        assertEquals(2, agents.size());\n\n        assertEquals(\"ERROR\", agents.get(0).getLoggingLevel());\n        assertEquals(\"INFO\", agents.get(1).getLoggingLevel());\n    }\n\n    @Test\n    public void testInheritedLoggingLevel() {\n        String config = \"minimesos { \\n\" +\n                \"loggingLevel = \\\"warning\\\" \\n\" +\n                \"agent { loggingLevel = \\\"ERROR\\\"} \\n\" +\n                \"agent {} \\n\" +\n                \"}\";\n\n        ConfigParser parser = new ConfigParser();\n        ClusterConfig dsl = parser.parse(config);\n\n        MesosCluster cluster = new MesosClusterContainersFactory().createMesosCluster(dsl);\n\n        List<MesosAgentContainer> agents = CollectionsUtils.typedList(cluster.getAgents(), MesosAgentContainer.class);\n        assertEquals(2, agents.size());\n\n        assertEquals(\"ERROR\", agents.get(0).getLoggingLevel());\n        assertEquals(\"WARNING\", agents.get(1).getLoggingLevel());\n    }\n\n}\n"
  },
  {
    "path": "minimesos/src/test/java/com/containersol/minimesos/ParseStateJSONTest.java",
    "content": "package com.containersol.minimesos;\n\nimport com.containersol.minimesos.state.Framework;\nimport com.containersol.minimesos.state.State;\nimport com.fasterxml.jackson.core.JsonParseException;\nimport com.fasterxml.jackson.databind.JsonMappingException;\nimport org.junit.Test;\n\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertNotNull;\n\npublic class ParseStateJSONTest {\n\n    public final String EXAMPLE_STATE_JSON = \"{\" +\n            \"\\\"activated_slaves\\\": 3,\" +\n            \"\\\"build_date\\\": \\\"2015-05-05 06:15:50\\\",\" +\n            \"\\\"build_time\\\": 1430806550,\" +\n            \"\\\"build_user\\\": \\\"root\\\",\" +\n            \"\\\"completed_frameworks\\\": [\" +\n            \"\" +\n            \"],\" +\n            \"\\\"deactivated_slaves\\\": 0,\" +\n            \"\\\"elected_time\\\": 1441628978.271,\" +\n            \"\\\"failed_tasks\\\": 0,\" +\n            \"\\\"finished_tasks\\\": 0,\" +\n            \"\\\"flags\\\": {\" +\n            \"\\\"allocation_interval\\\": \\\"1secs\\\",\" +\n            \"\\\"authenticate\\\": \\\"false\\\",\" +\n            \"\\\"authenticate_slaves\\\": \\\"false\\\",\" +\n            \"\\\"authenticators\\\": \\\"crammd5\\\",\" +\n            \"\\\"framework_sorter\\\": \\\"drf\\\",\" +\n            \"\\\"help\\\": \\\"false\\\",\" +\n            \"\\\"initialize_driver_logging\\\": \\\"true\\\",\" +\n            \"\\\"ip\\\": \\\"0.0.0.0\\\",\" +\n            \"\\\"log_auto_initialize\\\": \\\"true\\\",\" +\n            \"\\\"log_dir\\\": \\\"\\\\/var\\\\/log\\\",\" +\n            \"\\\"logbufsecs\\\": \\\"0\\\",\" +\n            \"\\\"logging_level\\\": \\\"INFO\\\",\" +\n            \"\\\"port\\\": \\\"5050\\\",\" +\n            \"\\\"quiet\\\": \\\"false\\\",\" +\n            \"\\\"quorum\\\": \\\"1\\\",\" +\n            \"\\\"recovery_slave_removal_limit\\\": \\\"100%\\\",\" +\n            \"\\\"registry\\\": \\\"replicated_log\\\",\" +\n            \"\\\"registry_fetch_timeout\\\": \\\"1mins\\\",\" +\n            \"\\\"registry_store_timeout\\\": \\\"5secs\\\",\" +\n            \"\\\"registry_strict\\\": \\\"false\\\",\" +\n            \"\\\"root_submissions\\\": \\\"true\\\",\" +\n            \"\\\"slave_reregister_timeout\\\": \\\"10mins\\\",\" +\n            \"\\\"user_sorter\\\": \\\"drf\\\",\" +\n            \"\\\"version\\\": \\\"false\\\",\" +\n            \"\\\"webui_dir\\\": \\\"\\\\/usr\\\\/share\\\\/mesos\\\\/webui\\\",\" +\n            \"\\\"work_dir\\\": \\\"\\\\/var\\\\/lib\\\\/mesos\\\",\" +\n            \"\\\"zk\\\": \\\"zk:\\\\/\\\\/localhost:2181\\\\/mesos\\\",\" +\n            \"\\\"zk_session_timeout\\\": \\\"10secs\\\"\" +\n            \"},\" +\n            \"\\\"frameworks\\\": [\" +\n            \"{\" +\n            \"\\\"active\\\": true,\" +\n            \"\\\"checkpoint\\\": true,\" +\n            \"\\\"completed_tasks\\\": [\" +\n            \"\" +\n            \"],\" +\n            \"\\\"failover_timeout\\\": 2592000,\" +\n            \"\\\"hostname\\\": \\\"0f43d2f7606a\\\",\" +\n            \"\\\"id\\\": \\\"20150907-122934-3858764204-5050-23-0000\\\",\" +\n            \"\\\"name\\\": \\\"elasticsearch\\\",\" +\n            \"\\\"offered_resources\\\": {\" +\n            \"\\\"cpus\\\": 0,\" +\n            \"\\\"disk\\\": 0,\" +\n            \"\\\"mem\\\": 0\" +\n            \"},\" +\n            \"\\\"offers\\\": [\" +\n            \"\" +\n            \"],\" +\n            \"\\\"registered_time\\\": 1441629007.9145,\" +\n            \"\\\"resources\\\": {\" +\n            \"\\\"cpus\\\": 3,\" +\n            \"\\\"disk\\\": 3072,\" +\n            \"\\\"mem\\\": 768,\" +\n            \"\\\"ports\\\": \\\"[9200-9202, 9300-9302]\\\"\" +\n            \"},\" +\n            \"\\\"role\\\": \\\"*\\\",\" +\n            \"\\\"tasks\\\": [\" +\n            \"{\" +\n            \"\\\"discovery\\\": {\" +\n            \"\\\"ports\\\": {\" +\n            \"\\\"ports\\\": [\" +\n            \"{\" +\n            \"\\\"name\\\": \\\"CLIENT_PORT\\\",\" +\n            \"\\\"number\\\": 9200\" +\n            \"},\" +\n            \"{\" +\n            \"\\\"name\\\": \\\"TRANSPORT_PORT\\\",\" +\n            \"\\\"number\\\": 9300\" +\n            \"}\" +\n            \"]\" +\n            \"},\" +\n            \"\\\"visibility\\\": \\\"EXTERNAL\\\"\" +\n            \"},\" +\n            \"\\\"executor_id\\\": \\\"29deeca9-0f28-4df7-af1d-14ae790044f6\\\",\" +\n            \"\\\"framework_id\\\": \\\"20150907-122934-3858764204-5050-23-0000\\\",\" +\n            \"\\\"id\\\": \\\"elasticsearch_slave1_20150907T123008.379Z\\\",\" +\n            \"\\\"labels\\\": [\" +\n            \"\" +\n            \"],\" +\n            \"\\\"name\\\": \\\"esdemo\\\",\" +\n            \"\\\"resources\\\": {\" +\n            \"\\\"cpus\\\": 1,\" +\n            \"\\\"disk\\\": 1024,\" +\n            \"\\\"mem\\\": 256,\" +\n            \"\\\"ports\\\": \\\"[9200-9200, 9300-9300]\\\"\" +\n            \"},\" +\n            \"\\\"slave_id\\\": \\\"20150907-122934-3858764204-5050-23-S1\\\",\" +\n            \"\\\"state\\\": \\\"TASK_RUNNING\\\",\" +\n            \"\\\"statuses\\\": [\" +\n            \"{\" +\n            \"\\\"state\\\": \\\"TASK_STARTING\\\",\" +\n            \"\\\"timestamp\\\": 1441629015.6595\" +\n            \"},\" +\n            \"{\" +\n            \"\\\"state\\\": \\\"TASK_RUNNING\\\",\" +\n            \"\\\"timestamp\\\": 1441629278.4553\" +\n            \"}\" +\n            \"]\" +\n            \"},\" +\n            \"{\" +\n            \"\\\"discovery\\\": {\" +\n            \"\\\"ports\\\": {\" +\n            \"\\\"ports\\\": [\" +\n            \"{\" +\n            \"\\\"name\\\": \\\"CLIENT_PORT\\\",\" +\n            \"\\\"number\\\": 9202\" +\n            \"},\" +\n            \"{\" +\n            \"\\\"name\\\": \\\"TRANSPORT_PORT\\\",\" +\n            \"\\\"number\\\": 9302\" +\n            \"}\" +\n            \"]\" +\n            \"},\" +\n            \"\\\"visibility\\\": \\\"EXTERNAL\\\"\" +\n            \"},\" +\n            \"\\\"executor_id\\\": \\\"ec8dee06-4176-4d7e-b5e1-9e6f1f8b5fdf\\\",\" +\n            \"\\\"framework_id\\\": \\\"20150907-122934-3858764204-5050-23-0000\\\",\" +\n            \"\\\"id\\\": \\\"elasticsearch_slave3_20150907T123008.294Z\\\",\" +\n            \"\\\"labels\\\": [\" +\n            \"\" +\n            \"],\" +\n            \"\\\"name\\\": \\\"esdemo\\\",\" +\n            \"\\\"resources\\\": {\" +\n            \"\\\"cpus\\\": 1,\" +\n            \"\\\"disk\\\": 1024,\" +\n            \"\\\"mem\\\": 256,\" +\n            \"\\\"ports\\\": \\\"[9202-9202, 9302-9302]\\\"\" +\n            \"},\" +\n            \"\\\"slave_id\\\": \\\"20150907-122934-3858764204-5050-23-S0\\\",\" +\n            \"\\\"state\\\": \\\"TASK_RUNNING\\\",\" +\n            \"\\\"statuses\\\": [\" +\n            \"{\" +\n            \"\\\"state\\\": \\\"TASK_STARTING\\\",\" +\n            \"\\\"timestamp\\\": 1441629015.3181\" +\n            \"},\" +\n            \"{\" +\n            \"\\\"state\\\": \\\"TASK_RUNNING\\\",\" +\n            \"\\\"timestamp\\\": 1441629278.3756\" +\n            \"}\" +\n            \"]\" +\n            \"},\" +\n            \"{\" +\n            \"\\\"discovery\\\": {\" +\n            \"\\\"ports\\\": {\" +\n            \"\\\"ports\\\": [\" +\n            \"{\" +\n            \"\\\"name\\\": \\\"CLIENT_PORT\\\",\" +\n            \"\\\"number\\\": 9201\" +\n            \"},\" +\n            \"{\" +\n            \"\\\"name\\\": \\\"TRANSPORT_PORT\\\",\" +\n            \"\\\"number\\\": 9301\" +\n            \"}\" +\n            \"]\" +\n            \"},\" +\n            \"\\\"visibility\\\": \\\"EXTERNAL\\\"\" +\n            \"},\" +\n            \"\\\"executor_id\\\": \\\"5aa2fc1d-6ad5-4710-9f47-1f9ddcf01ecb\\\",\" +\n            \"\\\"framework_id\\\": \\\"20150907-122934-3858764204-5050-23-0000\\\",\" +\n            \"\\\"id\\\": \\\"elasticsearch_slave2_20150907T123008.041Z\\\",\" +\n            \"\\\"labels\\\": [\" +\n            \"\" +\n            \"],\" +\n            \"\\\"name\\\": \\\"esdemo\\\",\" +\n            \"\\\"resources\\\": {\" +\n            \"\\\"cpus\\\": 1,\" +\n            \"\\\"disk\\\": 1024,\" +\n            \"\\\"mem\\\": 256,\" +\n            \"\\\"ports\\\": \\\"[9201-9201, 9301-9301]\\\"\" +\n            \"},\" +\n            \"\\\"slave_id\\\": \\\"20150907-122934-3858764204-5050-23-S2\\\",\" +\n            \"\\\"state\\\": \\\"TASK_RUNNING\\\",\" +\n            \"\\\"statuses\\\": [\" +\n            \"{\" +\n            \"\\\"state\\\": \\\"TASK_STARTING\\\",\" +\n            \"\\\"timestamp\\\": 1441629015.8581\" +\n            \"},\" +\n            \"{\" +\n            \"\\\"state\\\": \\\"TASK_RUNNING\\\",\" +\n            \"\\\"timestamp\\\": 1441629278.2919\" +\n            \"}\" +\n            \"]\" +\n            \"}\" +\n            \"],\" +\n            \"\\\"unregistered_time\\\": 0,\" +\n            \"\\\"used_resources\\\": {\" +\n            \"\\\"cpus\\\": 3,\" +\n            \"\\\"disk\\\": 3072,\" +\n            \"\\\"mem\\\": 768,\" +\n            \"\\\"ports\\\": \\\"[9200-9202, 9300-9302]\\\"\" +\n            \"},\" +\n            \"\\\"user\\\": \\\"root\\\",\" +\n            \"\\\"webui_url\\\": \\\"http:\\\\/\\\\/0f43d2f7606a:31100\\\"\" +\n            \"}\" +\n            \"],\" +\n            \"\\\"git_sha\\\": \\\"d6309f92a7f9af3ab61a878403e3d9c284ea87e0\\\",\" +\n            \"\\\"git_tag\\\": \\\"0.22.1\\\",\" +\n            \"\\\"hostname\\\": \\\"d3666e54bf39\\\",\" +\n            \"\\\"id\\\": \\\"20150907-122934-3858764204-5050-23\\\",\" +\n            \"\\\"killed_tasks\\\": 0,\" +\n            \"\\\"leader\\\": \\\"master@172.17.0.230:5050\\\",\" +\n            \"\\\"log_dir\\\": \\\"\\\\/var\\\\/log\\\",\" +\n            \"\\\"lost_tasks\\\": 0,\" +\n            \"\\\"orphan_tasks\\\": [\" +\n            \"\" +\n            \"],\" +\n            \"\\\"pid\\\": \\\"master@172.17.0.230:5050\\\",\" +\n            \"\\\"slaves\\\": [\" +\n            \"{\" +\n            \"\\\"active\\\": true,\" +\n            \"\\\"attributes\\\": {\" +\n            \"\" +\n            \"},\" +\n            \"\\\"hostname\\\": \\\"slave2\\\",\" +\n            \"\\\"id\\\": \\\"20150907-122934-3858764204-5050-23-S2\\\",\" +\n            \"\\\"pid\\\": \\\"slave(1)@172.17.0.230:5052\\\",\" +\n            \"\\\"registered_time\\\": 1441628979.0617,\" +\n            \"\\\"resources\\\": {\" +\n            \"\\\"cpus\\\": 3,\" +\n            \"\\\"disk\\\": 13483,\" +\n            \"\\\"mem\\\": 4936,\" +\n            \"\\\"ports\\\": \\\"[9201-9201, 9301-9301]\\\"\" +\n            \"}\" +\n            \"},\" +\n            \"{\" +\n            \"\\\"active\\\": true,\" +\n            \"\\\"attributes\\\": {\" +\n            \"\" +\n            \"},\" +\n            \"\\\"hostname\\\": \\\"slave1\\\",\" +\n            \"\\\"id\\\": \\\"20150907-122934-3858764204-5050-23-S1\\\",\" +\n            \"\\\"pid\\\": \\\"slave(1)@172.17.0.230:5051\\\",\" +\n            \"\\\"registered_time\\\": 1441628978.5892,\" +\n            \"\\\"resources\\\": {\" +\n            \"\\\"cpus\\\": 3,\" +\n            \"\\\"disk\\\": 13483,\" +\n            \"\\\"mem\\\": 4936,\" +\n            \"\\\"ports\\\": \\\"[9200-9200, 9300-9300]\\\"\" +\n            \"}\" +\n            \"},\" +\n            \"{\" +\n            \"\\\"active\\\": true,\" +\n            \"\\\"attributes\\\": {\" +\n            \"\" +\n            \"},\" +\n            \"\\\"hostname\\\": \\\"slave3\\\",\" +\n            \"\\\"id\\\": \\\"20150907-122934-3858764204-5050-23-S0\\\",\" +\n            \"\\\"pid\\\": \\\"slave(1)@172.17.0.230:5053\\\",\" +\n            \"\\\"registered_time\\\": 1441628978.5096,\" +\n            \"\\\"resources\\\": {\" +\n            \"\\\"cpus\\\": 3,\" +\n            \"\\\"disk\\\": 13483,\" +\n            \"\\\"mem\\\": 4936,\" +\n            \"\\\"ports\\\": \\\"[9202-9202, 9302-9302]\\\"\" +\n            \"}\" +\n            \"}\" +\n            \"],\" +\n            \"\\\"staged_tasks\\\": 3,\" +\n            \"\\\"start_time\\\": 1441628974.9045,\" +\n            \"\\\"started_tasks\\\": 3,\" +\n            \"\\\"unregistered_frameworks\\\": [\" +\n            \"\" +\n            \"],\" +\n            \"\\\"version\\\": \\\"0.22.1\\\"\" +\n            \"}\";\n\n    @Test\n    public void exampleStateJSONIsParsedCorrectly() throws JsonParseException, JsonMappingException {\n        State parsedState = State.fromJSON(EXAMPLE_STATE_JSON);\n        assertEquals(\"20150907-122934-3858764204-5050-23\", parsedState.getId());\n        assertEquals(1, parsedState.getFrameworks().size());\n        Framework framework = parsedState.getFramework(\"elasticsearch\");\n        assertNotNull(framework);\n        assertEquals(\"elasticsearch\", framework.getName());\n        assertEquals(true, framework.isActive());\n        assertEquals(true, framework.isCheckpoint());\n        assertEquals(2592000, framework.getFailoverTimeout());\n        assertEquals(\"0f43d2f7606a\", framework.getHostname());\n        assertEquals(\"20150907-122934-3858764204-5050-23-0000\", framework.getId());\n        assertEquals(\"elasticsearch\", framework.getName());\n        assertEquals(\"*\", framework.getRole());\n        assertEquals(\"0.22.1\", parsedState.getVersion());\n        assertEquals(0, framework.getExecutors().size());\n        assertEquals(\"29deeca9-0f28-4df7-af1d-14ae790044f6\", framework.getTasks().get(0).getExecutorId());\n        assertEquals(\"20150907-122934-3858764204-5050-23-0000\", framework.getTasks().get(0).getFrameworkId());\n    }\n}\n"
  },
  {
    "path": "minimesos/src/test/java/com/containersol/minimesos/factory/MesosClusterContainersFactoryTest.java",
    "content": "package com.containersol.minimesos.factory;\n\nimport com.containersol.minimesos.cluster.MesosCluster;\nimport com.containersol.minimesos.mesos.MesosClusterContainersFactory;\nimport org.junit.Test;\n\nimport java.io.FileInputStream;\nimport java.io.FileNotFoundException;\n\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertNotNull;\n\npublic class MesosClusterContainersFactoryTest {\n\n    @Test\n    public void testCreateMesosCluster() throws FileNotFoundException {\n        MesosCluster mesosCluster = new MesosClusterContainersFactory().createMesosCluster(new FileInputStream(\"src/test/resources/configFiles/minimesosFile-mesosClusterTest\"));\n        assertEquals(3 , mesosCluster.getAgents().size());\n        assertNotNull(mesosCluster.getZooKeeper());\n        assertNotNull(mesosCluster.getMaster());\n    }\n\n}\n"
  },
  {
    "path": "minimesos/src/test/java/com/containersol/minimesos/integrationtest/container/ContainerNameTest.java",
    "content": "package com.containersol.minimesos.integrationtest.container;\n\nimport com.containersol.minimesos.cluster.MesosCluster;\nimport com.containersol.minimesos.mesos.MesosAgentContainer;\nimport com.containersol.minimesos.mesos.MesosClusterContainersFactory;\nimport org.junit.Before;\nimport org.junit.Test;\n\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertFalse;\nimport static org.junit.Assert.assertTrue;\n\npublic class ContainerNameTest {\n\n    private MesosCluster cluster;\n    private String clusterId;\n\n    @Before\n    public void before() {\n        cluster = new MesosClusterContainersFactory().createMesosCluster(\"src/test/resources/configFiles/minimesosFile-mesosClusterTest\");\n\n        clusterId = cluster.getClusterId();\n    }\n\n    @Test\n    public void testBelongsToCluster() throws Exception {\n        MesosAgentContainer agent = new MesosAgentContainer(cluster, \"UUID\", \"CONTAINERID\");\n        String containerName = ContainerName.get(agent);\n\n        assertTrue(ContainerName.hasRoleInCluster(containerName, clusterId, agent.getRole()));\n        assertTrue(ContainerName.belongsToCluster(containerName, clusterId));\n    }\n\n    @Test\n    public void testWrongCluster() throws Exception {\n        MesosAgentContainer agent = new MesosAgentContainer(cluster, \"UUID\", \"CONTAINERID\");\n        String containerName = ContainerName.get(agent);\n\n        assertFalse(ContainerName.hasRoleInCluster(containerName, \"XXXXXX\", agent.getRole()));\n        assertFalse(ContainerName.belongsToCluster(containerName, \"XXXXXX\"));\n    }\n\n    @Test\n    public void testWrongRole() throws Exception {\n        MesosAgentContainer agent = new MesosAgentContainer(cluster, \"UUID\", \"CONTAINERID\");\n        String containerName = ContainerName.get(agent);\n\n        assertFalse(ContainerName.hasRoleInCluster(containerName, clusterId, \"XXXXXX\"));\n        assertTrue(ContainerName.belongsToCluster(containerName, clusterId));\n    }\n\n    @Test\n    public void testSimpleContainerName() {\n        String[] names = new String[1];\n        names[0] = \"/minimesos-agent\";\n\n        assertEquals(\"minimesos-agent\", ContainerName.getFromDockerNames(names));\n    }\n\n    @Test\n    public void testLinkedContainerNames() {\n        String[] names = new String[4];\n        names[0] = \"/minimesos-agent0/minimesos-zookeeper\";\n        names[1] = \"/minimesos-agent1/minimesos-zookeeper\";\n        names[2] = \"/minimesos-agent2/minimesos-zookeeper\";\n        names[3] = \"/minimesos-zookeeper\";\n\n        assertEquals(\"minimesos-zookeeper\", ContainerName.getFromDockerNames(names));\n    }\n\n}\n"
  },
  {
    "path": "minimesos/src/test/java/com/containersol/minimesos/integrationtest/container/MesosAgentTest.java",
    "content": "package com.containersol.minimesos.integrationtest.container;\n\nimport com.containersol.minimesos.MinimesosException;\nimport com.containersol.minimesos.config.ClusterConfig;\nimport com.containersol.minimesos.config.MesosAgentConfig;\nimport com.containersol.minimesos.mesos.MesosAgentContainer;\nimport org.junit.Assert;\nimport org.junit.Test;\n\npublic class MesosAgentTest {\n\n    /**\n     * It must be possible to detect wrong image within 30 seconds\n     */\n    @Test(expected = MinimesosException.class, timeout = 60 * 1000)\n    public void testPullingWrongContainer() {\n        MesosAgentConfig config = new MesosAgentConfig(ClusterConfig.DEFAULT_MESOS_VERSION);\n        config.setImageTag(\"non-existing-one\");\n\n        MesosAgentContainer agent = new MesosAgentContainer(config);\n        agent.pullImage();\n    }\n\n    /**\n     * Test error message\n     */\n    @Test\n    public void testPullingWrongContainerMessage() {\n\n        String imageTag = \"non-existing-one\";\n\n        MesosAgentConfig config = new MesosAgentConfig(ClusterConfig.DEFAULT_MESOS_VERSION);\n        config.setImageTag(imageTag);\n\n        MesosAgentContainer agent = new MesosAgentContainer(config);\n        try {\n            agent.pullImage();\n            Assert.fail(\"Pulling non-existing image should result in an exception\");\n        } catch (MinimesosException mme) {\n            Assert.assertTrue(\"Name of the image should be in the error message: \" + mme.getMessage(), mme.getMessage().contains(imageTag));\n        }\n    }\n\n}\n"
  },
  {
    "path": "minimesos/src/test/java/com/containersol/minimesos/jdepend/JDependCyclesTest.java",
    "content": "package com.containersol.minimesos.jdepend;\n\nimport jdepend.framework.JDepend;\nimport org.junit.Before;\nimport org.junit.Test;\n\nimport java.io.IOException;\n\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertNotNull;\nimport static org.junit.Assert.assertTrue;\n\n/**\n * Ensures absence of dependency cycles\n */\npublic class JDependCyclesTest {\n\n    private static final String EXPECTED_PACKAGE = \"com.containersol.minimesos\";\n\n    private JDepend jdepend;\n\n    @Before\n    public void before() throws IOException {\n        jdepend = new JDepend();\n        jdepend.addDirectory(\"build/classes/main\");\n    }\n\n    /**\n     * Tests that a package dependency cycle does not\n     * exist for any of the analyzed packages.\n     */\n    @Test\n    public void testAllPackages() {\n\n        jdepend.analyze();\n        assertTrue(\"Something is wrong with JDepend setup\", jdepend.getPackages().size() > 0);\n        assertNotNull(\"Package \" + EXPECTED_PACKAGE + \" is not found. Please, check\", jdepend.getPackage(EXPECTED_PACKAGE));\n        assertEquals(\"Dependency Cycles are introduced\", false, jdepend.containsCycles());\n\n    }\n\n}\n"
  },
  {
    "path": "minimesos/src/test/java/com/containersol/minimesos/mesos/ClusterContainersTest.java",
    "content": "package com.containersol.minimesos.mesos;\n\nimport com.containersol.minimesos.cluster.ClusterProcess;\nimport com.containersol.minimesos.cluster.Filter;\nimport com.containersol.minimesos.cluster.MesosAgent;\nimport com.containersol.minimesos.cluster.MesosMaster;\nimport com.containersol.minimesos.cluster.ZooKeeper;\nimport org.junit.Test;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertTrue;\nimport static org.mockito.Mockito.mock;\n\n/**\n * Tests\n */\npublic class ClusterContainersTest {\n    @Test\n    public void shouldEmptyStart() {\n        assertTrue(new ClusterContainers().getContainers().isEmpty());\n    }\n\n    @Test\n    public void shouldAllowInjection() {\n        List<ClusterProcess> dummyList = new ArrayList<>();\n        assertEquals(dummyList, new ClusterContainers(dummyList).getContainers());\n    }\n\n    @Test\n    public void shouldFilterZooKeeper() {\n        ZooKeeper mock = mock(ZooKeeper.class);\n        ClusterProcess clusterProcess = mock(ClusterProcess.class);\n        ClusterContainers clusterContainers = new ClusterContainers();\n        clusterContainers.add(mock).add(clusterProcess);\n\n        assertTrue(clusterContainers.isPresent(Filter.zooKeeper()));\n    }\n\n    @Test\n    public void shouldFilterMesosMaster() {\n        MesosMaster mock = mock(MesosMaster.class);\n        ClusterProcess clusterProcess = mock(ClusterProcess.class);\n        ClusterContainers clusterContainers = new ClusterContainers();\n        clusterContainers.add(mock).add(clusterProcess);\n\n        assertTrue(clusterContainers.isPresent(Filter.mesosMaster()));\n    }\n\n    @Test\n    public void shouldFilterMesosAgent() {\n        MesosAgent mock = mock(MesosAgent.class);\n        ClusterProcess clusterProcess = mock(ClusterProcess.class);\n        ClusterContainers clusterContainers = new ClusterContainers();\n        clusterContainers.add(mock).add(clusterProcess);\n\n        assertTrue(clusterContainers.isPresent(Filter.mesosAgent()));\n    }\n}\n"
  },
  {
    "path": "minimesos/src/test/java/com/containersol/minimesos/mesos/ClusterUtilTest.java",
    "content": "package com.containersol.minimesos.mesos;\n\nimport com.containersol.minimesos.cluster.ClusterProcess;\nimport com.containersol.minimesos.cluster.ClusterUtil;\nimport com.containersol.minimesos.config.ClusterConfig;\nimport com.containersol.minimesos.config.ConsulConfig;\nimport com.containersol.minimesos.config.MesosAgentConfig;\nimport com.containersol.minimesos.config.MesosMasterConfig;\nimport com.containersol.minimesos.integrationtest.container.AbstractContainer;\nimport com.github.dockerjava.api.command.CreateContainerCmd;\nimport org.junit.Test;\n\nimport java.util.Arrays;\nimport java.util.List;\n\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertTrue;\n\n/**\n * Tests helper methods\n */\npublic class ClusterUtilTest {\n\n    @Test\n    public void testGetDistinctRoleProcesses() throws Exception {\n\n        ClusterProcess master = new AbstractContainer(new MesosMasterConfig(ClusterConfig.DEFAULT_MESOS_VERSION)) {\n            @Override\n            protected CreateContainerCmd dockerCommand() {\n                return null;\n            }\n\n            @Override\n            public String getRole() {\n                return \"master\";\n            }\n        };\n        ClusterProcess consul = new AbstractContainer(new ConsulConfig()) {\n            @Override\n            protected CreateContainerCmd dockerCommand() {\n                return null;\n            }\n\n            @Override\n            public String getRole() {\n                return \"consul\";\n            }\n        };\n        ClusterProcess agent1 = new AbstractContainer(new MesosAgentConfig(ClusterConfig.DEFAULT_MESOS_VERSION)) {\n            @Override\n            protected CreateContainerCmd dockerCommand() {\n                return null;\n            }\n\n            @Override\n            public String getRole() {\n                return \"agent\";\n            }\n        };\n        ClusterProcess agent2 = new AbstractContainer(new MesosAgentConfig(ClusterConfig.DEFAULT_MESOS_VERSION)) {\n            @Override\n            protected CreateContainerCmd dockerCommand() {\n                return null;\n            }\n\n            @Override\n            public String getRole() {\n                return \"agent\";\n            }\n        };\n\n        List<ClusterProcess> processes = Arrays.asList(master, consul, agent1, agent2);\n        List<ClusterProcess> distinct = ClusterUtil.getDistinctRoleProcesses(processes);\n\n        assertEquals(2, distinct.size());\n        assertTrue(\"master has a distinct role\", distinct.contains(master));\n        assertTrue(\"consul has a distinct role\", distinct.contains(consul));\n\n    }\n}\n"
  },
  {
    "path": "minimesos/src/test/java/com/containersol/minimesos/util/CollectionsUtilsTest.java",
    "content": "package com.containersol.minimesos.util;\n\nimport com.containersol.minimesos.MinimesosException;\n\nimport org.junit.Test;\n\nimport static org.junit.Assert.assertArrayEquals;\n\npublic class CollectionsUtilsTest {\n\n    @Test(expected = MinimesosException.class)\n    public void testSplitCmd_uncoherentCommandLine() {\n        CollectionsUtils.splitCmd(\"foo bar='test\");\n    }\n\n    @Test\n    public void testSplitCmd_cmdLineEmpty() {\n        assertArrayEquals(\n            CollectionsUtils.splitCmd(\"\"),\n            new String[]{}\n        );\n    }\n\n    @Test\n    public void testSplitCmd_cmdLineNoQuotes() {\n        assertArrayEquals(\n            CollectionsUtils.splitCmd(\"foo bar baaz qux\"),\n            new String[]{\"foo\", \"bar\", \"baaz\", \"qux\"}\n        );\n    }\n\n    @Test\n    public void testSplitCmd_cmdLineWithQuotes() {\n        assertArrayEquals(\n            CollectionsUtils.splitCmd(\"foo='bar baaz' qux\"),\n            new String[]{\"foo='bar baaz'\", \"qux\"}\n        );\n    }\n\n    @Test\n    public void testSplitCmd_cmdLineWithDoubleQuotes() {\n        assertArrayEquals(\n            CollectionsUtils.splitCmd(\"foo=\\\"bar baaz\\\" qux\"),\n            new String[]{\"foo=\\\"bar baaz\\\"\", \"qux\"}\n        );\n    }\n}\n"
  },
  {
    "path": "minimesos/src/test/java/com/containersol/minimesos/util/EnvironmentBuilderTest.java",
    "content": "package com.containersol.minimesos.util;\n\nimport org.junit.Assert;\nimport org.junit.Test;\n\nimport java.util.Map;\nimport java.util.TreeMap;\n\nimport static org.hamcrest.Matchers.array;\nimport static org.hamcrest.Matchers.is;\n\npublic class EnvironmentBuilderTest {\n\n    @Test\n    @SuppressWarnings(\"unchecked\")\n    public void mergingSeveralSourcesProducesCorrectMap() {\n        Map<String, String> source1 = new TreeMap<>();\n        source1.put(\"envVar1\", \"value1\");\n        source1.put(\"envVar2\", \"value2\");\n        source1.put(\"envVar3\", \"value3\");\n        source1.put(\"envVar4\", \"value4\");\n        Map<String, String> source2 = new TreeMap<>();\n        source2.put(\"envVar5\", \"value5\");\n        source2.put(\"envVar6\", \"value6\");\n\n        String[] result = EnvironmentBuilder.newEnvironment()\n                .withValues(source1)\n                .withValue(\"envVarX\", \"valueX\")\n                .withValues(source2)\n                .createEnvironment();\n\n        Assert.assertThat(result, array(is(\"envVar1=value1\"),\n                is(\"envVar2=value2\"), is(\"envVar3=value3\"), is(\"envVar4=value4\"),\n                is(\"envVar5=value5\"), is(\"envVar6=value6\"), is(\"envVarX=valueX\")));\n    }\n}\n"
  },
  {
    "path": "minimesos/src/test/java/com/containersol/minimesos/util/ResourceUtilTest.java",
    "content": "package com.containersol.minimesos.util;\n\nimport com.containersol.minimesos.MinimesosException;\nimport org.junit.Test;\n\nimport java.util.ArrayList;\n\nimport static org.junit.Assert.assertEquals;\n\npublic class ResourceUtilTest {\n\n    @Test(expected = MinimesosException.class)\n    public void testParsePorts_emptyResourceString() {\n        ResourceUtil.parsePorts(\"\");\n    }\n\n    @Test(expected = MinimesosException.class)\n    public void testParsePorts_nullResource() {\n        ResourceUtil.parsePorts(null);\n    }\n\n    @Test\n    public void testParsePorts_singlePortRange() {\n        ArrayList<Integer> ports = ResourceUtil.parsePorts(\"ports(*):[8080-8080]\");\n        assertEquals(1, ports.size());\n        assertEquals(8080, ports.get(0).intValue());\n    }\n\n    @Test\n    public void testParsePorts_portRange() {\n        ArrayList<Integer> ports = ResourceUtil.parsePorts(\"ports(*):[8080-8082]\");\n        assertEquals(3, ports.size());\n        assertEquals(8080, ports.get(0).intValue());\n        assertEquals(8081, ports.get(1).intValue());\n        assertEquals(8082, ports.get(2).intValue());\n    }\n\n    @Test(expected = MinimesosException.class)\n    /**\n     * Should be resolved by https://github.com/ContainerSolutions/minimesos/issues/237\n     */\n    public void testParsePorts_portRanges() {\n        ResourceUtil.parsePorts(\"ports(*):[8080-8082],[5000-5001]\");\n    }\n\n}\n"
  },
  {
    "path": "minimesos/src/test/resources/configFiles/minimesosFile-authenticationTest",
    "content": "minimesos {\n    clusterName = \"authentication-test\"\n    mapPortsToHost = false\n    loggingLevel = \"INFO\"\n    mapAgentSandboxVolume = false\n    mesosVersion = \"1.0.0\"\n    timeout = 60\n\n    master {\n        imageName       = \"containersol/mesos-master\"\n        imageTag        = \"1.0.0-0.1.0\"\n        authenticate    = true\n        aclJson         = \"\"\"\n                            {\n                                \"run_tasks\": [\n                                    { \"principals\": { \"values\": [\"foo\", \"bar\"] },\n                                      \"users\": { \"values\": [\"alice\"] }\n                                    }\n                               ]\n                            }\n                          \"\"\"\n    }\n\n    zookeeper {\n        imageName = \"jplock/zookeeper\"\n        imageTag = \"3.4.6\"\n    }\n\n    agent {\n        imageName = \"containersol/mesos-agent\"\n        imageTag = \"1.0.0-0.1.0\"\n        loggingLevel = \"# INHERIT FROM CLUSTER\"\n        portNumber = 5051\n\n        resources {\n\n            cpu {\n                role = \"*\"\n                value = 4\n            }\n\n            disk {\n                role = \"*\"\n                value = 2000\n            }\n\n            mem {\n                role = \"*\"\n                value = 512\n            }\n\n            ports {\n                role = \"*\"\n                value = \"[31000-32000]\"\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "minimesos/src/test/resources/configFiles/minimesosFile-mesosClusterTest",
    "content": "minimesos {\n    clusterName = \"mesos-cluster-test\"\n    mapPortsToHost = true\n    loggingLevel = \"INFO\"\n    mapAgentSandboxVolume = false\n    mesosVersion = \"1.0.0\"\n    timeout = 60\n\n    agent {\n        imageName = \"containersol/mesos-agent\"\n        imageTag = \"1.0.0-0.1.0\"\n        loggingLevel = \"# INHERIT FROM CLUSTER\"\n        portNumber = 5051\n        attributes = \"az:0a\"\n\n        resources {\n\n            cpu {\n                role = \"*\"\n                value = 4\n            }\n\n            disk {\n                role = \"*\"\n                value = 2000\n            }\n\n            mem {\n                role = \"*\"\n                value = 512\n            }\n\n            ports {\n                role = \"*\"\n                value = \"[31000-32000]\"\n            }\n        }\n    }\n\n    agent {\n        imageName = \"containersol/mesos-agent\"\n        imageTag = \"1.0.0-0.1.0\"\n        loggingLevel = \"# INHERIT FROM CLUSTER\"\n        portNumber = 5051\n\n        resources {\n\n            cpu {\n                role = \"*\"\n                value = 4\n            }\n\n            disk {\n                role = \"*\"\n                value = 2000\n            }\n\n            mem {\n                role = \"*\"\n                value = 512\n            }\n\n            ports {\n                role = \"*\"\n                value = \"[31000-32000]\"\n            }\n        }\n    }\n\n    agent {\n        imageName = \"containersol/mesos-agent\"\n        imageTag = \"1.0.0-0.1.0\"\n        loggingLevel = \"# INHERIT FROM CLUSTER\"\n        portNumber = 5051\n\n        resources {\n\n            cpu {\n                role = \"*\"\n                value = 4\n            }\n\n            disk {\n                role = \"*\"\n                value = 2000\n            }\n\n            mem {\n                role = \"*\"\n                value = 512\n            }\n\n            ports {\n                role = \"*\"\n                value = \"[31000-32000]\"\n            }\n        }\n    }\n\n    consul {\n        imageName = \"consul\"\n        imageTag = \"0.7.1\"\n    }\n\n    master {\n        imageName = \"containersol/mesos-master\"\n        imageTag = \"1.0.0-0.1.0\"\n        loggingLevel = \"# INHERIT FROM CLUSTER\"\n    }\n\n    marathon {\n        imageName = \"mesosphere/marathon\"\n        imageTag = \"v1.3.5\"\n    }\n\n    zookeeper {\n        imageName = \"jplock/zookeeper\"\n        imageTag = \"3.4.6\"\n    }\n}\n"
  },
  {
    "path": "minimesos/src/test/resources/logback-test.xml",
    "content": "<configuration debug=\"false\">\n\n\t<appender name=\"console\" class=\"ch.qos.logback.core.ConsoleAppender\">\n\t\t<target>System.out</target>\n\t\t<encoder>\n\t\t\t<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36}: %msg%n</pattern>\n\t\t</encoder>\n\t</appender>\n\n\t<logger name=\"com.containersol.minimesos\" level=\"debug\"/>\n\n\t<root level=\"warn\">\n\t\t<appender-ref ref=\"console\" />\n\t</root>\n\n</configuration>\n"
  },
  {
    "path": "opt/apps/weave-scope.json",
    "content": "{\n  \"id\": \"weave-scope\",\n  \"cpus\": 1,\n  \"mem\": 128,\n  \"instances\": 1,\n  \"constraints\": [\n    [\n      \"hostname\",\n      \"UNIQUE\"\n    ]\n  ],\n  \"container\": {\n    \"type\": \"DOCKER\",\n    \"docker\": {\n      \"image\": \"weaveworks/scope:0.13.1\",\n      \"network\": \"HOST\",\n      \"privileged\": true,\n      \"parameters\": [\n        { \"key\": \"pid\", \"value\": \"host\" },\n        { \"key\": \"name\", \"value\": \"weavescope\" }\n      ]\n    },\n    \"volumes\": [\n      {\n        \"containerPath\": \"/var/run/docker.sock\",\n        \"hostPath\": \"/var/run/docker.sock\",\n        \"mode\": \"RW\"\n      }\n    ]\n  },\n  \"args\": [\"--probe.docker\", \"true\"],\n  \"env\": {\n    \"CHECKPOINT_DISABLE\": \"\"\n  },\n  \"portDefinitions\": [\n    { \"port\": 4040, \"protocol\": \"tcp\", \"name\": \"http\" }\n  ]\n}\n"
  },
  {
    "path": "opt/sonar/DockerFile",
    "content": "FROM sonarqube:5.3\nMAINTAINER Container Solutions BV <info@container-solutions.com>\n\nADD sonar-plugins/sonar-github-plugin-1.1.jar /opt/sonarqube/extensions/plugins\nADD sonar-plugins/sonar-java-plugin-3.7.1.jar /opt/sonarqube/extensions/plugins\nADD sonar-plugins/sonar-scm-git-plugin-1.0.jar /opt/sonarqube/extensions/plugins\nADD sonar-plugins/sonar-scm-svn-plugin-1.2.jar /opt/sonarqube/extensions/plugins\n"
  },
  {
    "path": "opt/sonar/certificate.yaml",
    "content": "apiVersion: extensions/v1beta1\nkind: ThirdPartyResource\ndescription: \"A specification of a Let's Encrypt Certificate to manage.\"\nmetadata:\n  name: \"certificate.stable.hightower.com\"\nversions:\n  - name: v1\n"
  },
  {
    "path": "opt/sonar/setup.md",
    "content": "### Create Persistent Disk\n`gcloud compute disks create --size 200GB minimesos-sonar-postgres-disk`\n\n### Attach created disk to linux instance for formatting and data transfer\n`gcloud compute instances attach-disk jenkins-ci-4 --disk minimesos-sonar-postgres-disk --device-name postgresdisk`\n\n### Mount and format disk\n`/usr/share/google/safe_format_and_mount  /dev/disk/by-id/google-postgresdisk /postgresdisk`\n\n### Detach Disk from linux instance\n`gcloud compute instances detach-disk jenkins-ci-4 --disk minimesos-sonar-postgres-disk`\n\n### Create cluster\n`gcloud container clusters create \"minimesos-sonar\" --zone \"europe-west1-d\" --machine-type \"n1-standard-2\" --num-nodes \"1\" --network \"ci-network\" --enable-cloud-logging`\n\nDownload cluster credentials into kubectl:\n`gcloud container clusters get-credentials minimesos-sonar`\n\n### Create database password secret\nThis password gets applied to the postgres database on first start, changin it later is not possible as it's persisted\nto the persistent disk\n\necho -n \"thepassword\" > password\n`kubectl create secret generic postgres-pwd --from-file=./password`\n\n### Create Certificate\n\nkubectl create -f certificate.yaml\n\n\n\n1. Create new domain name as old one can't be shared anymore between Jenkins and Sonar. sonar.minimesos.ci.container-solutions.com\n2. Make https work for sonar\n3. User management in sonar?\n"
  },
  {
    "path": "opt/sonar/sonar-deployment.yaml",
    "content": "apiVersion: extensions/v1beta1\nkind: Deployment\nmetadata:\n  name: sonar\nspec:\n  replicas: 1\n  template:\n    metadata:\n      name: sonar\n      labels:\n        name: sonar\n    spec:\n      containers:\n        - image: containersol/minimesos-sonar\n          args:\n            - -Dsonar.web.context=/sonar\n          name: sonar\n          env:\n            - name: SONARQUBE_JDBC_PASSWORD\n              valueFrom:\n                secretKeyRef:\n                  name: postgres-pwd\n                  key: password\n            - name: SONARQUBE_JDBC_URL\n              value: jdbc:postgresql://sonar-postgres:5432/sonar\n          ports:\n            - containerPort: 9000\n              name: sonar\n"
  },
  {
    "path": "opt/sonar/sonar-postgres-deployment.yaml",
    "content": "apiVersion: extensions/v1beta1\nkind: Deployment\nmetadata:\n  name: sonar-postgres\nspec:\n  replicas: 1\n  template:\n    metadata:\n      name: sonar-postgres\n      labels:\n        name: sonar-postgres\n    spec:\n      containers:\n        - image: postgres:9.5.3\n          name: sonar-postgres\n          env:\n            - name: POSTGRES_PASSWORD\n              valueFrom:\n                secretKeyRef:\n                  name: postgres-pwd\n                  key: password\n            - name: POSTGRES_USER\n              value: sonar\n          ports:\n            - containerPort: 5432\n              name: postgresport\n          volumeMounts:\n            # This name must match the volumes.name below.\n            - name: data-disk\n              mountPath: /var/lib/postgresql/data\n      volumes:\n        - name: data-disk\n          gcePersistentDisk:\n            # This disk must already exist.\n            pdName: minimesos-sonar-postgres-disk\n            fsType: ext4\n"
  },
  {
    "path": "opt/sonar/sonar-postgres-service.yaml",
    "content": "apiVersion: v1\nkind: Service\nmetadata:\n  labels:\n    name: sonar-postgres\n  name: sonar-postgres\nspec:\n  ports:\n    - port: 5432\n  selector:\n    name: sonar-postgres\n"
  },
  {
    "path": "opt/sonar/sonar-service.yaml",
    "content": "apiVersion: v1\nkind: Service\nmetadata:\n  labels:\n    name: sonar\n  name: sonar\nspec:\n  ports:\n    - port: 80\n      targetPort: 9000\n      name: sonarport\n  selector:\n    name: sonar\n  type: LoadBalancer\n"
  },
  {
    "path": "opt/vagrant/debian/jessie64/Vagrantfile",
    "content": "Vagrant.configure(2) do |config|\n\n  config.vm.box = \"debian/contrib-jessie64\"\n\n  config.vm.provider \"virtualbox\" do |vb|\n    vb.memory = \"4096\"\n  end\n\n  config.vm.network \"private_network\", ip: \"192.168.123.11\"\n\n  config.vm.provision :shell, :path => \"provision.sh\"\n\nend\n"
  },
  {
    "path": "opt/vagrant/debian/jessie64/provision.sh",
    "content": "#!/usr/bin/env bash\n\n###\n###  JDK and Gradle\n###\n\necho \"deb http://ftp.debian.org/debian jessie-backports main\" > /etc/apt/sources.list.d/openjdk.list && \\\napt-get update -qq && apt-get install -qqy \\\n    apt-transport-https \\\n    curl \\\n    unzip \\\n    openjdk-8-jdk\n\ncurl https://downloads.gradle.org/distributions/gradle-2.12-bin.zip --output /tmp/gradle-2.12-bin.zip --silent\nunzip -q /tmp/gradle-2.12-bin.zip -d /usr/share\n\necho \"export JAVA_HOME=/usr/lib/jvm/java-8-openjdk-amd64/jre\" >> /home/vagrant/.profile\necho \"export GRADLE_HOME=/usr/share/gradle-2.12\" >> /home/vagrant/.profile\necho \"export PATH=\\$JAVA_HOME/bin:\\$GRADLE_HOME/bin:\\$PATH\" >> /home/vagrant/.profile\n\nupdate-ca-certificates -f\n\n###\n###  Getting Docker installed\n###\necho \"\"\necho \"Getting Docker\"\n\necho \"deb https://apt.dockerproject.org/repo debian-jessie main\" > /etc/apt/sources.list.d/docker.list && \\\n\tapt-key adv --keyserver hkp://p80.pool.sks-keyservers.net:80 --recv-keys 58118E89F3A912897C070ADBF76221572C52609D && \\\n\tapt-get update -qq && \\\n\tapt-get -qqy install docker-engine=1.9.1-0~jessie\n\necho \"Enabling non-sudo access to docker\"\ngpasswd -a vagrant docker\n\nservice docker start\n\necho ''\necho 'Apply command below on the host machine'\necho 'sudo route delete 172.17.0.0/16; sudo route -n add 172.17.0.0/16 192.168.123.11'\n\n"
  },
  {
    "path": "settings.gradle",
    "content": "rootProject.name = 'minimesos-project'\ninclude \"minimesos\"\ninclude \"cli\"\n"
  },
  {
    "path": "travis.sh",
    "content": "#!/bin/bash\nset -ev\n\nGH_SONARQ_PARAMS=\"\"\n\n# When run on Travis CI, env var TRAVIS_PULL_REQUEST either contains PR number (for PR builds) or \"false\" (for push builds).\n# Locally this env var is not set. Test: if variable is not empty and is not equal \"false\"\nif [ ! -z \"$TRAVIS_PULL_REQUEST\" ] && [ \"${TRAVIS_PULL_REQUEST}\" != \"false\" ] && [ \"${TRAVIS_SECURE_ENV_VARS}\" == \"true\" ]; then\n    echo \"PR build. Will execute SonarQube preview scan\"\n    GH_SONARQ_PARAMS=\"jacocoTestReport sonarqube -Dsonar.analysis.mode=preview -Dsonar.host.url=$SQ_URL -Dsonar.github.oauth=$GH_TOKEN -Dsonar.github.repository=$TRAVIS_REPO_SLUG -Dsonar.github.pullRequest=$TRAVIS_PULL_REQUEST\"\nfi\n\n# Update SonarQube data on push builds on master branch\nif [ \"${TRAVIS_PULL_REQUEST}\" == \"false\" ] && [ \"${TRAVIS_BRANCH}\" == \"master\" ] && [ \"${TRAVIS_SECURE_ENV_VARS}\" == \"true\" ]; then\n    echo \"Building $TRAVIS_BRANCH branch. Will execute SonarQube scan\"\n    GH_SONARQ_PARAMS=\"jacocoTestReport sonarqube -Dsonar.host.url=$SQ_URL -Dsonar.jdbc.url=$SQ_JDBC_URL -Dsonar.jdbc.driverClassName=org.postgresql.Driver -Dsonar.jdbc.user=$SQ_JDBC_USER -Dsonar.jdbc.password=$SQ_JDBC_PASSWORD\"\nfi\n\n./gradlew --info --stacktrace clean build integrationTest $GH_SONARQ_PARAMS\n\n"
  }
]